AutomationFlowsWeb Scraping › Generate and Score Google Maps Leads with Gemini, Apify, and Google Sheets

Generate and Score Google Maps Leads with Gemini, Apify, and Google Sheets

Byiamvaar @iamvaar on n8n.io

Youtube Video:https://youtu.be/NGruDecKxAg?si=ot9pj6IN4IoLstow

Chat trigger trigger★★★★☆ complexityAI-powered25 nodesChat TriggerHTTP RequestGoogle Sheets
Web Scraping Trigger: Chat trigger Nodes: 25 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Chat Trigger → 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
{
  "id": "fRXGWXWLU7otJy8V",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Conversational Google Maps Lead Generation",
  "tags": [],
  "nodes": [
    {
      "id": "ad538e34-b292-4621-a76a-ae74de3049d6",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        272
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Conversational Google Maps Lead Generation\n### How it works\n\n1. Receives a search request via a chat interface and uses Gemini AI to parse the user's intent.\n2. Extracts search parameters and triggers Apify to scrape Google Maps for potential leads.\n3. Cleans and deduplicates the scraped results to ensure high-quality data.\n4. Uses Gemini AI to score and rank the leads based on specific criteria.\n5. Cross-references results with a Google Sheet to filter out previously saved leads.\n6. Saves the new, high-value leads back to the spreadsheet and sends a formatted reply back to the chat.\n\n### Setup steps\n\n- [ ] Add your Gemini API key to the HTTP Request nodes to enable AI parsing and scoring.\n- [ ] Set up an Apify account and authenticate the Apify HTTP Request node to allow scraping Google Maps.\n- [ ] Connect your Google account and select the correct Google Sheet in the 'Get row(s)' and 'Save to Google Sheets' nodes.\n\n### Customization\n\nYou can tweak the Gemini prompt in the 'Score & Rank Leads' node to match your business's specific criteria for a good lead."
      },
      "typeVersion": 1
    },
    {
      "id": "85bba3b3-5fdc-401d-bdfb-0939b4767957",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1504,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 304,
        "content": "## Process chat input\n\nReceives the user's search request via chat and uses Gemini AI to interpret their intent."
      },
      "typeVersion": 1
    },
    {
      "id": "53de951a-a5e8-43bd-b3c1-35b3417247bb",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1968,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## Search and scrape\n\nBuilds the necessary search parameters and triggers an Apify scraper to extract businesses from Google Maps."
      },
      "typeVersion": 1
    },
    {
      "id": "f87f5ba6-595d-48a7-9c5a-49ad62b48245",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 304,
        "content": "## Clean scraped data\n\nStrips out unnecessary data fields and removes any duplicate leads that were found."
      },
      "typeVersion": 1
    },
    {
      "id": "022593ff-869e-44aa-acb6-73f10b0abd6a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2864,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## AI lead scoring\n\nCombines the remaining leads into a list and sends them to Gemini AI to be scored and ranked."
      },
      "typeVersion": 1
    },
    {
      "id": "91991b91-ab6a-422b-a671-f3a31f81e040",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3312,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## Parse AI results\n\nExtracts the scored leads from the AI response and splits them back into individual items."
      },
      "typeVersion": 1
    },
    {
      "id": "f0ee2271-a5e3-406b-a2bf-58215f6feb90",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3744,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 320,
        "content": "## Check existing leads\n\nRetrieves existing records from Google Sheets and aggregates the new leads alongside them for comparison."
      },
      "typeVersion": 1
    },
    {
      "id": "f9f093b0-e796-4a80-a5c8-e3ec3549353c",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4208,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## Filter and save leads\n\nFilters out leads that have already been saved in the past and writes the new ones to the spreadsheet."
      },
      "typeVersion": 1
    },
    {
      "id": "d46ea733-efd3-4316-82b2-71253237c45e",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4640,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 304,
        "content": "## Format chat reply\n\nAggregates the newly saved leads to construct a formatted reply back to the chat interface."
      },
      "typeVersion": 1
    },
    {
      "id": "483d7769-dbbe-4610-abbf-d8abbdf0f4eb",
      "name": "When Chat Message Received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        1568,
        432
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "c98ba0ee-ca93-4b3a-a371-2287b370dd0d",
      "name": "Parse Search Intent",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1792,
        432
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-lite:generateContent",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ contents: [ { parts: [ { text: \"You are a request parser for a Google Maps lead-generation tool.\\n\\nExtract details from this user request.\\n\\nUser request: \\\"\" + $json.chatInput + \"\\\"\\n\\nRules:\\n- business_type: the type of business/service to search for, lowercase, as phrased.\\n- location: 'City, State/Country' if inferable, otherwise exactly as given.\\n- max_results: number of leads requested. Default 20 if unspecified. Never exceed 50.\\n- output_mode: 'contacts_only' if the user ONLY wants names and/or phone numbers (e.g. mentions 'numbers', 'phone numbers', 'contacts', 'just the numbers'). Otherwise 'full_details'.\\n\\nReturn ONLY a raw JSON object with exactly these keys: business_type, location, max_results, output_mode. No markdown, no explanation.\" } ] } ], generationConfig: { responseMimeType: \"application/json\" } }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1723d648-72d0-48d4-94d6-c3059a34ce7b",
      "name": "Set Search Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        2016,
        432
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b1-1",
              "name": "business_type",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).business_type }}"
            },
            {
              "id": "b1-2",
              "name": "location",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).location }}"
            },
            {
              "id": "b1-3",
              "name": "max_results",
              "type": "number",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).max_results }}"
            },
            {
              "id": "b1-5",
              "name": "apifyInput",
              "type": "object",
              "value": "={{ (() => { const p = JSON.parse($json.candidates[0].content.parts[0].text); return { includeWebResults: false, language: \"en\", locationQuery: p.location, maxCrawledPlacesPerSearch: p.max_results, maximumLeadsEnrichmentRecords: 0, scrapeDirectories: false, scrapeOrderOnline: false, scrapePlaceDetailPage: p.output_mode === \"full_details\", scrapeSocialMediaProfiles: { facebooks: false, instagrams: false, tiktoks: false, twitters: false, youtubes: false }, scrapeTableReservationProvider: false, searchStringsArray: [ p.business_type ], skipClosedPlaces: false, verifyLeadsEnrichmentEmails: false }; })() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "123ddb92-6d22-47c8-9f55-6f4374922c06",
      "name": "Scrape Google Maps via Apify",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2240,
        432
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/acts/WnMxbsRLNbPeYL6ge/run-sync-get-dataset-items",
        "method": "POST",
        "options": {
          "timeout": 300000
        },
        "jsonBody": "={{ $json.apifyInput }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "95129725-0b97-4299-89b2-768177bb09e8",
      "name": "Select Lead Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        2464,
        432
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c1-0",
              "name": "placeId",
              "type": "string",
              "value": "={{ $json.placeId }}"
            },
            {
              "id": "c1-1",
              "name": "title",
              "type": "string",
              "value": "={{ $json.title }}"
            },
            {
              "id": "c1-2",
              "name": "category",
              "type": "string",
              "value": "={{ $json.categoryName }}"
            },
            {
              "id": "c1-3",
              "name": "address",
              "type": "string",
              "value": "={{ $json.address }}"
            },
            {
              "id": "c1-4",
              "name": "phone",
              "type": "string",
              "value": "={{ $json.phone || $json.phoneUnformatted || '' }}"
            },
            {
              "id": "c1-5",
              "name": "website",
              "type": "string",
              "value": "={{ $json.website || '' }}"
            },
            {
              "id": "c1-6",
              "name": "rating",
              "type": "number",
              "value": "={{ $json.totalScore }}"
            },
            {
              "id": "c1-7",
              "name": "reviewsCount",
              "type": "number",
              "value": "={{ $json.reviewsCount }}"
            },
            {
              "id": "c1-8",
              "name": "mapsUrl",
              "type": "string",
              "value": "={{ $json.url }}"
            },
            {
              "id": "c1-9",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0fe4b408-c1e1-461a-baeb-8ec7a572f69c",
      "name": "Deduplicate by Place ID",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        2688,
        432
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": "placeId"
      },
      "typeVersion": 2
    },
    {
      "id": "1dea951e-688d-4b97-8189-70b6760802d1",
      "name": "Merge Scraped Leads",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2912,
        432
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "leads"
      },
      "typeVersion": 1
    },
    {
      "id": "fe3b7f0f-e099-400d-ba95-a06b39f6147a",
      "name": "Score and Rank Leads",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3136,
        432
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-lite:generateContent",
        "method": "POST",
        "options": {
          "timeout": 120000
        },
        "jsonBody": "={{ JSON.stringify({ contents: [ { parts: [ { text: \"You are scoring Google Maps business leads for a sales outreach campaign.\\n\\nSearch context: business_type=\\\"\" + $('Set Search Parameters').first().json.business_type + \"\\\", location=\\\"\" + $('Set Search Parameters').first().json.location + \"\\\"\\n\\nLeads (JSON array):\\n\" + JSON.stringify($json.leads) + \"\\n\\nFor each lead, assign a score from 0-100 based on:\\n- Has a phone number (+20)\\n- Has a website (+20)\\n- Rating >= 4.0 (+20)\\n- Review count >= 20 (+20)\\n- Category clearly matches the requested business type (+20)\\n\\nReturn ONLY a raw JSON array of the SAME lead objects (keep every existing field, including placeId), each with two added fields: 'score' (number) and 'score_reason' (short string). Sort the array descending by score. No markdown, no explanation, no extra text.\" } ] } ], generationConfig: { responseMimeType: \"application/json\" } }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "3ca45721-425f-44a0-9fb6-9ac4509f8c21",
      "name": "Prepare Scored Leads",
      "type": "n8n-nodes-base.set",
      "position": [
        3360,
        432
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e1-1",
              "name": "leads",
              "type": "array",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e35dda54-415b-4cb2-95af-7c41cff2415f",
      "name": "Split Leads Array",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        3584,
        432
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "leads"
      },
      "typeVersion": 1
    },
    {
      "id": "3d2c7bd9-50ac-4a1d-a410-070f4c898eee",
      "name": "Update Sheet with Leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        4480,
        432
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $json.phone }}",
            "score": "={{ $json.score }}",
            "title": "={{ $json.title }}",
            "rating": "={{ $json.rating }}",
            "address": "={{ $json.address }}",
            "mapsUrl": "={{ $json.mapsUrl }}",
            "placeId": "={{ $json.placeId }}",
            "website": "={{ $json.website }}",
            "category": "={{ $json.category }}",
            "description": "={{ $json.description }}",
            "reviewsCount": "={{ $json.reviewsCount }}",
            "score_reason": "={{ $json.score_reason }}"
          },
          "schema": [
            {
              "id": "placeId",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "placeId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "address",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "website",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "rating",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reviewsCount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reviewsCount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mapsUrl",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mapsUrl",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "score_reason",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "score_reason",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "placeId"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1831367429,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1N4ZTRINIzD09OllP6eUhC3JMQpdSZ1aNCFkqwfqRQWc/edit#gid=1831367429",
          "cachedResultName": "sheet"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1N4ZTRINIzD09OllP6eUhC3JMQpdSZ1aNCFkqwfqRQWc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1N4ZTRINIzD09OllP6eUhC3JMQpdSZ1aNCFkqwfqRQWc/edit?usp=drivesdk",
          "cachedResultName": "Google Maps Info"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "4ffdc3bb-d705-4b8b-ba7a-c0832a443b38",
      "name": "Aggregate Existing Leads",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        4032,
        432
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "leads"
      },
      "typeVersion": 1
    },
    {
      "id": "bd6ceb07-a4a8-49b9-9d4b-21858fb17e4a",
      "name": "Format Chat Response",
      "type": "n8n-nodes-base.code",
      "position": [
        4928,
        432
      ],
      "parameters": {
        "jsCode": "// The Aggregate node outputs to 'data' by default, not 'leads'\nconst leads = $input.first().json.data || []; \n\n// Fetch context, with a fallback for testing purposes\nconst searchParamsNode = $('Set Search Parameters').first();\nconst ctx = searchParamsNode ? searchParamsNode.json : { output_mode: 'default', business_type: 'Unknown Business', location: 'Unknown Location' };\n\nconst basic = ctx.output_mode === 'contacts_only';\n\nlet output = `Found ${leads.length} ranked leads for \"${ctx.business_type}\" in ${ctx.location}:\\n\\n`;\n\nleads.forEach((l, i) => {\n  if (basic) {\n    output += `${i + 1}. ${l.title} \u2014 ${l.phone || 'N/A'}\\n`;\n  } else {\n    output += `${i + 1}. ${l.title} \u2014 Score: ${l.score || 0}/100\\n`;\n    output += `   Phone: ${l.phone || 'N/A'} | Website: ${l.website || 'N/A'}\\n`;\n    output += `   Rating: ${l.rating || 'N/A'} (${l.reviewsCount || 0} reviews)\\n`;\n    output += `   Address: ${l.address || 'N/A'}\\n`;\n    if (l.mapsUrl) output += `   Maps: ${l.mapsUrl}\\n`;\n    if (l.description) output += `   About: ${l.description}\\n`;\n    if (l.score_reason) output += `   Why: ${l.score_reason}\\n`;\n  }\n  output += `\\n`;\n});\n\noutput += `Saved to your leads sheet (deduped by Place ID).`;\n\nreturn [{ json: { output, leads } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "c87873c3-f24f-484f-865f-add7bb4ea35c",
      "name": "Read Leads from Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3808,
        432
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.placeId }}",
              "lookupColumn": "placeId"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1831367429,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1N4ZTRINIzD09OllP6eUhC3JMQpdSZ1aNCFkqwfqRQWc/edit#gid=1831367429",
          "cachedResultName": "HVAC Contractors Data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1N4ZTRINIzD09OllP6eUhC3JMQpdSZ1aNCFkqwfqRQWc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1N4ZTRINIzD09OllP6eUhC3JMQpdSZ1aNCFkqwfqRQWc/edit?usp=drivesdk",
          "cachedResultName": "Google Maps Info"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "4dfc6c28-111f-4a0b-9d0e-3f0dece1f28c",
      "name": "Compare Leads Data",
      "type": "n8n-nodes-base.code",
      "position": [
        4256,
        432
      ],
      "parameters": {
        "jsCode": "// 1. Get the array of leads that already exist in the Google Sheet\nconst existingLeads = $input.first().json.leads || [];\n\n// 2. Create a Set of existing placeIds for fast lookup\nconst existingPlaceIds = new Set(existingLeads.map(lead => lead.placeId));\n\n// 3. Fetch all the original leads from the \"Split Out Leads\" node\nconst allLeads = $('Split Leads Array').all();\n\n// 4. Filter the original leads to return ONLY the ones not in the Google Sheet\nconst uniqueLeads = allLeads.filter(item => {\n  return !existingPlaceIds.has(item.json.placeId);\n});\n\n// 5. Return the new, unique leads to continue down the workflow\nreturn uniqueLeads;"
      },
      "typeVersion": 2
    },
    {
      "id": "59add994-839b-4885-8e20-37b93d1780d5",
      "name": "Finalize Results Aggregation",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        4704,
        432
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "beaac4a4-2b57-4c07-b0b8-4733755fde2c",
  "nodeGroups": [],
  "connections": {
    "Split Leads Array": {
      "main": [
        [
          {
            "node": "Read Leads from Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Leads Data": {
      "main": [
        [
          {
            "node": "Update Sheet with Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select Lead Fields": {
      "main": [
        [
          {
            "node": "Deduplicate by Place ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Scraped Leads": {
      "main": [
        [
          {
            "node": "Score and Rank Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Search Intent": {
      "main": [
        [
          {
            "node": "Set Search Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Scored Leads": {
      "main": [
        [
          {
            "node": "Split Leads Array",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score and Rank Leads": {
      "main": [
        [
          {
            "node": "Prepare Scored Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Leads from Sheet": {
      "main": [
        [
          {
            "node": "Aggregate Existing Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Search Parameters": {
      "main": [
        [
          {
            "node": "Scrape Google Maps via Apify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deduplicate by Place ID": {
      "main": [
        [
          {
            "node": "Merge Scraped Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet with Leads": {
      "main": [
        [
          {
            "node": "Finalize Results Aggregation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Existing Leads": {
      "main": [
        [
          {
            "node": "Compare Leads Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Chat Message Received": {
      "main": [
        [
          {
            "node": "Parse Search Intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Finalize Results Aggregation": {
      "main": [
        [
          {
            "node": "Format Chat Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Google Maps via Apify": {
      "main": [
        [
          {
            "node": "Select Lead Fields",
            "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

Youtube Video:https://youtu.be/NGruDecKxAg?si=ot9pj6IN4IoLstow

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

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

My workflow 8. Uses httpRequest, chatTrigger, chainLlm, lmChatOpenAi. Chat trigger; 14 nodes.

HTTP Request, Chat Trigger, Chain Llm +1
Web Scraping

Search-And-Crawl. Uses n8n-nodes-searxng, httpRequest, chatTrigger, executeWorkflowTrigger. Chat trigger; 8 nodes.

N8N Nodes Searxng, HTTP Request, Chat Trigger +1
Web Scraping

This workflow is Part 2 of the HR Client Acquisition system and builds on the lead discovery pipeline from the previous workflow:

Google Sheets, HTTP Request, OpenAI +2
Web Scraping

This workflow runs on a schedule to scrape job listings via Apify (Google Jobs and Career Site API), scores each role against your base resume using OpenAI, generates tailored cover letter and optiona

@Apify/N8N Nodes Apify, Google Sheets, Google Docs +4
Web Scraping

End-to-end lead pipeline (discovery → enrichment → outreach) Google Search–based LinkedIn discovery (safe approach) Batch processing with controlled loops AI-generated cold emails and follow-ups Googl

HTTP Request, Google Sheets, Google Gemini +1