AutomationFlowsSlack & Telegram › Fetch Daily Job Postings From Jsearch to Google Sheets and Telegram

Fetch Daily Job Postings From Jsearch to Google Sheets and Telegram

ByHrayr Movsisyan @hrayronair on n8n.io

This workflow automatically fetches job postings from the JSearch API once per day, filters out duplicates, and saves only new jobs to a Google Sheet. It also sends a Telegram summary with the number of newly added jobs.

Cron / scheduled trigger★★★★☆ complexity18 nodesHTTP RequestGoogle SheetsTelegram
Slack & Telegram Trigger: Cron / scheduled Nodes: 18 Complexity: ★★★★☆ Added:
Fetch Daily Job Postings From Jsearch to Google Sheets and Telegram — n8n workflow card showing HTTP Request, Google Sheets, Telegram integration

This workflow corresponds to n8n.io template #10918 — we link there as the canonical source.

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "NsSvrI5J3SWwYgKE",
  "name": "Daily Job Postings Fetcher by Query (JSearch \u2192 Google Sheets \u2192 Telegram)",
  "tags": [],
  "nodes": [
    {
      "id": "eac919e3-dd1c-4d6b-af7b-aa058f0b8bee",
      "name": "Run Daily at 22:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1856,
        608
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 22
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ff1ac477-1069-4269-8c8c-f64206683ddc",
      "name": "Fetch Jobs from JSearch API",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Fetches job results",
      "position": [
        -1184,
        912
      ],
      "parameters": {
        "url": "=https://jsearch.p.rapidapi.com/search?query={{ $json.query }}&page=1&num_pages=10&date_posted=week",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "155d612f-91d0-4469-aad0-3812fc3ab045",
      "name": "Split API Results",
      "type": "n8n-nodes-base.splitOut",
      "notes": "Splits job items",
      "position": [
        -976,
        912
      ],
      "parameters": {
        "include": "allOtherFields",
        "options": {},
        "fieldToSplitOut": "data"
      },
      "notesInFlow": true,
      "typeVersion": 1
    },
    {
      "id": "d4ad60d5-c586-4075-9780-a742cb644115",
      "name": "Load Existing Job IDs",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Loads saved job IDs",
      "position": [
        -928,
        592
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "[YOUR_GOOGLE_SHEET_HERE]"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "[YOUR_GOOGLE_SHEET_HERE]"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "abb11618-7250-4e47-85d1-eedb8058bea6",
      "name": "Combine API Results with Existing IDs",
      "type": "n8n-nodes-base.merge",
      "notes": "Merges items for check",
      "position": [
        -704,
        592
      ],
      "parameters": {
        "mode": "chooseBranch",
        "useDataOfInput": 2
      },
      "notesInFlow": true,
      "typeVersion": 3.2
    },
    {
      "id": "00cbeb08-38df-42d7-9cdb-bdcb46343069",
      "name": "Filter Out Existing Jobs",
      "type": "n8n-nodes-base.code",
      "notes": "Skips already saved jobs",
      "position": [
        -480,
        592
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Get all existing IDs from Google Sheets\nconst existingIds = $items('Load Existing Job IDs').map(i => i.json.ID);\n\n// Current job\nconst job = $input.item.json;\nconst jobId = job.data?.job_id;\n\n// If ID already exists \u2192 skip this item\nif (existingIds.includes(jobId)) {\n\treturn null; // <-- correct way to skip\n}\n\n// Otherwise \u2192 pass it forward\nreturn $input.item;"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "1e405143-b08f-4f8c-9f19-f9e48697cb69",
      "name": "Count New Jobs",
      "type": "n8n-nodes-base.code",
      "notes": "Counts new job items",
      "position": [
        -256,
        592
      ],
      "parameters": {
        "jsCode": "// Get all incoming items to this node\nconst allItems = $input.all();\n// Count them\nconst count = allItems.length;\n\nreturn [{ json: { count } }];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "756b2f27-e413-47f9-a251-75439e39f474",
      "name": "Loop Over New Jobs",
      "type": "n8n-nodes-base.splitInBatches",
      "notes": "Loops through new jobs",
      "position": [
        0,
        400
      ],
      "parameters": {
        "options": {}
      },
      "notesInFlow": true,
      "typeVersion": 3
    },
    {
      "id": "bad27543-e41e-407f-8fc9-aa1a2f6e7ac7",
      "name": "Delay Before Writing to Sheet",
      "type": "n8n-nodes-base.wait",
      "notes": "Google API limits",
      "position": [
        176,
        400
      ],
      "parameters": {
        "amount": 2
      },
      "notesInFlow": true,
      "typeVersion": 1.1
    },
    {
      "id": "1381d96a-9e1c-49cb-a6ef-0c56206c2cf8",
      "name": "Save Job to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Writes job to sheet",
      "position": [
        352,
        400
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID": "={{ $('Loop Over New Jobs').item.json.data.job_id }}",
            "Type": "={{ $('Loop Over New Jobs').item.json.data.job_employment_type }}",
            "Remote": "={{ $('Loop Over New Jobs').item.json.data.job_is_remote }}",
            "Salary": "={{ $('Loop Over New Jobs').item.json.data.job_salary }}",
            "Source": "={{ $('Loop Over New Jobs').item.json.data.job_publisher }}",
            "Status": "New",
            "Maximum": "={{ $('Loop Over New Jobs').item.json.data.job_max_salary }}",
            "Minimum": "={{ $('Loop Over New Jobs').item.json.data.job_min_salary }}",
            "Location": "={{ $('Loop Over New Jobs').item.json.data.job_location }}",
            "Job Title": "={{ $('Loop Over New Jobs').item.json.data.job_title }}",
            "Apply Link": "==HYPERLINK(\"{{ $('Loop Over New Jobs').item.json.data.job_apply_link }}\",\"Apply now\")",
            "Fetched At": "={{ $now }}",
            "Google Link": "==HYPERLINK(\"{{ $('Loop Over New Jobs').item.json.data.job_google_link }}\",\"View in Google\")",
            "Company Name": "={{ $('Loop Over New Jobs').item.json.data.employer_name }}",
            "Direct Apply": "={{ $('Loop Over New Jobs').item.json.data.job_apply_is_direct }}",
            "Company Website": "={{ $('Loop Over New Jobs').item.json.data.employer_website }}",
            "Date Posted (UTC)": "={{ new Date($('Loop Over New Jobs').item.json.data.job_posted_at_datetime_utc).toLocaleString('en-GB', { timeZone: 'Asia/Yerevan' }) }}"
          },
          "schema": [
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Job Title",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Job Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Company Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Company Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Apply Link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Apply Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Company Website",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Company Website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Type",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Direct Apply",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Direct Apply",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Remote",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Remote",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date Posted (UTC)",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date Posted (UTC)",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Fetched At",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Fetched At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Google Link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Google Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Salary",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Salary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Minimum",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Minimum",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Maximum",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Maximum",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "[YOUR_GOOGLE_SHEET_HERE]"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "[YOUR_GOOGLE_SHEET_HERE]"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.7
    },
    {
      "id": "3856c183-c121-4a81-9a30-0e99c2f1ef87",
      "name": "Combine Count and Save Results",
      "type": "n8n-nodes-base.merge",
      "notes": "Merges count and flow",
      "position": [
        176,
        592
      ],
      "parameters": {
        "mode": "chooseBranch",
        "useDataOfInput": 2
      },
      "typeVersion": 3.2
    },
    {
      "id": "d7c89547-3671-4852-8327-703d60507f06",
      "name": "Send Telegram Report",
      "type": "n8n-nodes-base.telegram",
      "notes": "Sends job summary",
      "position": [
        352,
        592
      ],
      "parameters": {
        "text": "={{ $json.count }} jobs fetched.  | <a href=\"https://docs.google.com/spreadsheets/d/1MlCTZvfqmKmIovTUTvr7b_2RY1q3ye9NXO9NMA48w1A/edit?gid=0#gid=0\">View</a>\n#Jobs_Wags",
        "chatId": "[YOUR_CHAT_ID_HERE]",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 1.2
    },
    {
      "id": "8398eca6-b09a-4724-a132-c81ff2ac1529",
      "name": "Set Search Parameters",
      "type": "n8n-nodes-base.set",
      "notes": "Edit the query here",
      "position": [
        -1632,
        912
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"search_query\": \"[YOUR_JOB_NAME_HERE]\"\n}\n"
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "7195e276-08c1-4ca6-af25-b7fdfc354307",
      "name": "Build Search Query",
      "type": "n8n-nodes-base.code",
      "notes": "Encodes the search query",
      "position": [
        -1408,
        912
      ],
      "parameters": {
        "jsCode": "const input = $json.search_query || \"\";\nconst query = encodeURIComponent(input);\n\nreturn [{ json: { query } }];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "fb9b986b-25e6-494b-9a57-826e52898f58",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1680,
        800
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 288,
        "content": "### Query Building and Querying Section\n* Defines search text, encodes it\n* Fetches job results from the API\n* Splits them into individual items"
      },
      "typeVersion": 1
    },
    {
      "id": "19b0bb8b-ec8b-4190-9069-8c44ad6b0dd7",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 304,
        "content": "### Existing-ID Filtering Section\n*  Loads existing job IDs from the sheet\n* Matches API results against them\n* Removes duplicates\n* Counts how many new jobs remain for processing"
      },
      "typeVersion": 1
    },
    {
      "id": "97696dc3-73e1-4c2e-bfb5-1fde2c85508c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 528,
        "content": "### Saving & Reporting Section\n* Iterates through new jobs\n* Applies a small delay to respect Google API limits\n* Writes each job to the sheet\n* Combines results\n* Sends a Telegram summary"
      },
      "typeVersion": 1
    },
    {
      "id": "6247f510-2f32-4915-929d-8192351e0675",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1904,
        -16
      ],
      "parameters": {
        "width": 736,
        "height": 608,
        "content": "## How it works\nThis workflow runs once per day and automatically collects new job postings from the JSearch API.\nIt builds a search query, fetches recent job listings, and splits the API response into individual jobs.\nBefore saving anything, it loads existing job IDs from a Google Sheet and filters out duplicates, ensuring each job is stored only once.\nNew jobs are then written to the sheet with structured fields (title, company, salary, links, location, etc.).\nTo avoid Google API rate limits, each write is slightly delayed.\nAt the end of the run, the workflow sends a Telegram message with a summary showing the number of new jobs added and a direct link to the sheet.\nThe goal is to provide a clean, automated daily job tracker that eliminates manual checks and duplicate entries.\n\n## Setup steps\n### Create a Google Sheet with matching headers\n(Status, ID, Job Title, Company Name, Apply Link, Company Website, Source, Type, Direct Apply, Remote, Date Posted (UTC), Fetched At, Location, Google Link, Salary, Minimum, Maximum)\n### Add credentials:\n\u2022 RapidAPI key for JSearch.\n\u2022 Google Sheets OAuth.\n\u2022 Telegram Bot token.\n### Update placeholders:\n\u2022 [YOUR_JOB_NAME_HERE] in Set Search Parameters.\n\u2022 [YOUR_GOOGLE_SHEET_HERE] (Document ID + Sheet).\n\u2022 [YOUR_CHAT_ID_HERE] for Telegram.\n### Enable the workflow and adjust the schedule time if needed."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "e631f69c-586d-4877-bc74-f9a673f61e62",
  "connections": {
    "Count New Jobs": {
      "main": [
        [
          {
            "node": "Combine Count and Save Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Split API Results": {
      "main": [
        [
          {
            "node": "Combine API Results with Existing IDs",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Build Search Query": {
      "main": [
        [
          {
            "node": "Fetch Jobs from JSearch API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over New Jobs": {
      "main": [
        [
          {
            "node": "Combine Count and Save Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Delay Before Writing to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Daily at 22:00": {
      "main": [
        [
          {
            "node": "Load Existing Job IDs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Set Search Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Existing Job IDs": {
      "main": [
        [
          {
            "node": "Combine API Results with Existing IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Search Parameters": {
      "main": [
        [
          {
            "node": "Build Search Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Out Existing Jobs": {
      "main": [
        [
          {
            "node": "Count New Jobs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Loop Over New Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Job to Google Sheet": {
      "main": [
        [
          {
            "node": "Loop Over New Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Jobs from JSearch API": {
      "main": [
        [
          {
            "node": "Split API Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delay Before Writing to Sheet": {
      "main": [
        [
          {
            "node": "Save Job to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Count and Save Results": {
      "main": [
        [
          {
            "node": "Send Telegram Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine API Results with Existing IDs": {
      "main": [
        [
          {
            "node": "Filter Out Existing Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automatically fetches job postings from the JSearch API once per day, filters out duplicates, and saves only new jobs to a Google Sheet. It also sends a Telegram summary with the number of newly added jobs.

Source: https://n8n.io/workflows/10918/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

This workflow automates plant care reminders and records using Google Sheets, Telegram, and OpenWeather API.

Google Sheets, HTTP Request, Telegram
Slack & Telegram

Apollo Data Enrichment Using Company Id to automatically finds contacts for companies listed in your Google Sheet, enriches each person with emails and phone numbers via Apollo’s API, and writes verif

Google Sheets, HTTP Request, Error Trigger +1
Slack & Telegram

++Download the google sheet here++ and replace this with the googles sheet node: Google sheet , upload to google sheets and replace in the google sheets node. Scheduled trigger: Runs once a day at 8 A

Google Sheets, HTTP Request, Telegram
Slack & Telegram

YT AI News Playlist Creator/AI News Form Updater. Uses googleSheets, httpRequest, splitOut, stickyNote. Scheduled trigger; 23 nodes.

Google Sheets, HTTP Request, YouTube +1
Slack & Telegram

Automatically monitor multiple websites every 5 minutes, log downtime, notify your team instantly via multiple channels, and track uptime/downtime in a Google Sheet—without relying on expensive monito

HTTP Request, Google Sheets, Gmail +2