{
  "name": "Email new Indeed jobs that meet your salary and employer-rating bar with Apify and Google Sheets",
  "nodes": [
    {
      "parameters": {
        "content": "## Get only the Indeed jobs that meet your bar\n\n### How it works\nEvery morning this workflow searches Indeed with the [Indeed Global Job Scraper with Company Salary Reports](https://apify.com/nimbryxo-studio/indeed-global-job-scraper-with-company-salary) Actor on Apify and judges each new job once. Is it really the role you searched for? Does the salary range reach your minimum? Is the employer rated high enough? Does the ad avoid words you never want? Is it a repost of a job you already saw?\n\nJobs that pass arrive in one email, highest pay first. Every new job is saved in Google Sheets with its verdict, such as \"no salary listed\" or \"repost of a job already seen\", so you can see why a job was skipped. Days with nothing new send nothing.\n\nIt's for job seekers and recruiters who are tired of reading the same reposted ads. The salary bar uses the yearly figures Indeed shows. Jobs without a salary come in a separate list; set `includeJobsWithoutSalary` to false to drop them.\n\n### Setup\n1. In Apify Console > Settings > API & Integrations, copy your token. In n8n, create a **Header Auth** credential (name `Authorization`, value `Bearer <token>`) and select it in the three Apify nodes.\n2. Add a Google Sheets tab named `Jobs` with this header row: `key, roleKey, title, company, location, salary, salaryAnnualMin, salaryAnnualMax, companyRating, jobType, posted, verdict, firstSeen, url`\n3. Connect Google Sheets and Gmail.\n4. Fill in **Config**: job title, words the title must contain, location, country, salary and rating bars, excluded words, sheet ID and email.\n\n### Customization\nSet `workplaceType` to \"remote\" or widen `postedWithinDays`. Each run is billed to your Apify account; `maxCostUsd` caps it.",
        "height": 842,
        "width": 560
      },
      "id": "6363fc53-0509-4dc3-a2d2-1b0cbe145feb",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -660,
        -356
      ]
    },
    {
      "parameters": {
        "content": "## 1. Settings and jobs seen\nChecks Config before anything costs money, then reads every job already judged so none is judged twice.",
        "height": 388,
        "width": 940,
        "color": 7
      },
      "id": "1b029ef0-4771-4854-aadb-61d7afc9a2ea",
      "name": "Section: settings",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -188
      ]
    },
    {
      "parameters": {
        "content": "## 2. Run the Actor and wait\nStarts the Apify run and checks its status every minute until it ends. A failed or stuck run goes to the alert below.",
        "height": 828,
        "width": 460,
        "color": 7
      },
      "id": "5214647b-b088-4ec5-a5d9-dd8cb29ebb89",
      "name": "Section: run",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        920,
        -188
      ]
    },
    {
      "parameters": {
        "content": "## 3. Screen new jobs\nSkips reposts, jobs under your salary bar, low-rated employers and ads with excluded words. Each job keeps the reason.",
        "height": 388,
        "width": 460,
        "color": 7
      },
      "id": "8ed2fbe1-fb91-4615-a439-a92438cbf00d",
      "name": "Section: compare",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1400,
        -188
      ]
    },
    {
      "parameters": {
        "content": "## 4. Save and email\nAdds every new job with its verdict (matched on `key`) and emails the ones that pass, highest pay first.",
        "height": 828,
        "width": 460,
        "color": 7
      },
      "id": "cf713ac2-3056-4a12-a65d-98cb2046f136",
      "name": "Section: deliver",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1880,
        -188
      ]
    },
    {
      "parameters": {
        "content": "## If the run fails\nEmails the run status and a console link, then stops with an error so the execution shows as failed.",
        "height": 366,
        "width": 460,
        "color": 7
      },
      "id": "4cb10f0d-fe05-4318-a3bc-6aa6f42e53e5",
      "name": "Section: failure",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1400,
        274
      ]
    },
    {
      "parameters": {
        "content": "## Paid run\nStarts a run on your Apify account. `maxCostUsd` in Config caps what one run can spend.",
        "height": 148,
        "width": 300,
        "color": 3
      },
      "id": "12bc9c1a-0a45-4928-a5e8-3c317d19ed78",
      "name": "Warning: paid run",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        860,
        -356
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "0ac2f235-30b8-48c0-a63f-1a1b46fab14c",
      "name": "Every day at 08:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "{\n  \"position\": \"data analyst\",\n  \"titleMustInclude\": [\n    \"analyst\"\n  ],\n  \"location\": \"Austin, TX\",\n  \"country\": \"US\",\n  \"workplaceType\": \"\",\n  \"postedWithinDays\": \"1\",\n  \"maxJobs\": 50,\n  \"minAnnualSalary\": 70000,\n  \"includeJobsWithoutSalary\": true,\n  \"minCompanyRating\": 3.5,\n  \"excludeWords\": [\n    \"commission only\",\n    \"unpaid\"\n  ],\n  \"emailTo\": \"you@example.com\",\n  \"googleSheetId\": \"YOUR_GOOGLE_SHEET_ID\",\n  \"sheetTab\": \"Jobs\",\n  \"maxCostUsd\": 1,\n  \"maxWaitMinutes\": 20\n}\n",
        "options": {}
      },
      "id": "149f314f-868d-4d76-ac52-bb2c145a330a",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        240,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Checks Config before any paid Apify run and builds the Actor input.\nconst cfg = $('Config').first().json;\nconst problems = [];\nconst position = String(cfg.position || '').trim();\nif (!position) problems.push('set \"position\" to a job title or keywords, for example \"data analyst\"');\nconst maxJobs = Number(cfg.maxJobs);\nif (!Number.isInteger(maxJobs) || maxJobs < 1 || maxJobs > 1000) problems.push('\"maxJobs\" must be a whole number from 1 to 1000');\nif (!['1', '3', '7', '14', '30'].includes(String(cfg.postedWithinDays))) problems.push('\"postedWithinDays\" must be \"1\", \"3\", \"7\", \"14\" or \"30\"');\nif (cfg.minAnnualSalary !== '' && cfg.minAnnualSalary !== null && !Number.isFinite(Number(cfg.minAnnualSalary))) problems.push('\"minAnnualSalary\" must be a number (0 = no salary bar)');\nif (!cfg.googleSheetId || /YOUR_/i.test(String(cfg.googleSheetId))) problems.push('set \"googleSheetId\" to the ID in your sheet URL');\nif (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(String(cfg.emailTo || '')) || /example\\.com$/i.test(String(cfg.emailTo))) problems.push('set \"emailTo\" to your email address');\nif (problems.length) throw new Error(`Config: ${problems.join('; ')}`);\n\nconst actorInput = {\n  position,\n  country: String(cfg.country || 'US').toUpperCase(),\n  datePosted: String(cfg.postedWithinDays),\n  maxItems: maxJobs,\n  sort: 'date',\n  includeCompanyRatings: true,\n  saveOnlyUniqueItems: true,\n};\nif (String(cfg.location || '').trim()) actorInput.location = String(cfg.location).trim();\nif (['remote', 'hybrid', 'inPerson'].includes(cfg.workplaceType)) actorInput.workplaceType = cfg.workplaceType;\nreturn [{ json: { actorInput } }];\n"
      },
      "id": "77704593-f2b5-4e60-a17d-6256f559e310",
      "name": "Check settings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        0
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').first().json.googleSheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Config').first().json.sheetTab }}"
        },
        "options": {}
      },
      "id": "5d78ac2f-404c-4a5e-a482-f54296533b1a",
      "name": "Read previous run from Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        720,
        0
      ],
      "executeOnce": true,
      "alwaysOutputData": true,
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 3000
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/acts/nimbryxo-studio~indeed-global-job-scraper-with-company-salary/runs",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "waitForFinish",
              "value": "60"
            },
            {
              "name": "maxTotalChargeUsd",
              "value": "={{ $('Config').first().json.maxCostUsd }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($('Check settings').first().json.actorInput) }}",
        "options": {
          "timeout": 90000
        }
      },
      "id": "98fcfc99-1bb5-4f5c-ad47-72a66896e5b0",
      "name": "Start Apify run",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        960,
        0
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "succeeded",
                    "leftValue": "={{ $json.data.status }}",
                    "rightValue": "SUCCEEDED",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Succeeded"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "running",
                    "leftValue": "={{ ['READY', 'RUNNING'].includes($json.data.status) && $runIndex < $('Config').first().json.maxWaitMinutes }}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Still running"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "Failed or stuck"
        }
      },
      "id": "42c80900-35d7-4211-a0ac-b01b92f8d635",
      "name": "Route by run status",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        1200,
        0
      ]
    },
    {
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/actor-runs/' + $json.data.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "waitForFinish",
              "value": "60"
            }
          ]
        },
        "options": {
          "timeout": 90000
        }
      },
      "id": "b3a600ca-eeee-41d2-a0e1-eccb260e5ded",
      "name": "Check run status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1200,
        440
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/datasets/' + $json.data.defaultDatasetId + '/items' }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "clean",
              "value": "true"
            },
            {
              "name": "format",
              "value": "json"
            }
          ]
        },
        "options": {
          "timeout": 120000
        }
      },
      "id": "ac922f57-e88f-4874-a229-f52746a4792d",
      "name": "Download results",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1440,
        0
      ],
      "alwaysOutputData": true,
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Screens every job the first time it is seen: salary bar, employer rating, words you never want, reposts of a\n// job you already saw. Every new job is saved with its verdict so it is never judged twice; only the jobs that\n// pass go into the email. Returns ONE item: { rows } for the sheet and { report, subject, html } for the email.\nconst cfg = $('Config').first().json;\nconst today = new Date().toISOString().slice(0, 10);\nconst num = (v) => {\n  if (v === null || v === undefined || v === '') return null;\n  const n = Number(String(v).replace(/[^0-9.-]/g, ''));\n  return Number.isFinite(n) ? n : null;\n};\nconst esc = (s) => String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst norm = (s) => String(s ?? '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();\nconst salaryBar = num(cfg.minAnnualSalary) ?? 0;\nconst ratingBar = num(cfg.minCompanyRating);\nconst banned = (cfg.excludeWords || []).map(norm).filter(Boolean);\nconst titleWords = (cfg.titleMustInclude || []).map(norm).filter(Boolean); // Indeed's search also returns loosely related titles\n\nconst saved = new Map();\nconst seenRoles = new Set();\nfor (const item of $('Read previous run from Google Sheets').all()) {\n  const r = item.json || {};\n  if (!r.key) continue;\n  saved.set(String(r.key), r);\n  if (r.roleKey) seenRoles.add(String(r.roleKey));\n}\n\nconst jobs = $input.all().map((i) => i.json || {}).filter((j) => j.id || j.url);\nconst rows = [];\nconst matches = [];\nconst tally = { repost: 0, title: 0, salary: 0, rating: 0, words: 0 };\nfor (const j of jobs) {\n  const key = String(j.id || j.url);\n  if (saved.has(key)) continue; // judged on an earlier run\n  const roleKey = [norm(j.company), norm(j.normalizedTitle || j.positionName), norm(j.city || j.location)].join('|');\n  const top = num(j.salaryAnnualMax) ?? num(j.salaryAnnualMin);\n  const rating = num(j.rating);\n  const text = norm(`${j.positionName} ${j.description || ''}`);\n  let verdict = 'match';\n  const title = norm(j.positionName || j.displayTitle);\n  if (seenRoles.has(roleKey)) { verdict = 'repost of a job already seen'; tally.repost += 1; }\n  else if (titleWords.length && !titleWords.some((w) => ` ${title} `.includes(` ${w} `))) { verdict = 'title does not match'; tally.title += 1; }\n  else if (salaryBar > 0 && top === null && cfg.includeJobsWithoutSalary !== true) { verdict = 'no salary listed'; tally.salary += 1; }\n  else if (salaryBar > 0 && top !== null && top < salaryBar) { verdict = `pays up to ${Math.round(top)}, under your bar`; tally.salary += 1; }\n  else if (ratingBar !== null && rating !== null && rating < ratingBar) { verdict = `employer rated ${rating}`; tally.rating += 1; }\n  else {\n    const hit = banned.find((w) => ` ${text} `.includes(` ${w} `));\n    if (hit) { verdict = `mentions \"${hit}\"`; tally.words += 1; }\n  }\n  seenRoles.add(roleKey);\n  const row = {\n    key, roleKey,\n    title: j.positionName || '',\n    company: j.company || '',\n    location: j.location || '',\n    salary: j.salary || '',\n    salaryAnnualMin: num(j.salaryAnnualMin) ?? '',\n    salaryAnnualMax: num(j.salaryAnnualMax) ?? '',\n    companyRating: rating ?? '',\n    jobType: j.jobType || '',\n    posted: String(j.datePostedISO || '').slice(0, 10) || j.postedAt || '',\n    verdict,\n    firstSeen: today,\n    url: j.url || '',\n  };\n  rows.push(row);\n  if (verdict === 'match') matches.push(row);\n}\nmatches.sort((a, b) => (num(b.salaryAnnualMax) ?? num(b.salaryAnnualMin) ?? 0) - (num(a.salaryAnnualMax) ?? num(a.salaryAnnualMin) ?? 0));\n\nconst what = `${esc(cfg.position)}${cfg.location ? ` in ${esc(cfg.location)}` : ''}`;\nconst skipped = Object.entries(tally).filter(([, n]) => n).map(([k, n]) => `${n} ${{ repost: 'reposts', title: 'with other job titles', salary: 'under or without the salary bar', rating: 'below the rating bar', words: 'with excluded words' }[k]}`);\nlet subject = '';\nlet html = '';\nif (jobs.length === 0) {\n  subject = `Indeed job alert: no jobs returned for ${cfg.position}`;\n  html = `<p>Indeed returned no jobs for <b>${what}</b> posted in the last ${esc(cfg.postedWithinDays)} day(s). Widen <code>location</code> or <code>postedWithinDays</code> in Config if this repeats.</p>`;\n} else if (matches.length) {\n  subject = `${matches.length} new ${cfg.position} job${matches.length === 1 ? '' : 's'} worth a look`;\n  const li = (list) => `<ul style=\"font-family:Arial,sans-serif;font-size:14px;line-height:1.5\">${list.map((m) => `<li><a href=\"${esc(m.url)}\"><b>${esc(m.title)}</b></a> at ${esc(m.company)}${m.companyRating !== '' ? ` (rated ${esc(m.companyRating)})` : ''}, ${esc(m.location)}<br>${esc(m.salary || 'salary not listed')}${m.jobType ? ` &middot; ${esc(m.jobType)}` : ''}${m.posted ? ` &middot; posted ${esc(m.posted)}` : ''}</li>`).join('')}</ul>`;\n  const paid = matches.filter((m) => m.salaryAnnualMin !== '' || m.salaryAnnualMax !== '');\n  const open = matches.filter((m) => m.salaryAnnualMin === '' && m.salaryAnnualMax === '');\n  html = `<p>New since the last run for <b>${what}</b>:</p>`;\n  if (paid.length) html += `${salaryBar > 0 ? `<p><b>Pays ${Math.round(salaryBar).toLocaleString('en-US')} a year or more</b></p>` : ''}${li(paid)}`;\n  if (open.length) html += `<p><b>Salary not listed</b>${salaryBar > 0 ? ' (ask before you apply)' : ''}</p>${li(open)}`;\n} else if (saved.size === 0) {\n  subject = `Indeed job alert is set up for ${cfg.position}`;\n  html = `<p>First run: ${rows.length} jobs for <b>${what}</b> saved to your sheet, none of them passed your bar. You'll get an email when a new one does.</p>`;\n}\nif (html && skipped.length) html += `<p style=\"color:#666;font-size:12px\">Also new but skipped: ${esc(skipped.join(', '))}. Every job is in your sheet with the reason.</p>`;\nreturn [{ json: { report: Boolean(html), subject, html, rows, matches: matches.length, checked: jobs.length } }];\n"
      },
      "id": "31dc585d-62ac-4d42-ac22-2101e0af1292",
      "name": "Screen new jobs against your bar",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1680,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// One item per sheet row (none on a day without rows, so the sheet step is skipped).\nreturn ($json.rows || []).map((row) => ({ json: row }));"
      },
      "id": "f630ad87-54f4-486e-a87c-68e14d891768",
      "name": "Rows for the sheet",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1920,
        0
      ]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').first().json.googleSheetId }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Config').first().json.sheetTab }}"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "key"
          ],
          "schema": []
        },
        "options": {
          "cellFormat": "RAW"
        }
      },
      "id": "020cacf7-70d8-4bd2-a759-5363339123b2",
      "name": "Save to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2160,
        0
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 3000
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "report",
              "leftValue": "={{ $json.report }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "8cc65f0a-7129-40ae-aabf-4ff5c45e8a25",
      "name": "Anything to report?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1920,
        440
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $('Config').first().json.emailTo }}",
        "subject": "={{ $json.subject }}",
        "emailType": "html",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "4c6ed5c3-911d-4c73-a8b4-f929b56bc7e0",
      "name": "Email the report",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        2160,
        440
      ],
      "retryOnFail": true,
      "maxTries": 2,
      "waitBetweenTries": 3000
    },
    {
      "parameters": {
        "sendTo": "={{ $('Config').first().json.emailTo }}",
        "subject": "={{ 'Apify run ' + $json.data.status + ': Email new Indeed jobs that meet your salary and employer-rating bar' }}",
        "emailType": "html",
        "message": "={{ '<p>The Apify run ended as <b>' + $json.data.status + '</b>' + (['READY','RUNNING'].includes($json.data.status) ? ' (still running after ' + $('Config').first().json.maxWaitMinutes + ' minutes)' : '') + '.</p><p><a href=\"https://console.apify.com/view/runs/' + $json.data.id + '\">Open the run in Apify Console</a> to see its log.</p>' }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "968e48eb-a219-4108-af3c-8f2721aea18b",
      "name": "Alert: run failed",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1440,
        440
      ],
      "retryOnFail": true,
      "maxTries": 2,
      "waitBetweenTries": 3000
    },
    {
      "parameters": {
        "errorMessage": "={{ 'Apify run ' + $('Route by run status').item.json.data.status + ': https://console.apify.com/view/runs/' + $('Route by run status').item.json.data.id }}"
      },
      "id": "c48b2ff1-7cf3-4897-ab64-a3d5c3abb365",
      "name": "Stop: run failed",
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        1680,
        440
      ]
    }
  ],
  "connections": {
    "Every day at 08:00": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Check settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check settings": {
      "main": [
        [
          {
            "node": "Read previous run from Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read previous run from Google Sheets": {
      "main": [
        [
          {
            "node": "Start Apify run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Apify run": {
      "main": [
        [
          {
            "node": "Route by run status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by run status": {
      "main": [
        [
          {
            "node": "Download results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check run status",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Alert: run failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check run status": {
      "main": [
        [
          {
            "node": "Route by run status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download results": {
      "main": [
        [
          {
            "node": "Screen new jobs against your bar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Screen new jobs against your bar": {
      "main": [
        [
          {
            "node": "Rows for the sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Anything to report?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rows for the sheet": {
      "main": [
        [
          {
            "node": "Save to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anything to report?": {
      "main": [
        [
          {
            "node": "Email the report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert: run failed": {
      "main": [
        [
          {
            "node": "Stop: run failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  }
}
