{
  "name": "Add new job postings from company career pages to Google Sheets with Apify",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "daysInterval": 1,
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "b46ef8f3-2e50-584c-8cca-b4289070b472",
      "name": "Every day at 08:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "resource": "Actors",
        "operation": "Run actor and get dataset",
        "actorId": {
          "__rl": true,
          "value": "ooXv5feBjdefzRfdC",
          "mode": "id"
        },
        "customBody": "{\n  \"companies\": [\n    \"airbnb\",\n    \"figma\",\n    \"notion\",\n    \"https://jobs.lever.co/spotify\",\n    \"ashby:ramp\"\n  ],\n  \"titleKeywords\": [\n    \"engineer\",\n    \"data\",\n    \"product\"\n  ],\n  \"locations\": [],\n  \"remoteOnly\": false,\n  \"withSalaryOnly\": false,\n  \"postedWithinDays\": 0,\n  \"onlyNew\": true,\n  \"includeDescription\": false,\n  \"maxResults\": 500\n}",
        "maxTotalChargeUsd": 1
      },
      "id": "184c1a8e-fd50-5bcc-8a50-c36940cfd8af",
      "name": "Get new job postings",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 2,
      "position": [
        240,
        0
      ],
      "credentials": {
        "apifyApi": {
          "id": "REPLACE_WITH_YOUR_APIFY_CREDENTIAL",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// One sheet row per new job. Column names must match the header row of your sheet\n// (the Sheets node adds any missing columns on the first run).\nconst salary = (j) => {\n  if (j.salaryMin == null && j.salaryMax == null) return '';\n  const f = (n) => (n == null ? '?' : Math.round(n).toLocaleString('en-US'));\n  return `${j.salaryCurrency ?? ''} ${f(j.salaryMin)}-${f(j.salaryMax)} per ${j.salaryPeriod ?? 'year'}`.trim();\n};\nreturn $input.all()\n  .map((i) => i.json)\n  .filter((j) => j.title && j.url)\n  .map((j) => ({ json: {\n    company: j.company ?? '',\n    title: j.title,\n    department: j.department ?? '',\n    location: j.location ?? '',\n    country: j.country ?? '',\n    workplace: j.workplaceType ?? (j.remote ? 'remote' : ''),\n    employmentType: j.employmentType ?? '',\n    salary: salary(j),\n    postedAt: j.postedAt ?? '',\n    url: j.url,\n    jobId: `${j.source ?? ''}:${j.jobId ?? j.url}`,\n    foundAt: new Date().toISOString().slice(0, 10),\n  } }));"
      },
      "id": "0bba57ba-562b-51aa-a746-f101756f435c",
      "name": "Shape sheet rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        0
      ]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Jobs",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "jobId"
          ],
          "schema": []
        },
        "options": {}
      },
      "id": "6ce05c0e-602d-5077-9b48-5d49ded549c8",
      "name": "Add rows to the Jobs sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        720,
        0
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "REPLACE_WITH_YOUR_GOOGLE_SHEETS_CREDENTIAL",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "content": "## Get new postings\nYour company list and filters live in the Input JSON.",
        "height": 260,
        "width": 440,
        "color": 7
      },
      "id": "4a7cb0ac-8f96-550c-acab-6e5c153dca45",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -50,
        -130
      ]
    },
    {
      "parameters": {
        "content": "## Save to the sheet\nOne row per job, matched on `jobId`.",
        "height": 260,
        "width": 440,
        "color": 7
      },
      "id": "88e1ff44-2a75-558a-9917-eaa3dbeaeff7",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        430,
        -130
      ]
    },
    {
      "parameters": {
        "content": "## Add new job postings from company career pages to Google Sheets\n\nFor recruiters, job seekers, sales teams watching hiring signals, and analysts who track a list of companies.\n\n### How it works\n1. Every morning the schedule runs the **careers-jobs** Actor on Apify.\n2. It reads each company's public careers board (Greenhouse, Lever, Ashby, Personio, Teamtailor), applies your title, location and keyword filters, and with `onlyNew` returns only postings it has not returned before.\n3. A Code node flattens each job (salary range, location, workplace type, link) and the Sheets node adds or updates one row per job, keyed on `jobId`.\n\n### Setup\n1. Add your own Apify API token as an *Apify API* credential.\n2. Create a Google Sheet with a tab named **Jobs** and a header row containing at least `jobId`. Paste the sheet ID into the Sheets node and pick your Google credential.\n3. List your companies in the Input JSON: a board name (`figma`), a board URL, or `ashby:ramp`.\n\n### Requirements\nApify account (free plan: up to 100 jobs per run) and Google Sheets. The Actor charges $2 per 1,000 jobs; the node caps each run at $1. The first run returns every matching job, later runs only new ones.\n\n**Community node:** uses the verified Apify node (`@apify/n8n-nodes-apify`). On n8n Cloud it installs from the canvas; on self-hosted n8n install it under Settings > Community nodes.\n\n### Customization tips\nTurn on `withSalaryOnly` or `remoteOnly`, add `departments`, or turn on `includeDescription` and add a column for it.\n\nMade by locaihost, a small studio that builds its tools with AI agents.",
        "height": 859,
        "width": 560
      },
      "id": "596f5a2f-d255-5716-b965-28a4e3493e9f",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -670,
        -130
      ]
    }
  ],
  "connections": {
    "Every day at 08:00": {
      "main": [
        [
          {
            "node": "Get new job postings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get new job postings": {
      "main": [
        [
          {
            "node": "Shape sheet rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shape sheet rows": {
      "main": [
        [
          {
            "node": "Add rows to the Jobs sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
