AutomationFlowsData & Sheets › Find Jobs via Telegram Bot: Ai-powered Linkedin, Indeed & Monster Scraper

Find Jobs via Telegram Bot: Ai-powered Linkedin, Indeed & Monster Scraper

ByTegar karunia ilham @tegarkaruniailham on n8n.io

Turn job searching into a conversational experience! This intelligent Telegram bot automatically scrapes job postings from LinkedIn, Indeed, and Monster, filters for sales & marketing positions, and delivers personalized results directly to your chat. Interactive Telegram…

Event trigger★★★★☆ complexity15 nodesTelegram TriggerTelegramHTTP RequestGoogle SheetsAirtable
Data & Sheets Trigger: Event Nodes: 15 Complexity: ★★★★☆ Added:

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

This workflow follows the Airtable → Google Sheets 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
{
  "meta": {
    "version": "2.0"
  },
  "tags": [
    "telegram",
    "job-scraping",
    "sales",
    "marketing",
    "automation",
    "ai"
  ],
  "nodes": [
    {
      "id": "telegram_trigger",
      "name": "Telegram Bot Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        260,
        200
      ],
      "parameters": {
        "updates": [
          "message"
        ]
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "telegram_command_filter",
      "name": "Command Filter",
      "type": "n8n-nodes-base.filter",
      "position": [
        480,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{$json.message.text}}",
              "rightValue": "/start"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "job_search_filter",
      "name": "Job Search Filter",
      "type": "n8n-nodes-base.filter",
      "position": [
        480,
        350
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "startsWith"
              },
              "leftValue": "={{$json.message.text}}",
              "rightValue": "/jobs"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "welcome_message",
      "name": "Send Welcome Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        700,
        200
      ],
      "parameters": {
        "text": "\ud83e\udd16 **Job Scraper Bot Activated!** \ud83d\ude80\n\n*Welcome to your personal job hunting assistant!*\n\n**Available Commands:**\n/jobs [keyword] [location] - Search for jobs\n/status - Check bot status\n/help - Show this help\n\n**Examples:**\n\u2022 `/jobs sales manager New York`\n\u2022 `/jobs marketing remote`\n\u2022 `/jobs developer San Francisco`\n\n*Let me find the perfect job opportunities for you!* \ud83d\udcbc",
        "chatId": "={{$json.message.chat.id}}",
        "resource": "message",
        "operation": "sendMessage",
        "parseMode": "Markdown"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "parse_job_command",
      "name": "Parse Job Command",
      "type": "n8n-nodes-base.code",
      "position": [
        700,
        350
      ],
      "parameters": {
        "jsCode": "// Parse Telegram job search command\nconst message = $input.first().json.message;\nconst text = message.text;\nconst chatId = message.chat.id;\nconst userId = message.from.id;\nconst userName = message.from.first_name || 'User';\n\n// Parse command: /jobs [keyword] [location]\nconst parts = text.split(' ');\nlet keyword = 'sales marketing';\nlet location = 'New York';\n\nif (parts.length >= 2) {\n  keyword = parts.slice(1, -1).join(' ') || 'sales marketing';\n  location = parts[parts.length - 1] || 'New York';\n}\n\n// If only one parameter, treat as keyword\nif (parts.length === 2) {\n  keyword = parts[1];\n  location = 'New York';\n}\n\n// Create search parameters\nconst searchParams = {\n  keyword: keyword,\n  location: location,\n  chatId: chatId,\n  userId: userId,\n  userName: userName,\n  timestamp: new Date().toISOString()\n};\n\nreturn [{\n  json: {\n    ...searchParams,\n    originalMessage: message,\n    searchQuery: `${keyword} in ${location}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "search_status_message",
      "name": "Send Search Status",
      "type": "n8n-nodes-base.telegram",
      "position": [
        920,
        350
      ],
      "parameters": {
        "text": "\ud83d\udd0d **Searching for Jobs...**\n\n*Keyword:* `{{$json.keyword}}`\n*Location:* `{{$json.location}}`\n\n*Please wait while I scan LinkedIn, Indeed, and Monster for the best opportunities...* \u23f3",
        "chatId": "={{$json.chatId}}",
        "resource": "message",
        "operation": "sendMessage",
        "parseMode": "Markdown"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "linkedin_scraper_telegram",
      "name": "LinkedIn Jobs Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1140,
        250
      ],
      "parameters": {
        "url": "https://api.brightdata.com/datasets/v3/trigger",
        "method": "POST",
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 3,
            "waitBetween": 2000
          },
          "timeout": 30000
        },
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "dataset_id",
              "value": "gd_lpfbbndm1xnopbrcr0"
            },
            {
              "name": "country",
              "value": "US"
            },
            {
              "name": "keyword",
              "value": "={{$json.keyword}}"
            },
            {
              "name": "location",
              "value": "={{$json.location}}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "indeed_scraper_telegram",
      "name": "Indeed Jobs Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1140,
        350
      ],
      "parameters": {
        "url": "https://api.brightdata.com/datasets/v3/trigger",
        "method": "POST",
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 3,
            "waitBetween": 2000
          },
          "timeout": 30000
        },
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "dataset_id",
              "value": "gd_l4dx9j9sscpvs7no2"
            },
            {
              "name": "what",
              "value": "={{$json.keyword}}"
            },
            {
              "name": "where",
              "value": "={{$json.location}}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "monster_scraper_telegram",
      "name": "Monster Jobs Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1140,
        450
      ],
      "parameters": {
        "url": "https://api.monster.com/job-search/v2/search",
        "method": "GET",
        "options": {
          "retry": {
            "enabled": true,
            "maxTries": 3,
            "waitBetween": 2000
          },
          "timeout": 30000
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "q",
              "value": "={{$json.keyword}}"
            },
            {
              "name": "where",
              "value": "={{$json.location}}"
            },
            {
              "name": "page",
              "value": "1"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "telegram_job_processor",
      "name": "Process Jobs for Telegram",
      "type": "n8n-nodes-base.code",
      "position": [
        1360,
        350
      ],
      "parameters": {
        "jsCode": "// Enhanced job processing with Telegram context\nconst items = $input.all();\nconst processedJobs = [];\nlet chatId, userName, searchQuery;\n\n// Get Telegram context from first item\nif (items.length > 0 && items[0].json.chatId) {\n  chatId = items[0].json.chatId;\n  userName = items[0].json.userName || 'User';\n  searchQuery = items[0].json.searchQuery || 'jobs';\n}\n\nfor (const item of items) {\n  const source = item.json;\n  \n  // Skip items that are just telegram context\n  if (source.chatId && !source.company_name && !source.jobkey && !source.jobId) {\n    continue;\n  }\n  \n  // Determine source platform\n  let platform = 'unknown';\n  if (source.company_name || source.job_title) {\n    platform = 'linkedin';\n  } else if (source.jobkey || source.jobtitle) {\n    platform = 'indeed';\n  } else if (source.jobId) {\n    platform = 'monster';\n  }\n  \n  // Standardize job data structure\n  const standardizedJob = {\n    platform: platform,\n    job_id: source.job_id || source.jobkey || source.jobId || '',\n    title: source.job_title || source.jobtitle || source.title || '',\n    company: source.company_name || source.company || source.companyName || '',\n    location: source.location || source.formattedLocation || '',\n    description: source.description || source.snippet || '',\n    salary: source.salary || source.salary_formatted || '',\n    url: source.url || source.link || '',\n    posted_date: source.date_posted || source.date || source.postDate || '',\n    scraped_at: new Date().toISOString(),\n    // Telegram context\n    telegram_chat_id: chatId,\n    telegram_user: userName,\n    search_query: searchQuery,\n    // Enhanced filtering\n    is_relevant: checkRelevance(source.job_title || source.title || '', source.description || source.snippet || ''),\n    experience_level: extractExperience(source.description || source.snippet || ''),\n    remote_option: checkRemote(source.description || source.snippet || ''),\n    salary_range: extractSalary(source.salary || source.salary_formatted || '')\n  };\n  \n  processedJobs.push({ json: standardizedJob });\n}\n\n// Filter and deduplicate\nconst filteredJobs = processedJobs.filter(job => job.json.is_relevant);\nconst uniqueJobs = [];\nconst seen = new Set();\n\nfor (const job of filteredJobs) {\n  const key = `${job.json.title}-${job.json.company}`.toLowerCase().replace(/[^a-z0-9]/g, '');\n  if (!seen.has(key) && uniqueJobs.length < 10) { // Limit to 10 jobs for Telegram\n    seen.add(key);\n    uniqueJobs.push(job);\n  }\n}\n\n// Add summary statistics\nif (uniqueJobs.length > 0 && chatId) {\n  const summary = {\n    json: {\n      telegram_chat_id: chatId,\n      telegram_user: userName,\n      search_query: searchQuery,\n      total_jobs_found: uniqueJobs.length,\n      platforms_searched: [...new Set(uniqueJobs.map(job => job.json.platform))],\n      remote_jobs: uniqueJobs.filter(job => job.json.remote_option).length,\n      search_timestamp: new Date().toISOString(),\n      is_summary: true\n    }\n  };\n  uniqueJobs.unshift(summary);\n}\n\nfunction checkRelevance(title, description) {\n  const text = (title + ' ' + description).toLowerCase();\n  const keywords = ['sales', 'marketing', 'business development', 'account manager', 'digital marketing', \n                   'marketing manager', 'sales representative', 'business analyst', 'account executive'];\n  return keywords.some(keyword => text.includes(keyword));\n}\n\nfunction extractExperience(description) {\n  const senior_keywords = ['senior', '5+ years', 'lead', 'principal', 'director'];\n  const entry_keywords = ['entry level', '0-2 years', 'junior', 'associate'];\n  const text = description.toLowerCase();\n  \n  if (senior_keywords.some(keyword => text.includes(keyword))) return 'senior';\n  if (entry_keywords.some(keyword => text.includes(keyword))) return 'entry';\n  return 'mid';\n}\n\nfunction checkRemote(description) {\n  return description.toLowerCase().includes('remote') || \n         description.toLowerCase().includes('work from home') ||\n         description.toLowerCase().includes('telecommute');\n}\n\nfunction extractSalary(salary) {\n  if (!salary) return 'Not specified';\n  // Clean and format salary information\n  return salary.replace(/[^0-9$,k-]/gi, '').substring(0, 50);\n}\n\nreturn uniqueJobs;"
      },
      "typeVersion": 2
    },
    {
      "id": "format_telegram_message",
      "name": "Format Jobs Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1580,
        350
      ],
      "parameters": {
        "jsCode": "// Format jobs for Telegram message\nconst items = $input.all();\nlet chatId, userName, summary;\nconst jobs = [];\n\n// Separate summary from jobs\nfor (const item of items) {\n  if (item.json.is_summary) {\n    summary = item.json;\n    chatId = item.json.telegram_chat_id;\n    userName = item.json.telegram_user;\n  } else {\n    jobs.push(item.json);\n  }\n}\n\nif (jobs.length === 0) {\n  return [{\n    json: {\n      telegram_chat_id: chatId,\n      telegram_message: `\ud83d\ude1e **No Jobs Found**\\n\\n*Sorry ${userName}, I couldn't find any relevant job opportunities for your search.*\\n\\n*Try different keywords or locations!*`,\n      has_jobs: false\n    }\n  }];\n}\n\n// Create formatted message\nlet message = `\ud83c\udfaf **Job Search Results** \ud83c\udfaf\\n\\n`;\nmessage += `*Found ${summary.total_jobs_found} relevant opportunities*\\n`;\nmessage += `*Platforms: ${summary.platforms_searched.join(', ')}*\\n`;\nmessage += `*Remote jobs: ${summary.remote_jobs}*\\n\\n`;\nmessage += `\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\\n\\n`;\n\n// Format individual jobs\nfor (let i = 0; i < Math.min(jobs.length, 5); i++) {\n  const job = jobs[i];\n  const platformIcon = {\n    'linkedin': '\ud83d\udcbc',\n    'indeed': '\ud83d\udd0d', \n    'monster': '\ud83d\udc79'\n  }[job.platform] || '\ud83d\udcbc';\n  \n  message += `${platformIcon} **${job.title}**\\n`;\n  message += `\ud83c\udfe2 *${job.company}*\\n`;\n  message += `\ud83d\udccd ${job.location}\\n`;\n  \n  if (job.salary_range && job.salary_range !== 'Not specified') {\n    message += `\ud83d\udcb0 ${job.salary_range}\\n`;\n  }\n  \n  if (job.remote_option) {\n    message += `\ud83c\udf10 *Remote Available*\\n`;\n  }\n  \n  message += `\ud83d\udcca *${job.experience_level} level*\\n`;\n  \n  if (job.url) {\n    message += `\ud83d\udd17 [Apply Now](${job.url})\\n`;\n  }\n  \n  message += `\\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\\n\\n`;\n}\n\nif (jobs.length > 5) {\n  message += `*... and ${jobs.length - 5} more jobs available!*\\n\\n`;\n}\n\nmessage += `\u26a1 *Use /jobs [keyword] [location] for new search*\\n`;\nmessage += `\ud83d\udcbe *Jobs saved to Google Sheets & Airtable*`;\n\nreturn [{\n  json: {\n    telegram_chat_id: chatId,\n    telegram_message: message,\n    has_jobs: true,\n    job_count: jobs.length,\n    formatted_jobs: jobs.slice(0, 5)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "send_job_results",
      "name": "Send Job Results",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1800,
        350
      ],
      "parameters": {
        "text": "={{$json.telegram_message}}",
        "chatId": "={{$json.telegram_chat_id}}",
        "options": {
          "disable_web_page_preview": true
        },
        "resource": "message",
        "operation": "sendMessage",
        "parseMode": "Markdown"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "telegram_google_sheets",
      "name": "Save to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1580,
        500
      ],
      "parameters": {
        "options": {
          "useAppend": true
        },
        "operation": "appendOrUpdate",
        "sheetName": "Telegram_Jobs",
        "documentId": "your_google_sheet_id",
        "authentication": "oAuth2",
        "columnToMatchOn": "job_id"
      },
      "typeVersion": 4
    },
    {
      "id": "telegram_airtable",
      "name": "Save to Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1580,
        600
      ],
      "parameters": {
        "baseId": "your_airtable_base_id",
        "options": {
          "bulkSize": 10,
          "ignoreMissingColumns": true
        },
        "tableId": "telegram_jobs_table",
        "operation": "create",
        "authentication": "airtableTokenApi"
      },
      "typeVersion": 2
    },
    {
      "id": "usage_analytics",
      "name": "Log Usage Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1800,
        500
      ],
      "parameters": {
        "url": "https://hooks.zapier.com/hooks/catch/YOUR_WEBHOOK_ID/",
        "method": "POST",
        "options": {
          "timeout": 10000
        },
        "jsonBody": "={{ {\n  \"user_id\": $json.telegram_user,\n  \"chat_id\": $json.telegram_chat_id,\n  \"search_query\": $json.search_query,\n  \"jobs_found\": $json.job_count,\n  \"timestamp\": $json.search_timestamp,\n  \"platform\": \"telegram\",\n  \"workflow\": \"job_search\"\n} }}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.1
    }
  ],
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "saveManualExecutions": true
  },
  "updatedAt": "2025-09-05T11:41:00.000Z",
  "versionId": "2",
  "staticData": {},
  "connections": {
    "Command Filter": {
      "main": [
        [
          {
            "node": "Send Welcome Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Job Search Filter": {
      "main": [
        [
          {
            "node": "Parse Job Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Job Command": {
      "main": [
        [
          {
            "node": "Send Search Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Search Status": {
      "main": [
        [
          {
            "node": "LinkedIn Jobs Scraper",
            "type": "main",
            "index": 0
          },
          {
            "node": "Indeed Jobs Scraper",
            "type": "main",
            "index": 0
          },
          {
            "node": "Monster Jobs Scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Jobs Message": {
      "main": [
        [
          {
            "node": "Send Job Results",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log Usage Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Indeed Jobs Scraper": {
      "main": [
        [
          {
            "node": "Process Jobs for Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monster Jobs Scraper": {
      "main": [
        [
          {
            "node": "Process Jobs for Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Bot Trigger": {
      "main": [
        [
          {
            "node": "Command Filter",
            "type": "main",
            "index": 0
          },
          {
            "node": "Job Search Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Jobs Scraper": {
      "main": [
        [
          {
            "node": "Process Jobs for Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Jobs for Telegram": {
      "main": [
        [
          {
            "node": "Format Jobs Message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Save to Google Sheets",
            "type": "main",
            "index": 0
          },
          {
            "node": "Save to Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "triggerCount": 1
}

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

Turn job searching into a conversational experience! This intelligent Telegram bot automatically scrapes job postings from LinkedIn, Indeed, and Monster, filters for sales & marketing positions, and delivers personalized results directly to your chat. Interactive Telegram…

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

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

This n8n workflow helps you restrict access to your internal chats or chatbots so that only authorized team members can interact with them. It's perfect for setups using Telegram, Slack, or other corp

Telegram, Telegram Trigger, Slack Trigger +4
Data & Sheets

This template is ideal for solo store owners, eCommerce marketers, automation beginners, or anyone using Shopify and Gmail who wants to recover lost revenue without coding.

HTTP Request, Gmail, Twilio +3
Data & Sheets

This guide will walk you through setting up your n8n workflow. By the end, you'll have a fully automated system for managing your recruitment pipeline.

Google Calendar Trigger, Slack, HTTP Request +4
Data & Sheets

This n8n workflow lets you control access to your internal Telegram bots and automation systems based on user roles and departments. It ensures that only authorized team members — defined in your empl

Telegram Trigger, Data Table, Slack Trigger +4
Data & Sheets

Automatically process invoices and receipts using Gemini OCR, extracting data directly into Google Sheets from multiple sources including Google Drive, Gmail, and Telegram. This powerful workflow ensu

HTTP Request, Google Sheets, Google Drive +3