{
  "name": "Track new LinkedIn jobs in Google Sheets (daily, deduplicated)",
  "nodes": [
    {
      "id": "d505be50-427e-5436-bfa2-fbe491c3883d",
      "name": "Setup and cost",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -820,
        -420
      ],
      "parameters": {
        "content": "## Track new LinkedIn jobs in Google Sheets\n\nRun a public LinkedIn jobs search each morning. Save job ID, title, company, location, posting date, application type, applicant count, salary and URL. The final Sheets node matches on job ID, so a repeated posting updates its row instead of adding another.\n\n**Setup**\n1. Add your Apify credential to both Apify nodes. The Actor is pinned to `alfalfa/linkedin-jobs-scraper`.\n2. Edit **Search settings**: keywords, location, hours and max jobs. Keep the window a little longer than the schedule interval if you want overlap protection.\n3. Create a Google Sheet with the header row in the README, paste its URL into **Upsert jobs by ID**, connect Google Sheets, and select the tab.\n4. Run once manually and inspect the rows. Only then activate the daily schedule. No email is sent.\n\n**Price**: $0.30 per 1,000 saved jobs on Apify Free, lower on paid tiers. No Actor start fee; Apify and residential proxy usage are included. Repeating a job in a later run is charged again even though the Sheet keeps one row.",
        "width": 950,
        "height": 370
      }
    },
    {
      "id": "0effc67b-6514-598e-a808-42ab88cb7e9e",
      "name": "Every morning",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -620,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "triggerAtHour": 7,
              "triggerAtMinute": 0
            }
          ]
        }
      }
    },
    {
      "id": "8dde5391-9520-5369-9c72-d80fe44a9394",
      "name": "Search settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -400,
        0
      ],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "keywords",
              "name": "keywords",
              "value": "data engineer",
              "type": "string"
            },
            {
              "id": "location",
              "name": "location",
              "value": "Berlin",
              "type": "string"
            },
            {
              "id": "hours",
              "name": "postedWithinHours",
              "value": 36,
              "type": "number"
            },
            {
              "id": "max",
              "name": "maxJobs",
              "value": 100,
              "type": "number"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "5aa5fa54-dbb7-54ff-afb7-0fe951b94ebd",
      "name": "Scrape public jobs",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 1,
      "position": [
        -180,
        0
      ],
      "parameters": {
        "actorId": {
          "__rl": true,
          "mode": "list",
          "value": "e98XAu5rphu6by5Kh",
          "cachedResultUrl": "https://console.apify.com/actors/e98XAu5rphu6by5Kh/input",
          "cachedResultName": "LinkedIn Jobs Scraper (alfalfa/linkedin-jobs-scraper)"
        },
        "operation": "Run actor",
        "timeout": {},
        "customBody": "={{ JSON.stringify({ keywords: $json.keywords, location: $json.location, postedWithinHours: $json.postedWithinHours, maxJobs: $json.maxJobs, scrapeDetails: true }) }}"
      }
    },
    {
      "id": "cf76cf5a-920f-5698-b1d7-30f5ba95d5da",
      "name": "Get job results",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 1,
      "position": [
        40,
        0
      ],
      "parameters": {
        "resource": "Datasets",
        "operation": "Get items",
        "datasetId": "={{ $json.defaultDatasetId }}",
        "limit": {}
      }
    },
    {
      "id": "d25acb5f-ee8f-5660-9c75-7fa03cc2c00c",
      "name": "Prepare sheet rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        260,
        0
      ],
      "parameters": {
        "jsCode": "// Keep IDs as text and write only fields a reader can use in a spreadsheet.\n// The Actor already removes duplicates within a run. Google Sheets matches ID across runs.\nreturn $input.all().filter(({ json: job }) => job.id && job.link).map(({ json: job }) => ({\n  json: {\n    id: String(job.id),\n    title: job.title || '',\n    company: job.companyName || '',\n    location: job.location || '',\n    postedAt: job.postedAt || '',\n    applyType: job.applyType || '',\n    applicants: job.applicantsText || job.applicantsCount || '',\n    salary: Array.isArray(job.salaryInfo) ? job.salaryInfo.join(' - ') : (job.salaryInfo || ''),\n    jobUrl: job.link,\n    companyUrl: job.companyLinkedinUrl || '',\n    lastSeenAt: new Date().toISOString()\n  }\n}));"
      }
    },
    {
      "id": "64716445-deee-5bd6-9cd8-5d7e42e227a4",
      "name": "Upsert jobs by ID",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        480,
        0
      ],
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID_HERE/edit"
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "Sheet1"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "id"
          ]
        },
        "options": {}
      }
    }
  ],
  "connections": {
    "Every morning": {
      "main": [
        [
          {
            "node": "Search settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search settings": {
      "main": [
        [
          {
            "node": "Scrape public jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape public jobs": {
      "main": [
        [
          {
            "node": "Get job results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get job results": {
      "main": [
        [
          {
            "node": "Prepare sheet rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare sheet rows": {
      "main": [
        [
          {
            "node": "Upsert jobs by ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  }
}
