AutomationFlowsAI & RAG › Monitor X/twitter for Hiring Posts with Apify, AI Filtering & Telegram Alerts

Monitor X/twitter for Hiring Posts with Apify, AI Filtering & Telegram Alerts

ByA Z @db-aze on n8n.io

Automatically scrape X (Twitter) for posts hiring specific roles (e.g., automation engineers, video editors, graphic designers), filter true hiring intent with AI, deduplicate in Google Sheets, and alert via Telegram. Pulls recent X/Twitter posts for multiple role keywords via…

Cron / scheduled trigger★★★★☆ complexityAI-powered13 nodesGoogle SheetsHTTP RequestTelegramAgentOpenAI ChatGoogle Sheets Tool
AI & RAG Trigger: Cron / scheduled Nodes: 13 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Agent → 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": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "b522f688-3201-468f-878c-96575adeef55",
      "name": "Append or update row in sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -832,
        1584
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $json.url }}",
            "text": "={{ $json.text }}",
            "author": "={{ $json.author.userName }}",
            "location": "={{ $json.author.location }}",
            "url author": "={{ $json.author.url }}"
          },
          "schema": [
            {
              "id": "url",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "text",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "text",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "author",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "author",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "url author",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "url author",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "location",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "url"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1b87NBYctXlv5e-cfjF8XrGgtfLElgnELyDBcumoM2bE/edit#gid=0",
          "cachedResultName": "X"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1b87NBYctXlv5e-cfjF8XrGgtfLElgnELyDBcumoM2bE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1b87NBYctXlv5e-cfjF8XrGgtfLElgnELyDBcumoM2bE/edit?usp=drivesdk",
          "cachedResultName": "X and threads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "92929cdf-2771-4e86-bd78-9d1cd7c8c4ba",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -1888,
        1680
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "946aeca1-2f8e-418d-9970-45a0c3c768c0",
              "name": "url",
              "type": "string",
              "value": "={{ $json.url }}"
            },
            {
              "id": "9c6a1f7e-5975-47bf-bebd-42eab220ccf1",
              "name": "text",
              "type": "string",
              "value": "={{ $json.text }}"
            },
            {
              "id": "1796f734-f0d9-430b-bce9-2c7fe9fa50c6",
              "name": "author.userName",
              "type": "string",
              "value": "={{ $json.author.userName }}"
            },
            {
              "id": "dcbda70c-ce37-4c6e-971b-d25a0cc1ef59",
              "name": "author.url",
              "type": "string",
              "value": "={{ $json.author.url }}"
            },
            {
              "id": "edaa24fe-eb31-4caa-a67f-2bfd6649752f",
              "name": "author.location",
              "type": "string",
              "value": "={{ $json.author.location }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d8ed862b-a231-46ea-ba29-eeddb19a6b1e",
      "name": "6 hours",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2336,
        1680
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 12
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ad8fa0da-5452-4de7-849c-56920740d38d",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2112,
        1680
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/apidojo~tweet-scraper/run-sync-get-dataset-items?",
        "method": "POST",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "jsonBody": "{\n    \"maxItems\": 50,\n    \"searchTerms\": [\n      \"n8n developer\", \n      \"looking for n8n\",\n      \"n8n expert\",\n      \"hire AI automation\", \n      \"looking for AI automation\"\n    ],\n    \"sort\": \"Latest\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "Bearer "
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "c7442722-6a28-4e0e-b8bd-09fc95cd6a2f",
      "name": "Send a text message1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -832,
        1744
      ],
      "parameters": {
        "text": "=Tweet URL: {{ $json.url }} \nAuthor:{{ $json.author.userName }}\nAuthor URL: {{ $json.author.url }}\nLocation:{{ $json.author.location }}\nText: {{ $json.text }}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0745d915-2372-4ae4-af55-10f659327f68",
      "name": "AI Agent1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1664,
        1680
      ],
      "parameters": {
        "text": "=This is the text that you have to check  {{ $json.text }}{{ $json.url }}{{ $json.author }}\n",
        "options": {
          "systemMessage": "=<goal> You are **Hiring Post Classifier & Extractor**. Read short social posts (Twitter/Threads), plus author bio and any link-preview text, and return **only** posts where the **author is hiring an n8n/AI automation engineer** in allowed geographies. Deduplicate against Google Sheets by `url`. If non-passing or duplicate \u2192 **output nothing**. </goal>\n\n<format_rules>\n\nInput (exact shape):\n\n{\n  \"url\": \"https://twitter.com/... or https://www.threads.net/...\",\n  \"text\": \"raw post text\",\n  \"author\": {\n    \"userName\": \"handle or display name\",\n    \"url\": \"author profile url\",\n    \"location\": \"free-text location from bio (optional)\"\n  }\n}\n\n\nOutput: strict JSON (see <output>) for passing, non-duplicate posts; otherwise no output (empty response).\n\nNo extra fields, arrays, prose, or explanations.\n</format_rules>\n\n\n<decision_rules>\n\nReduce tokens where ever possible\n\nHire-only by the author (no replies/retweets unless the author restates as the hirer).\nAccept only if text clearly signals an open n8n/AI automation engineer role.\nStrong hire cues (any): hiring, we\u2019re hiring, opening, role, position, join our team, bring on an automation engineer, seeking n8n engineer, need an AI automation engineer, apply, send your portfolio/resume, email to, link to job.\nHelpful (not required): full-time, part-time, contract, paid, salary, compensation.\nRole scope (accept): n8n engineer, AI automation engineer, workflow automation engineer, automation developer (explicit n8n/AI automation duties), no-code/low-code automation engineer (with n8n/AI explicitly mentioned).\nExclude: generic software roles without automation focus; RPA-only if n8n/AI automation isn\u2019t explicit; \u201cconsultant available\u201d language (see self-promo).\n\nExclude self-promotion / service ads / bait.\nReject if the author markets themselves/services (e.g., \u201chire me,\u201d \u201cavailable for projects,\u201d \u201cmy rates,\u201d \u201cportfolio/reel,\u201d \u201cDMs open to hire me,\u201d \u201cbook me,\u201d \u201cclient work,\u201d \u201ccommissions,\u201d \u201cservices\u201d).\nIf the bio indicates the author is a freelancer for hire and the post is ambiguous \u2192 treat as self-promo \u2192 exclude.\n\nDeduplication (Google Sheets).\nAfter passing 1\u20133, query the sheet for the url. If found \u2192 no output. If not found \u2192 output JSON.\n</decision_rules>\n\n<tools_and_usage>\n<google_sheets access=\"read-only\">\n\nmethod: find(url) \u2192 returns a match if the post url already exists\n</google_sheets>\n</tools_and_usage>\n\n<planning_guidance>\n\nConfirm author hiring signal for an n8n/AI automation engineer (not self-promo; not a reply/retweet unless restated).\n\nApply geo rules using post text, bio location, and link-preview text.\n\nIf pass, dedupe via Google Sheets on url.\n\nIf unique, emit strict JSON with minimal pass-through fields.\n</planning_guidance>\n\n<output> For passing, non-duplicate posts, return **only**: ```json { \"url\": \"{{ $json.url }}\", \"text\": \"{{ $json.text }}\", \"username\": \"{{ $json.author.userName }}\", \"profile_url\": \"{{ $json.author.url }}\", \"location\": \"{{ $json.author.location }}\" } ``` Otherwise, **return nothing**. </output>\n\n"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "beae3b6b-780b-4fa8-836b-c17e9d1bfa73",
      "name": "OpenAI Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -1664,
        1904
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ff6f42d3-893c-453f-bf85-db69c094ad46",
      "name": "Get row(s) in sheet in Google Sheets1",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        -1536,
        1904
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1b87NBYctXlv5e-cfjF8XrGgtfLElgnELyDBcumoM2bE/edit#gid=0",
          "cachedResultName": "X"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1b87NBYctXlv5e-cfjF8XrGgtfLElgnELyDBcumoM2bE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1b87NBYctXlv5e-cfjF8XrGgtfLElgnELyDBcumoM2bE/edit?usp=drivesdk",
          "cachedResultName": "X and threads"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "e4a986d2-4146-46bc-9ca8-d4acc07c9e52",
      "name": "Code1",
      "type": "n8n-nodes-base.code",
      "position": [
        -1088,
        1680
      ],
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n  try {\n    const rawData = item.json.output; // The raw string from the LLM\n\n    // --- FIX IS HERE ---\n    // Find the first opening curly brace '{'\n    const firstBrace = rawData.indexOf('{');\n    // Find the last closing curly brace '}'\n    const lastBrace = rawData.lastIndexOf('}');\n\n    // If both braces are found, extract the content between them\n    if (firstBrace !== -1 && lastBrace !== -1) {\n      const jsonString = rawData.substring(firstBrace, lastBrace + 1);\n\n      // Now, parse the cleaned string\n      const obj = JSON.parse(jsonString);\n\n      results.push({ json: obj });\n    }\n    // --- END OF FIX ---\n\n  } catch (error) {\n    // This will catch any items that still fail to parse,\n    // so the whole workflow doesn't stop for one bad item.\n    console.error(\"Failed to parse an item:\", error);\n  }\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4f0c0edc-ac12-41ff-9733-71df7f1ed66f",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -1312,
        1680
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d6b35f7a-4b86-4aa0-a826-c6992ad15da2",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.output }}",
              "rightValue": "={}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "63532971-8154-4fd7-8805-c9a86d685588",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3088,
        1264
      ],
      "parameters": {
        "color": 5,
        "width": 672,
        "height": 1184,
        "content": "Automatically scrape X (Twitter) for posts hiring specific roles (e.g., automation engineers, video editors, graphic designers), filter true hiring intent with AI, deduplicate in Google Sheets, and alert via Telegram.\n\n## What it does\n\n- Pulls recent X/Twitter posts for multiple role keywords via Apify.\n\n- Normalizes each post (text, author, links, location).\n\n- Uses an AI Agent to keep only posts where the author is hiring (not self-promo).\n\n- Checks Google Sheets for duplicates by URL before saving.\n\n- Writes qualified posts to a sheet and sends a Telegram notification.\n\nWe are using n8n automation roles as the example here\n\n\n## How it works (Step by Step)\n\n1. **Schedule Trigger** \u2013 Runs on an interval (currently every 12 hours).\n\n2. **Scrape X/Twitter** \u2013 Apify tweet-scraper fetches up to 50 latest posts for keywords like:\nn8n developer, looking for n8n, n8n expert, hire AI automation, looking for AI automation.\n\n3. **Normalize Fields** \u2013 Set node maps to: url, text, author.userName, author.url, author.location.\n\n4. **AI Filter & Dedupe Check**\n\n- Accept only clear hiring posts for n8n/AI automation roles (reject self-promotion).\n- Queries Google Sheets to see if url already exists; duplicates are dropped.\n\n5. **Gate** \u2013 IF node passes only non-empty AI outputs.\n\n6. **Parse JSON Safely** \u2013 Code node extracts/validates JSON from the AI output.\n\n7. **Save to Google Sheets** \u2013 Appends/updates a row (matching on url).\n\n8. **Telegram Alert** \u2013 Sends a message with the tweet URL, author, location, and text.\n\n## Who it\u2019s for\n\nFreelancers, agencies, and job seekers who want a steady radar of real hiring posts for their target roles.\n\n## Customization Ideas\n\nSwap keywords to track other roles (video editors, designers, copywriters, etc.).\n\nAdd Slack/Discord notifications.\n\nExtend the AI rules (e.g., different geographies or role scopes).\n\nTreat the sheet as a mini-CRM (status, outreach date, notes)."
      },
      "typeVersion": 1
    },
    {
      "id": "c143fed2-4d73-4532-8492-5fb66fd1ed2e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3088,
        2496
      ],
      "parameters": {
        "width": 672,
        "height": 256,
        "content": "# Looking for tailored workflows? Book through my Reddit or website\u2014and follow for future templates.\n## Reddit: https://www.reddit.com/user/designbyaze/\n## Website: https://hushtech.io/"
      },
      "typeVersion": 1
    },
    {
      "id": "76c04377-96f0-45ec-84be-6d55e9206f29",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2224,
        1264
      ],
      "parameters": {
        "color": 5,
        "width": 336,
        "height": 256,
        "content": "## 1. Replace the API keys in each Https node\n## 2. Add the specified role in the json part.\n## 3, The apify actor used: https://apify.com/apidojo/tweet-scraper\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Code1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code1": {
      "main": [
        [
          {
            "node": "Send a text message1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Append or update row in sheet1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6 hours": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent1": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "AI Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet in Google Sheets1": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "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

Automatically scrape X (Twitter) for posts hiring specific roles (e.g., automation engineers, video editors, graphic designers), filter true hiring intent with AI, deduplicate in Google Sheets, and alert via Telegram. Pulls recent X/Twitter posts for multiple role keywords via…

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

Template Name: AI Personal Assistant - Task & Email Management Price: $27 Category: Productivity & Automation Difficulty: Intermediate Use Case: Personal productivity automation for busy professionals

Telegram, HTTP Request, OpenAI +8
AI & RAG

Automatically scrape Meta Threads for posts hiring specific roles (e.g. automation engineers, video editors, graphic designers), filter true hiring intent, deduplicate, and send alerts.

Agent, OpenAI Chat, Google Sheets +3
AI & RAG

This automation is designed to help you generate AI-powered music tracks, cover art, and fully rendered music videos — all triggered from a simple Telegram chat and managed via Google Sheets.

OpenAI Chat, Memory Buffer Window, Output Parser Structured +11
AI & RAG

🧠 Gwen – The AI Voice Marketing Agent Gwen is your intelligent voice-powered marketing assistant built in n8n. She combines the power of OpenAI, ElevenLabs, and automation workflows to handle content

Tool Workflow, Memory Buffer Window, Agent +10
AI & RAG

This workflow automates the process of generating, reviewing, and publishing blog posts across multiple platforms, now enhanced with support for RSS Feeds as a content source. It streamlines the manag

HTTP Request, Html Extract, RSS Feed Read +9