{
  "name": "Indeed jobs to Google Sheets | Group Oject starter",
  "active": false,
  "nodes": [
    {
      "id": "run-manually",
      "name": "Run manually",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "parameters": {}
    },
    {
      "id": "search-settings",
      "name": "Search settings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "parameters": {
        "jsCode": "return [{json:{searchTerm:'software engineer',location:'New York, NY',sheetId:'REPLACE_WITH_GOOGLE_SHEET_ID'}}];"
      }
    },
    {
      "id": "read-existing-jobs",
      "name": "Read existing jobs",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        440,
        0
      ],
      "parameters": {
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Search settings').first().json.sheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Jobs"
        },
        "operation": "read",
        "options": {}
      },
      "alwaysOutputData": true
    },
    {
      "id": "fetch-up-to-20-jobs",
      "name": "Fetch up to 20 jobs",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        0
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/acts/groupoject~indeed-jobs-scraper/run-sync-get-dataset-items?timeout=120&maxTotalChargeUsd=0.10&format=json&clean=true",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({searchTerm:$('Search settings').first().json.searchTerm,location:$('Search settings').first().json.location,jobBoards:['indeed'],countryIndeed:'USA',maxResultsPerSite:20,maxItems:20,onlyNewJobs:false}) }}",
        "options": {
          "timeout": 150000,
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "executeOnce": true,
      "retryOnFail": false
    },
    {
      "id": "validate-and-deduplicate",
      "name": "Validate and deduplicate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        0
      ],
      "parameters": {
        "jsCode": "\nconst response = $input.first().json;\nif (response.statusCode !== 200 || !Array.isArray(response.body)) throw new Error('Expected a successful Apify dataset response. Inspect the run before retrying.');\nconst seen = new Set($('Read existing jobs').all().map(i => String(i.json.jobUrl || '').trim()));\nconst safe = value => { const s = String(value ?? '').replace(/[\\x00-\\x1f]/g, ' ').slice(0, 1000); return /^[=+@-]/.test(s.trimStart()) ? \"'\" + s : s; };\nconst rows = [];\nfor (const job of response.body) {\n  if (!job || typeof job.title !== 'string' || !job.title.trim() || typeof job.jobUrl !== 'string') throw new Error('Unexpected job schema; no rows written.');\n  let url; try { url = new URL(job.jobUrl); } catch { throw new Error('Invalid job URL; no rows written.'); }\n  if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) throw new Error('Unsafe job URL; no rows written.');\n  url.hash = '';\n  const key = url.href;\n  if (seen.has(key)) continue;\n  seen.add(key);\n  rows.push({ json: { jobUrl: key, title: safe(job.title), company: safe(job.company), location: safe(job.location), jobBoard: safe(job.jobBoard), collectedAt: new Date().toISOString() } });\n}\nreturn rows;\n"
      }
    },
    {
      "id": "save-jobs",
      "name": "Save jobs",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        1100,
        0
      ],
      "parameters": {
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Search settings').first().json.sheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Jobs"
        },
        "operation": "appendOrUpdate",
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "jobUrl"
          ],
          "schema": [
            {
              "id": "jobUrl",
              "displayName": "jobUrl",
              "type": "string",
              "required": false,
              "defaultMatch": true,
              "canBeUsedToMatch": true,
              "display": true
            },
            {
              "id": "title",
              "displayName": "title",
              "type": "string",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true
            },
            {
              "id": "company",
              "displayName": "company",
              "type": "string",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true
            },
            {
              "id": "location",
              "displayName": "location",
              "type": "string",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true
            },
            {
              "id": "jobBoard",
              "displayName": "jobBoard",
              "type": "string",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true
            },
            {
              "id": "collectedAt",
              "displayName": "collectedAt",
              "type": "string",
              "required": false,
              "defaultMatch": false,
              "canBeUsedToMatch": true,
              "display": true
            }
          ]
        },
        "options": {
          "cellFormat": "RAW"
        }
      }
    }
  ],
  "connections": {
    "Run manually": {
      "main": [
        [
          {
            "node": "Search settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search settings": {
      "main": [
        [
          {
            "node": "Read existing jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read existing jobs": {
      "main": [
        [
          {
            "node": "Fetch up to 20 jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch up to 20 jobs": {
      "main": [
        [
          {
            "node": "Validate and deduplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate and deduplicate": {
      "main": [
        [
          {
            "node": "Save jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": []
}
