AutomationFlowsData & Sheets › Automated Lead Scraping to Airtable & Gmail

Automated Lead Scraping to Airtable & Gmail

Original n8n title: Lead Generating Web Scraper & CRM Automation

Lead Generating Web Scraper & CRM Automation. Uses httpRequest, airtable, googleSheets, gmail. Scheduled trigger; 38 nodes.

Cron / scheduled trigger★★★★★ complexityAI-powered38 nodesHTTP RequestAirtableGoogle SheetsGmailError TriggerChain LlmOpenRouter Chat
Data & Sheets Trigger: Cron / scheduled Nodes: 38 Complexity: ★★★★★ AI nodes: yes Added:
Automated Lead Scraping to Airtable & Gmail — n8n workflow card showing HTTP Request, Airtable, Google Sheets integration

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

This workflow follows the Airtable → Chainllm 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
{
  "updatedAt": "2026-02-11T07:52:30.443Z",
  "createdAt": "2026-01-20T02:37:46.215Z",
  "id": "uq4hnH0YHfhYOOzO",
  "name": "Lead Generating Web Scraper & CRM Automation",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "id": "8b09efbe-4322-4e7f-8512-7805878088f9",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        200,
        300
      ],
      "typeVersion": 1.2
    },
    {
      "parameters": {},
      "id": "d35df8b8-f968-443c-8b89-39a59c0c5b6c",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        200,
        500
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "84cf11db-2680-4463-a454-c699becb72ee",
              "name": "searchQuery",
              "value": "dentists",
              "type": "string"
            },
            {
              "id": "f08cdf9d-8546-4dd3-b7eb-b94bbef7c532",
              "name": "location",
              "value": "Johannesburg",
              "type": "string"
            },
            {
              "id": "9e4af1d2-7149-45af-b052-9ab642a6754e",
              "name": "maxResults",
              "value": "50",
              "type": "number"
            },
            {
              "id": "115ebffa-b1b5-499a-a3a6-e37a19f90c2f",
              "name": "senderName",
              "value": "Ian Immelman",
              "type": "string"
            },
            {
              "id": "6a950459-74af-480d-a378-940e0b505ba7",
              "name": "senderCompany",
              "value": "AnyVision Media",
              "type": "string"
            },
            {
              "id": "8e8b75f3-f674-46fe-9fb4-6560bd139785",
              "name": "senderTitle",
              "value": "Director",
              "type": "string"
            },
            {
              "id": "7de74b4e-fbf6-434d-8340-552d9d6f468c",
              "name": "senderEmail",
              "value": "ian@anyvisionmedia.com",
              "type": "string"
            }
          ]
        }
      },
      "id": "d9c299bf-1039-4bb6-8c83-9025d9f67d29",
      "name": "Search Config",
      "type": "n8n-nodes-base.set",
      "position": [
        440,
        400
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {
        "jsCode": "const config = $input.first().json;\nconst query = encodeURIComponent(config.location + ' ' + config.searchQuery);\nconst url = 'https://www.google.com/maps/search/' + query;\nreturn {\n  json: {\n    ...config,\n    mapsUrl: url\n  }\n};"
      },
      "id": "de1745b8-9a52-4853-88e0-f120b7628682",
      "name": "Build Maps URL",
      "type": "n8n-nodes-base.code",
      "position": [
        660,
        400
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "url": "={{ $json.mapsUrl }}",
        "options": {
          "allowUnauthorizedCerts": true,
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "headers": {
            "parameters": [
              {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
              },
              {
                "name": "Accept-Language",
                "value": "en-US,en;q=0.9"
              }
            ]
          }
        }
      },
      "id": "0833296b-0d6b-4ce3-acf5-5f1cf4a7606f",
      "name": "Scrape Google Maps",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        400
      ],
      "typeVersion": 4.2,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const html = $input.first().json.data || '';\nconst config = $('Search Config').first().json;\n\n// Extract website URLs from Google Maps HTML\nconst urlRegex = /https?:\\/\\/[^\\/\\s\"'>]+/g;\nconst allUrls = html.match(urlRegex) || [];\n\n// Filter out Google/system domains\nconst blockedDomains = ['google', 'gstatic', 'googleapis', 'schema', 'ggpht', 'youtube', 'goo.gl'];\nconst validUrls = [...new Set(allUrls)].filter(url => {\n  const lower = url.toLowerCase();\n  return !blockedDomains.some(d => lower.includes(d));\n});\n\n// Try to extract business names from Maps HTML\n// Google Maps embeds business data in various patterns\nconst nameRegex = /\\[\"([^\"]{3,60})\"(?:,null){0,3},\"https?:\\/\\//g;\nconst names = [];\nlet nameMatch;\nwhile ((nameMatch = nameRegex.exec(html)) !== null) {\n  names.push(nameMatch[1]);\n}\n\n// Try to extract addresses\nconst addressRegex = /\\[\"(\\d+[^\"]{5,80}(?:St|Street|Ave|Avenue|Rd|Road|Dr|Drive|Blvd|Way|Lane|Ln|Ct|Pl)[^\"]{0,40})\"/gi;\nconst addresses = [];\nlet addrMatch;\nwhile ((addrMatch = addressRegex.exec(html)) !== null) {\n  addresses.push(addrMatch[1]);\n}\n\n// Try to extract phone numbers from Maps data\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst phones = [...new Set((html.match(phoneRegex) || []))];\n\n// Try to extract ratings\nconst ratingRegex = /\\[(\\d\\.\\d),\"\\d+ review/g;\nconst ratings = [];\nlet rateMatch;\nwhile ((rateMatch = ratingRegex.exec(html)) !== null) {\n  ratings.push(parseFloat(rateMatch[1]));\n}\n\n// Build business objects - match URLs with any extracted metadata\nconst maxResults = parseInt(config.maxResults) || 50;\nconst businesses = validUrls.slice(0, maxResults).map((url, i) => ({\n  businessName: names[i] || '',\n  website: url.split('/').slice(0, 3).join('/'),\n  address: addresses[i] || '',\n  phone: phones[i] || '',\n  rating: ratings[i] || 0,\n  industry: config.searchQuery,\n  location: config.location\n}));\n\nif (businesses.length === 0) {\n  return [{ json: { error: 'No businesses found', urlCount: allUrls.length } }];\n}\n\nreturn businesses.map(b => ({ json: b }));"
      },
      "id": "0de6edb7-d8c9-4d8d-a520-792acfe09704",
      "name": "Extract Business Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1100,
        400
      ],
      "typeVersion": 2,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "78e62e38-359d-41c5-812d-08a65777e4ed",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "google"
            },
            {
              "id": "0f8aff4a-667a-408d-8020-e9de3e58e925",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "gstatic"
            },
            {
              "id": "bee50ccd-bedb-4eb5-a7f9-7edf95b5ff40",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "f05bb8c6-dd38-40b7-9ae1-441dd1956979",
      "name": "Filter Valid URLs",
      "type": "n8n-nodes-base.filter",
      "position": [
        1320,
        400
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "compareValue": "={{ $json.website }}",
        "options": {}
      },
      "id": "cba71d67-6d61-4bab-aab2-cef1454acb40",
      "name": "Remove Duplicate URLs",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        1520,
        400
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "46e3ad26-9572-438a-93ce-7cde4fd68622",
      "name": "Loop Over Businesses",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1720,
        400
      ],
      "typeVersion": 3
    },
    {
      "parameters": {
        "url": "={{ $json.website }}",
        "options": {
          "allowUnauthorizedCerts": true,
          "timeout": 10000,
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "headers": {
            "parameters": [
              {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
              }
            ]
          }
        }
      },
      "id": "c1ee4f05-acfc-47ca-ae40-9695f7c2402a",
      "name": "Scrape Website",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1920,
        600
      ],
      "typeVersion": 4.2,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "amount": 2
      },
      "id": "c8518cba-78ff-4450-b4bd-5f92ef46d4ad",
      "name": "Rate Limit Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        2140,
        600
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "jsCode": "const html = $input.first().json.data || $input.first().json.body || '';\nconst original = $('Loop Over Businesses').item.json;\n\n// Email extraction (exclude image extensions)\nconst emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.(?!jpeg|jpg|png|gif|webp|svg|css|js)[a-zA-Z]{2,}/g;\nconst rawEmails = html.match(emailRegex) || [];\nconst emails = [...new Set(rawEmails.map(e => e.toLowerCase()))];\n\n// Phone extraction\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst rawPhones = html.match(phoneRegex) || [];\nconst phones = [...new Set(rawPhones)].filter(p => p.replace(/\\D/g, '').length >= 7);\n\n// Social media extraction\nconst linkedinRegex = /https?:\\/\\/(?:www\\.)?linkedin\\.com\\/(?:company|in)\\/[^\\s\"'<>)]+/gi;\nconst facebookRegex = /https?:\\/\\/(?:www\\.)?facebook\\.com\\/[^\\s\"'<>)]+/gi;\nconst instagramRegex = /https?:\\/\\/(?:www\\.)?instagram\\.com\\/[^\\s\"'<>)]+/gi;\n\nconst linkedin = (html.match(linkedinRegex) || [])[0] || '';\nconst facebook = (html.match(facebookRegex) || [])[0] || '';\nconst instagram = (html.match(instagramRegex) || [])[0] || '';\n\n// Merge with original business data\nconst result = {\n  businessName: original.businessName || '',\n  website: original.website || '',\n  address: original.address || '',\n  phone: phones[0] || original.phone || '',\n  rating: original.rating || 0,\n  industry: original.industry || '',\n  location: original.location || '',\n  emails: emails,\n  linkedin: linkedin,\n  facebook: facebook,\n  instagram: instagram\n};\n\nreturn { json: result };"
      },
      "id": "32891ac9-3b4f-4267-a6c6-2ec5bd19b018",
      "name": "Extract Contact Info",
      "type": "n8n-nodes-base.code",
      "position": [
        2360,
        600
      ],
      "typeVersion": 2,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "070f22a6-f970-4efa-928e-3365f444cb1e",
              "operator": {
                "type": "array",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.emails }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "d63f5e84-70e9-429a-b5ea-dd81b9dd24a4",
      "name": "Filter Has Email",
      "type": "n8n-nodes-base.filter",
      "position": [
        1920,
        260
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "fieldToSplitOut": "emails",
        "options": {}
      },
      "id": "94b26752-5433-41a7-90c6-90f04b6973ba",
      "name": "Split Emails",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        2140,
        260
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst seen = new Set();\nconst results = [];\n\nfor (const item of items) {\n  const email = (item.json.emails || '').toString().toLowerCase().trim();\n  if (!email || seen.has(email)) continue;\n  seen.add(email);\n\n  // Calculate lead score (0-100)\n  let score = 0;\n  if (email) score += 20;\n  if (item.json.phone) score += 15;\n  if (item.json.businessName) score += 15;\n  if (item.json.address) score += 10;\n  if (item.json.rating > 0) score += 10;\n  if (item.json.linkedin || item.json.facebook || item.json.instagram) score += 10;\n  if (item.json.website) score += 10;\n  if (item.json.phone && email) score += 10; // bonus for multiple contact methods\n\n  results.push({\n    json: {\n      ...item.json,\n      email: email,\n      leadScore: score,\n      status: 'New',\n      source: 'Google Maps Scraper',\n      datescraped: new Date().toISOString().split('T')[0]\n    }\n  });\n}\n\nreturn results.length > 0 ? results : [{ json: { _empty: true } }];"
      },
      "id": "8245be47-2574-4582-b81d-aae0890e2173",
      "name": "Score Leads",
      "type": "n8n-nodes-base.code",
      "position": [
        2360,
        260
      ],
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "search",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "filterByFormula": "=({Email} = \"{{ $json.email }}\")"
      },
      "id": "006a844f-efec-4965-bd74-ac1c75f68b51",
      "name": "Check Airtable Exists",
      "type": "n8n-nodes-base.airtable",
      "position": [
        2580,
        260
      ],
      "typeVersion": 2.1,
      "alwaysOutputData": true,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b49bdf15-d3aa-4079-9324-3bef95556834",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "92a5aad5-3acd-473a-bf09-35e4271ea76e",
      "name": "Is New Lead?",
      "type": "n8n-nodes-base.if",
      "position": [
        2800,
        260
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "operation": "create",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "columns": {
          "value": {
            "Business Name": "={{ $('Score Leads').item.json.businessName }}",
            "Email": "={{ $('Score Leads').item.json.email }}",
            "Phone": "={{ $('Score Leads').item.json.phone }}",
            "Website": "={{ $('Score Leads').item.json.website }}",
            "Address": "={{ $('Score Leads').item.json.address }}",
            "Industry": "={{ $('Score Leads').item.json.industry }}",
            "Location": "={{ $('Score Leads').item.json.location }}",
            "Rating": "={{ $('Score Leads').item.json.rating }}",
            "Social - LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
            "Social - Facebook": "={{ $('Score Leads').item.json.facebook }}",
            "Social - Instagram": "={{ $('Score Leads').item.json.instagram }}",
            "Lead Score": "={{ $('Score Leads').item.json.leadScore }}",
            "Status": "New",
            "Source": "Google Maps Scraper",
            "Date Scraped": "={{ $('Score Leads').item.json.datescraped }}"
          },
          "schema": [
            {
              "id": "Business Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Business Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Website",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Industry",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Industry",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rating",
              "type": "number",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - LinkedIn",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - LinkedIn",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Facebook",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Facebook",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Instagram",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Instagram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Lead Score",
              "type": "number",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date Scraped",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date Scraped",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {}
      },
      "id": "83c36de1-20d9-4e4a-80bc-611c55076342",
      "name": "Create in Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        3040,
        160
      ],
      "typeVersion": 2.1,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "columns": {
          "value": {
            "Phone": "={{ $('Score Leads').item.json.phone }}",
            "Social - LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
            "Social - Facebook": "={{ $('Score Leads').item.json.facebook }}",
            "Social - Instagram": "={{ $('Score Leads').item.json.instagram }}",
            "Lead Score": "={{ $('Score Leads').item.json.leadScore }}"
          },
          "schema": [
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - LinkedIn",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - LinkedIn",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Facebook",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Facebook",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Social - Instagram",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Social - Instagram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Lead Score",
              "type": "number",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Lead Score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {}
      },
      "id": "dbe06f00-f18e-4785-8d4a-9e76bbede172",
      "name": "Update in Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        3040,
        380
      ],
      "typeVersion": 2.1,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94",
          "mode": "list",
          "cachedResultName": "LEAD GEN EMAILSCRAPER",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E9_OSvO6F37iG9wh_gaetPT3IzuwdeSNbomIPXzKu94/edit#gid=0"
        },
        "columns": {
          "value": {
            "Business Name": "={{ $('Score Leads').item.json.businessName }}",
            "Email": "={{ $('Score Leads').item.json.email }}",
            "Phone": "={{ $('Score Leads').item.json.phone }}",
            "Website": "={{ $('Score Leads').item.json.website }}",
            "Address": "={{ $('Score Leads').item.json.address }}",
            "Industry": "={{ $('Score Leads').item.json.industry }}",
            "Location": "={{ $('Score Leads').item.json.location }}",
            "Rating": "={{ $('Score Leads').item.json.rating }}",
            "LinkedIn": "={{ $('Score Leads').item.json.linkedin }}",
            "Facebook": "={{ $('Score Leads').item.json.facebook }}",
            "Instagram": "={{ $('Score Leads').item.json.instagram }}",
            "Lead Score": "={{ $('Score Leads').item.json.leadScore }}",
            "Status": "={{ $('Score Leads').item.json.status }}",
            "Date Scraped": "={{ $('Score Leads').item.json.datescraped }}"
          },
          "schema": [
            {
              "id": "Business Name",
              "type": "string",
              "display": true,
              "displayName": "Business Name"
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "displayName": "Email"
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "displayName": "Phone"
            },
            {
              "id": "Website",
              "type": "string",
              "display": true,
              "displayName": "Website"
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "displayName": "Address"
            },
            {
              "id": "Industry",
              "type": "string",
              "display": true,
              "displayName": "Industry"
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "displayName": "Location"
            },
            {
              "id": "Rating",
              "type": "string",
              "display": true,
              "displayName": "Rating"
            },
            {
              "id": "LinkedIn",
              "type": "string",
              "display": true,
              "displayName": "LinkedIn"
            },
            {
              "id": "Facebook",
              "type": "string",
              "display": true,
              "displayName": "Facebook"
            },
            {
              "id": "Instagram",
              "type": "string",
              "display": true,
              "displayName": "Instagram"
            },
            {
              "id": "Lead Score",
              "type": "string",
              "display": true,
              "displayName": "Lead Score"
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "displayName": "Status"
            },
            {
              "id": "Date Scraped",
              "type": "string",
              "display": true,
              "displayName": "Date Scraped"
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {
          "useAppend": true
        }
      },
      "id": "8ef8bdff-836f-4128-9e18-b345431d3dde",
      "name": "Append to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3280,
        260
      ],
      "typeVersion": 4.5,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c2682c88-5122-4418-8da2-97fe49badfca",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        },
        "options": {}
      },
      "id": "dc5f251d-e3c0-4e4a-8fc6-7e402e48bc1c",
      "name": "Filter New Leads",
      "type": "n8n-nodes-base.filter",
      "position": [
        3500,
        160
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "amount": 30
      },
      "id": "9886589d-0ff5-4076-b2ee-249a20d56186",
      "name": "Rate Limit Emails",
      "type": "n8n-nodes-base.wait",
      "position": [
        3700,
        160
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst leadData = $('Score Leads').item.json;\nconst config = $('Search Config').first().json;\n\n// Parse AI response - chainLlm returns text in 'text' field\nlet emailContent;\ntry {\n  const rawText = input.text || input.response || JSON.stringify(input);\n  const jsonMatch = rawText.match(/\\{[\\s\\S]*\\}/);\n  emailContent = JSON.parse(jsonMatch[0]);\n} catch (e) {\n  emailContent = {\n    subject: 'Partnership opportunity for ' + (leadData.businessName || 'your business'),\n    body: 'I noticed ' + (leadData.businessName || 'your business') + ' in ' + leadData.location + ' and wanted to reach out about a potential collaboration that could benefit your ' + leadData.industry + ' practice.',\n    cta_text: 'Would a brief 10-minute call this week work to explore this?'\n  };\n}\n\nconst htmlBody = '<div style=\"font-family:Segoe UI,Arial,sans-serif;max-width:600px;margin:0 auto;background:#fff;\">' +\n  '<div style=\"padding:30px 40px 20px;border-bottom:3px solid #FF6D5A;\">' +\n  '<h1 style=\"margin:0;font-size:22px;color:#1A1A2E;\">' + config.senderCompany + '</h1></div>' +\n  '<div style=\"padding:30px 40px;\">' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;\">' + emailContent.body + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;\">' + emailContent.cta_text + '</p>' +\n  '<p style=\"font-size:15px;line-height:1.6;color:#333;margin-top:24px;\">Best regards,<br>' +\n  '<strong>' + config.senderName + '</strong><br>' +\n  '<span style=\"color:#666;\">' + config.senderTitle + '</span><br>' +\n  '<span style=\"color:#666;\">' + config.senderCompany + '</span></p></div>' +\n  '<div style=\"padding:20px 40px;background:#f8f8f8;border-top:1px solid #eee;\">' +\n  '<p style=\"font-size:11px;color:#999;\">You received this because your business was listed publicly on Google Maps. Reply &quot;unsubscribe&quot; to be removed.</p></div></div>';\n\nreturn {\n  json: {\n    to: leadData.email,\n    subject: emailContent.subject,\n    htmlBody: htmlBody,\n    leadEmail: leadData.email,\n    businessName: leadData.businessName\n  }\n};"
      },
      "id": "a9e237e5-cf9a-46d0-94af-f4717e8de8f1",
      "name": "Format Email",
      "type": "n8n-nodes-base.code",
      "position": [
        4140,
        160
      ],
      "typeVersion": 2,
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "emailType": "html",
        "message": "={{ $json.htmlBody }}",
        "options": {}
      },
      "id": "ce1c44f4-fbbd-4937-b420-ac4c6971be48",
      "name": "Send Outreach Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4360,
        160
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzcZpiIZ6QPtJXT",
          "cachedResultName": "n8n Workflows"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tbludJQgwxtvcyo2Q",
          "cachedResultName": "Leads"
        },
        "columns": {
          "value": {
            "Status": "Email Sent",
            "Email Sent Date": "={{ new Date().toISOString().split('T')[0] }}",
            "Notes": "={{ $('Format Email').item.json.subject }}",
            "Email": "={{ .item.json.leadEmail }}"
          },
          "schema": [
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "displayName": "Email"
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "displayName": "Status"
            },
            {
              "id": "Email Sent Date",
              "type": "string",
              "display": true,
              "displayName": "Email Sent Date"
            },
            {
              "id": "Notes",
              "type": "string",
              "display": true,
              "displayName": "Notes"
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Email"
          ]
        },
        "options": {}
      },
      "id": "c670553e-beea-4a4e-a2c3-8b479ff572e8",
      "name": "Update Lead Status",
      "type": "n8n-nodes-base.airtable",
      "position": [
        4580,
        160
      ],
      "typeVersion": 2.1,
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = $('Score Leads').all();\nconst config = $('Search Config').first().json;\n\nconst totalLeads = items.filter(i => !i.json._empty).length;\nconst avgScore = totalLeads > 0\n  ? Math.round(items.reduce((sum, i) => sum + (i.json.leadScore || 0), 0) / totalLeads)\n  : 0;\n\nconst now = new Date().toLocaleString('en-ZA', { timeZone: 'Africa/Johannesburg' });\n\nreturn {\n  json: {\n    subject: `Lead Scraper Complete: ${totalLeads} leads found`,\n    body: [\n      `<h2>Lead Scraper Run Complete</h2>`,\n      `<p><strong>Date:</strong> ${now}</p>`,\n      `<p><strong>Search:</strong> ${config.searchQuery} in ${config.location}</p>`,\n      `<hr>`,\n      `<p><strong>Total Leads Found:</strong> ${totalLeads}</p>`,\n      `<p><strong>Average Lead Score:</strong> ${avgScore}/100</p>`,\n      `<hr>`,\n      `<p>Check your <a href=\"https://airtable.com/appzcZpiIZ6QPtJXT\">Airtable CRM</a> for details.</p>`\n    ].join('\\n')\n  }\n};"
      },
      "id": "1f7002bf-1734-4722-abe2-5562e549dbcb",
      "name": "Aggregate Results",
      "type": "n8n-nodes-base.code",
      "position": [
        3500,
        460
      ],
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "sendTo": "ian@anyvisionmedia.com",
        "subject": "={{ $json.subject }}",
        "emailType": "html",
        "message": "={{ $json.body }}",
        "options": {}
      },
      "id": "88766fe8-6c3c-48ec-aa0b-215ae375d327",
      "name": "Send Summary",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3720,
        460
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {},
      "id": "96b86d80-eaf5-492b-b698-82ffc461c3e7",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        200,
        740
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "sendTo": "ian@anyvisionmedia.com",
        "subject": "Lead Scraper ERROR - {{ $json.workflow.name }}",
        "emailType": "html",
        "message": "=<h2>Workflow Error Alert</h2>\n<p><strong>Workflow:</strong> {{ $json.workflow.name }}</p>\n<p><strong>Error:</strong> {{ $json.execution.error.message }}</p>\n<p><strong>Node:</strong> {{ $json.execution.lastNodeExecuted }}</p>\n<p><a href=\"{{ $json.execution.url }}\">View Execution</a></p>",
        "options": {}
      },
      "id": "2e26c609-1352-4d70-ab2c-e3ed8c930a2c",
      "name": "Error Notification",
      "type": "n8n-nodes-base.gmail",
      "position": [
        440,
        740
      ],
      "typeVersion": 2.1,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## STAGE 1: Triggers & Configuration\n\n**Schedule:** Runs weekly on Monday 9AM\n**Manual:** Click 'Test workflow' for ad-hoc runs\n**Config:** Set your search query, location, and sender details",
        "height": 140,
        "width": 340
      },
      "id": "sticky-1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        140
      ],
      "typeVersion": 1,
      "name": "Note 1"
    },
    {
      "parameters": {
        "content": "## STAGE 2: Google Maps Scraping\n\nScrapes Google Maps HTML for business listings.\nExtracts names, websites, addresses, phones, ratings.",
        "height": 120,
        "width": 340
      },
      "id": "sticky-2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        620,
        240
      ],
      "typeVersion": 1,
      "name": "Note 2"
    },
    {
      "parameters": {
        "content": "## STAGE 3: Website Scraping & Enrichment\n\nVisits each business website with rate limiting.\nExtracts emails, phone numbers, and social media links.",
        "height": 120,
        "width": 360
      },
      "id": "sticky-3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        520
      ],
      "typeVersion": 1,
      "name": "Note 3"
    },
    {
      "parameters": {
        "content": "## STAGE 4: Scoring & Dedup\n\nScores leads 0-100 based on data completeness.\nDeduplicates by email address.",
        "height": 100,
        "width": 340
      },
      "id": "sticky-4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1880,
        120
      ],
      "typeVersion": 1,
      "name": "Note 4"
    },
    {
      "parameters": {
        "content": "## STAGE 5: CRM Storage\n\n**Airtable** (primary): Creates new or updates existing records\n**Google Sheets** (mirror): Appends all leads for easy sharing",
        "height": 100,
        "width": 380
      },
      "id": "sticky-5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2540,
        80
      ],
      "typeVersion": 1,
      "name": "Note 5"
    },
    {
      "parameters": {
        "content": "## STAGE 6: AI Email Outreach\n\nAI generates personalized cold email per lead.\nSent via Gmail with branded HTML template.\nAirtable status updated to 'Email Sent'.",
        "height": 100,
        "width": 380
      },
      "id": "sticky-6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3460,
        40
      ],
      "typeVersion": 1,
      "name": "Note 6"
    },
    {
      "parameters": {
        "content": "## STAGE 7: Notifications\n\nSummary email after each run.\nError alerts on failures.",
        "height": 80,
        "width": 340
      },
      "id": "sticky-7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3460,
        380
      ],
      "typeVersion": 1,
      "name": "Note 7"
    },
    {
      "parameters": {
        "prompt": {
          "value": "={{ \"You are a professional business development writer. Write a short, personalized cold outreach email.\\n\\nINSTRUCTIONS:\\n1. Subject line (max 50 chars) specific to their business\\n2. Personalized opening referencing their industry\\n3. 2-3 sentences with clear value proposition\\n4. Low-commitment CTA\\n5. Under 150 words\\n6. Professional, friendly - NOT salesy\\n\\nOUTPUT FORMAT (JSON only, no markdown):\\n{\\\"subject\\\": \\\"...\\\", \\\"body\\\": \\\"...\\\", \\\"cta_text\\\": \\\"...\\\"}\\n\\nBusiness: {businessName}\\nIndustry: {industry}\\nLocation: {location}\\nWebsite: {website}\".replace('{businessName}', $json.businessName || ['Business Name'] || '').replace('{industry}', $json.industry || ['Industry'] || '').replace('{location}', $json.location || ['Location'] || '').replace('{website}', $json.website || ['Website'] || '') }}"
        },
        "hasOutputParser": false,
        "options": {}
      },
      "id": "190fe33f-b95d-49be-a55b-fbe14eb69f16",
      "name": "AI Generate Email",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        3920,
        160
      ],
      "typeVersion": 1.4
    },
    {
      "parameters": {
        "model": "anthropic/claude-sonnet-4-20250514",
        "options": {
          "maxTokens": 500,
          "temperature": 0.7
        }
      },
      "id": "e7d1cd87-6dc4-4c46-b171-4dc1f8bfdea1",
      "name": "OpenRouter Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3940,
        380
      ],
      "typeVersion": 1,
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Search Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Search Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Config": {
      "main": [
        [
          {
            "node": "Build Maps URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Maps URL": {
      "main": [
        [
          {
            "node": "Scrape Google Maps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Google Maps": {
      "main": [
        [
          {
            "node": "Extract Business Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Business Data": {
      "main": [
        [
          {
            "node": "Filter Valid URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Valid URLs": {
      "main": [
        [
          {
            "node": "Remove Duplicate URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicate URLs": {
      "main": [
        [
          {
            "node": "Loop Over Businesses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Businesses": {
      "main": [
        [
          {
            "node": "Filter Has Email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Scrape Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Website": {
      "main": [
        [
          {
            "node": "Rate Limit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Wait": {
      "main": [
        [
          {
            "node": "Extract Contact Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Contact Info": {
      "main": [
        [
          {
            "node": "Loop Over Businesses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Has Email": {
      "main": [
        [
          {
            "node": "Split Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Emails": {
      "main": [
        [
          {
            "node": "Score Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Score Leads": {
      "main": [
        [
          {
            "node": "Check Airtable Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Airtable Exists": {
      "main": [
        [
          {
            "node": "Is New Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is New Lead?": {
      "main": [
        [
          {
            "node": "Create in Airtable",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update in Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create in Airtable": {
      "main": [
        [
          {
            "node": "Append to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update in Airtable": {
      "main": [
        [
          {
            "node": "Append to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append to Sheets": {
      "main": [
        [
          {
            "node": "Filter New Leads",
            "type": "main",
            "index": 0
          },
          {
            "node": "Aggregate Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter New Leads": {
      "main": [
        [
          {
            "node": "Rate Limit Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Emails": {
      "main": [
        [
          {
            "node": "AI Generate Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Email": {
      "main": [
        [
          {
            "node": "Send Outreach Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Outreach Email": {
      "main": [
        [
          {
            "node": "Update Lead Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Results": {
      "main": [
        [
          {
            "node": "Send Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Error Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Generate Email": {
      "main": [
        [
          {
            "node": "Format Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Generate Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": {
    "node:Schedule Trigger": {
      "recurrenceRules": []
    }
  },
  "meta": {
    "templateId": "5385",
    "templateCredsSetupCompleted": true
  },
  "versionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
  "activeVersionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
  "versionCounter": 9,
  "triggerCount": 1,
  "shared": [
    {
      "updatedAt": "2026-01-20T02:37:46.218Z",
      "createdAt": "2026-01-20T02:37:46.218Z",
      "role": "workflow:owner",
      "workflowId": "uq4hnH0YHfhYOOzO",
      "projectId": "pcmgdUiECq7jnQCv",
      "project": {
        "updatedAt": "2025-11-18T07:15:09.271Z",
        "createdAt": "2025-04-12T06:49:26.305Z",
        "id": "pcmgdUiECq7jnQCv",
        "name": "Ian Immelman <ian@anyvisionmedia.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "creatorId": "74ac6501-35e4-401e-af85-5fe3fc463160",
        "projectRelations": [
          {
            "updatedAt": "2025-04-12T06:49:26.305Z",
            "createdAt": "2025-04-12T06:49:26.305Z",
            "userId": "74ac6501-35e4-401e-af85-5fe3fc463160",
            "projectId": "pcmgdUiECq7jnQCv",
            "user": {
              "updatedAt": "2026-02-11T02:31:29.000Z",
              "createdAt": "2025-04-12T06:49:24.842Z",
              "id": "74ac6501-35e4-401e-af85-5fe3fc463160",
              "email": "ian@anyvisionmedia.com",
              "firstName": "Ian",
              "lastName": "Immelman",
              "personalizationAnswers": null,
              "settings": {
                "userActivated": true,
                "easyAIWorkflowOnboarded": true,
                "userClaimedAiCredits": true,
                "firstSuccessfulWorkflowId": "u0bLAklw7ax799uj",
                "userActivatedAt": 1762780853271,
                "npsSurvey": {
                  "responded": true,
                  "lastShownAt": 1763368708549
                }
              },
              "disabled": false,
              "mfaEnabled": false,
              "lastActiveAt": "2026-02-10",
              "isPending": false
            }
          }
        ]
      }
    }
  ],
  "tags": [],
  "activeVersion": {
    "updatedAt": "2026-02-11T07:52:30.444Z",
    "createdAt": "2026-02-11T07:52:30.444Z",
    "versionId": "d62b4ce2-21b0-4bb9-a9c9-b0ee5051a348",
    "workflowId": "uq4hnH0YHfhYOOzO",
    "nodes": [
      {
        "parameters": {
          "rule": {
            "interval": [
              {
                "field": "weeks",
                "triggerAtDay": [
                  1
                ],
                "triggerAtHour": 9
              }
            ]
          }
        },
        "id": "8b09efbe-4322-4e7f-8512-7805878088f9",
        "name": "Schedule Trigger",
        "type": "n8n-nodes-base.scheduleTrigger",
        "position": [
          200,
          300
        ],
        "typeVersion": 1.2
      },
      {
        "parameters": {},
        "id": "d35df8b8-f968-443c-8b89-39a59c0c5b6c",
        "name": "Manual Trigger",
        "type": "n8n-nodes-base.manualTrigger",
        "position": [
          200,
          500
        ],
        "typeVersion": 1
      },
      {
        "parameters": {
          "mode": "manual",
          "duplicateItem": false,
          "assignments": {
            "assignments": [
              {
                "id": "84cf11db-2680-4463-a454-c699becb72ee",
                "name": "searchQuery",
                "value": "dentists",
                "type": "string"
              },
              {
                "id": "f08cdf9d-8546-4dd3-b7eb-b94bbef7c532",
                "name": "location",
                "value": "Johannesburg",
                "type": "string"
              },
              {
                "id": "9e4af1d2-7149-45af-b052-9ab642a6754e",
                "name": "maxResults",
                "value": "50",
                "type": "number"
              },
              {
                "id": "115ebffa-b1b5-499a-a3a6-e37a19f90c2f",
                "name": "senderName",
                "value": "Ian Immelman",
                "type": "string"
              },
              {
                "id": "6a950459-74af-480d-a378-940e0b505ba7",
                "name": "senderCompany",
                "value": "AnyVision Media",
                "type": "string"
              },
              {
                "id": "8e8b75f3-f674-46fe-9fb4-6560bd139785",
                "name": "senderTitle",
                "value": "Director",
                "type": "string"
              },
              {
                "id": "7de74b4e-fbf6-434d-8340-552d9d6f468c",
                "name": "senderEmail",
                "value": "ian@anyvisionmedia.com",
                "type": "string"
              }
            ]
          }
        },
        "id": "d9c299bf-1039-4bb6-8c83-9025d9f67d29",
        "name": "Search Config",
        "type": "n8n-nodes-base.set",
        "position": [
          440,
          400
        ],
        "typeVersion": 3.4
      },
      {
        "parameters": {
          "jsCode": "const config = $input.first().json;\nconst query = encodeURIComponent(config.location + ' ' + config.searchQuery);\nconst url = 'https://www.google.com/maps/search/' + query;\nreturn {\n  json: {\n    ...config,\n    mapsUrl: url\n  }\n};"
        },
        "id": "de1745b8-9a52-4853-88e0-f120b7628682",
        "name": "Build Maps URL",
        "type": "n8n-nodes-base.code",
        "position": [
          660,
          400
        ],
        "typeVersion": 2
      },
      {
        "parameters": {
          "url": "={{ $json.mapsUrl }}",
          "options": {
            "allowUnauthorizedCerts": true,
            "response": {
              "response": {
                "fullResponse": true
              }
            }
          }
        },
        "id": "0833296b-0d6b-4ce3-acf5-5f1cf4a7606f",
        "name": "Scrape Google Maps",
        "type": "n8n-nodes-base.httpRequest",
        "position": [
          880,
          400
        ],
        "typeVersion": 4.2,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "jsCode": "const html = $input.first().json.data || '';\nconst config = $('Search Config').first().json;\n\n// Extract website URLs from Google Maps HTML\nconst urlRegex = /https?:\\/\\/[^\\/\\s\"'>]+/g;\nconst allUrls = html.match(urlRegex) || [];\n\n// Filter out Google/system domains\nconst blockedDomains = ['google', 'gstatic', 'googleapis', 'schema', 'ggpht', 'youtube', 'goo.gl'];\nconst validUrls = [...new Set(allUrls)].filter(url => {\n  const lower = url.toLowerCase();\n  return !blockedDomains.some(d => lower.includes(d));\n});\n\n// Try to extract business names from Maps HTML\n// Google Maps embeds business data in various patterns\nconst nameRegex = /\\[\"([^\"]{3,60})\"(?:,null){0,3},\"https?:\\/\\//g;\nconst names = [];\nlet nameMatch;\nwhile ((nameMatch = nameRegex.exec(html)) !== null) {\n  names.push(nameMatch[1]);\n}\n\n// Try to extract addresses\nconst addressRegex = /\\[\"(\\d+[^\"]{5,80}(?:St|Street|Ave|Avenue|Rd|Road|Dr|Drive|Blvd|Way|Lane|Ln|Ct|Pl)[^\"]{0,40})\"/gi;\nconst addresses = [];\nlet addrMatch;\nwhile ((addrMatch = addressRegex.exec(html)) !== null) {\n  addresses.push(addrMatch[1]);\n}\n\n// Try to extract phone numbers from Maps data\nconst phoneRegex = /(?:\\+\\d{1,3}[\\s.-]?)?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}/g;\nconst phones = [...new Set((html.match(phoneRegex) || []))];\n\n// Try to extract ratings\nconst ratingRegex = /\\[(\\d\\.\\d),\"\\d+ review/g;\nconst ratings = [];\nlet rateMatch;\nwhile ((rateMatch = ratingRegex.exec(html)) !== null) {\n  ratings.push(parseFloat(rateMatch[1]));\n}\n\n// Build business objects - match URLs with any extracted metadata\nconst maxResults = parseInt(config.maxResults) || 50;\nconst businesses = validUrls.slice(0, maxResults).map((url, i) => ({\n  businessName: names[i] || '',\n  website: url.split('/').slice(0, 3).join('/'),\n  address: addresses[i] || '',\n  phone: phones[i] || '',\n  rating: ratings[i] || 0,\n  industry: config.searchQuery,\n  location: config.location\n}));\n\nif (businesses.length === 0) {\n  return [{ json: { error: 'No businesses found', urlCount: allUrls.length } }];\n}\n\nreturn businesses.map(b => ({ json: b }));"
        },
        "id": "0de6edb7-d8c9-4d8d-a520-792acfe09704",
        "name": "Extract Business Data",
        "type": "n8n-nodes-base.code",
        "position": [
          1100,
          400
        ],
        "typeVersion": 2,
        "alwaysOutputData": true,
        "onError": "continueRegularOutput"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "version": 2,
              "leftValue": "",
              "caseSensitive": true,
              "typeValidation": "strict"
            },
            "combinator": "and",
            "conditions": [
              {
                "id": "78e62e38-359d-41c5-812d-08a65777e4ed",
                "operator": {
                  "type": "string",
                  "operation": "notContains"
                },
                "leftValue": "={{ $json.website }}",
                "rightValue": "google"
              },
              {
                "id": "0f8aff4a-667a-408d-8020-e9de3e58e925",
                "operator": {
                  "type": "string",
                  "operation": "notContains"
                },
                "leftValue": "={{ $json.website }}",
                "rightValue": "gstatic"
              },
              {
                "id": "bee50ccd-bedb-4eb5-a7f9-7edf95b5ff40",
                "operator": {
                  "type": "string",
                  "operation": "exists",
                  "singleValue": true
                },
                "leftValue": "={{ $json.website }}",
                "rightValue": ""
              }
            ]
          },
          "options": {}
        },
        "id": "f05bb8c6-dd38-40b7-9ae1-441dd1956979",
        "name": "Filter Valid URLs",
        "type": "n8n-nodes-base.filter",
        "position": [
          1320,
          400
        ],
        "typeVersion": 2.2
      },
      {
        "parameters": {
          "compareValue": "={{ $json.website }}",
          "options": {}
        },
        "id": "cba71d67-6d61-4bab-aab2-cef1454acb40",
        "name": "Remove Duplicate URLs",
        "type": "n8n-nodes-base.removeDuplicates",

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

Lead Generating Web Scraper & CRM Automation. Uses httpRequest, airtable, googleSheets, gmail. Scheduled trigger; 38 nodes.

Source: https://github.com/ianavm/n8n-ai-workflow-manager/blob/master/workflows/_archive/workflow_fixed.json — 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

Automates Singapore COE price tracking with AI forecasts and buy/wait recommendations. Weekly scraping collects LTA data, enriches with economic indicators, predicts 6-month trends, and alerts users v

HTTP Request, Google Sheets, Agent +4
Data & Sheets

Revenue operations teams, SaaS growth managers, and sales directors who need automated weekly insights from their Stripe payment data. Perfect for small to medium businesses tracking subscription reve

HTTP Request, Google Sheets, Google Gemini Chat +4
Data & Sheets

Automate your lead generation and outreach process seamlessly using AI, Gmail, and Google Sheets—all within n8n. No complicated setup—just import, activate, and start reaching prospects with personali

HTTP Request, Google Sheets, Gmail +3
Data & Sheets

This workflow automates tax compliance by aggregating multi-channel revenue data, calculating jurisdiction-specific tax obligations, detecting anomalies, and generating submission-ready reports for ta

Gmail, Google Sheets, Airtable +1
Data & Sheets

This n8n workflow automates the end-to-end client onboarding process: capturing client details, validating emails, assigning tiers, generating welcome packs, creating tasks, notifying teams, archiving

Google Sheets, Gmail, Airtable +5