This workflow corresponds to n8n.io template #14448 — we link there as the canonical source.
This workflow follows the Gmail → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": false
},
"name": "Scrape Google Maps businesses and send personalized outreach via Gmail",
"nodes": [
{
"id": "ec09f944-511d-4778-b539-38f069cc4c99",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-400,
48
],
"parameters": {
"width": 480,
"height": 896,
"content": "## Scrape Google Maps businesses and send personalized outreach via Gmail\n\n### How it works\n\n1. The workflow is triggered manually and configured with search parameters (business type, location, max results, sender name).\n2. A Google Maps scrape job is submitted to ScraperCity and polled until complete, with a 60-second retry loop if still in progress.\n3. Results are downloaded, parsed, filtered for businesses with websites, and deduplicated by domain.\n4. Businesses are batched and submitted to ScraperCity's email-finder API, which is polled in a similar retry loop until complete.\n5. Email finder results are downloaded and merged back with the original business records, then filtered for valid emails.\n6. A personalised outreach email is sent via Gmail for each qualifying business, and the batch loop continues until all businesses are processed.\n\n### Setup steps\n\n- [ ] Create a ScraperCity account and obtain your API key at app.scrapercity.com\n- [ ] Add your ScraperCity API key to the HTTP Request nodes (Start Google Maps Scrape, Poll Maps Scrape Status, Download Maps Results, Start Email Finder Scrape, Poll Email Finder Status, Download Email Finder Results)\n- [ ] Connect your Gmail account to n8n via OAuth2 credential for the Send Outreach Email node\n- [ ] Update the Configure Search Parameters node with your desired businessType, locationQuery, maxResults, and senderName values\n- [ ] Adjust the batch size in the Batch Businesses for Email Finding node to match your ScraperCity plan limits\n- [ ] Customise the email body in the Send Outreach Email node with your personalised outreach message template\n\n### Customization\n\nAdjust wait durations in the Wait nodes to match ScraperCity's typical job completion times for your plan. Add a Google Sheets or database node after Send Outreach Email to log contacted businesses. Modify the filter conditions in Filter Businesses With a Website and Filter Records With Valid Email to tighten or relax lead quality criteria."
},
"typeVersion": 1
},
{
"id": "cb998ba2-ac02-47dc-8d60-ec670143bfcd",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
160,
144
],
"parameters": {
"color": 7,
"width": 448,
"height": 320,
"content": "## Workflow entry and config\n\nManual trigger that kicks off the workflow and a Set node that defines all search parameters used downstream."
},
"typeVersion": 1
},
{
"id": "e1ddd724-0e5b-468d-a4cf-4d8c0db65659",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
144
],
"parameters": {
"color": 7,
"width": 448,
"height": 320,
"content": "## Submit Maps scrape job\n\nSends the Google Maps scrape request to ScraperCity and stores the returned run ID along with context fields for use in polling."
},
"typeVersion": 1
},
{
"id": "5f4b7fe5-f49d-4e2b-a013-f5246deda668",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1200,
176
],
"parameters": {
"color": 7,
"width": 960,
"height": 768,
"content": "## Poll Maps scrape status\n\nWaits before the first poll, checks whether the Maps scrape job is finished, and retries every 60 seconds if it is not yet complete."
},
"typeVersion": 1
},
{
"id": "fbfab6ba-c69d-4d5c-8fa8-4d635d23c499",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2192,
144
],
"parameters": {
"color": 7,
"width": 464,
"height": 320,
"content": "## Download and parse Maps results\n\nDownloads the completed Maps scrape file and parses or cleans the CSV/JSON payload into structured business records."
},
"typeVersion": 1
},
{
"id": "3ac3eb0d-84d2-434d-81c2-a78212b554df",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2688,
144
],
"parameters": {
"color": 7,
"width": 448,
"height": 320,
"content": "## Filter and deduplicate leads\n\nRemoves businesses without a website and eliminates duplicate domains so only unique, contactable leads proceed."
},
"typeVersion": 1
},
{
"id": "e8b88816-49e8-4a07-b2d9-d91a131fa99b",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
3168,
144
],
"parameters": {
"color": 7,
"width": 464,
"height": 320,
"content": "## Batch and prepare email finder\n\nSplits leads into manageable batches and builds the contacts payload required by the ScraperCity email-finder API."
},
"typeVersion": 1
},
{
"id": "5c4e146a-ebed-4761-b384-cd2564e7d137",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
3664,
144
],
"parameters": {
"color": 7,
"width": 448,
"height": 320,
"content": "## Submit email finder job\n\nPosts the batch to the ScraperCity email-finder endpoint and stores the returned run ID for subsequent polling."
},
"typeVersion": 1
},
{
"id": "19e286ab-f9c7-464f-a1a0-d1e0ff5100b7",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
4144,
176
],
"parameters": {
"color": 7,
"width": 976,
"height": 768,
"content": "## Poll email finder status\n\nWaits before the first poll, checks whether the email-finder job is finished, and retries every 60 seconds if still pending."
},
"typeVersion": 1
},
{
"id": "68959445-82fb-4040-a47a-1cd809c8e096",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
5152,
176
],
"parameters": {
"color": 7,
"width": 720,
"height": 304,
"content": "## Download and merge email results\n\nDownloads the completed email-finder results, merges them with the original business records, and filters out any records lacking a valid email address."
},
"typeVersion": 1
},
{
"id": "0351ee36-094c-436a-935b-3f2d5652339f",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
5920,
48
],
"parameters": {
"color": 7,
"width": 240,
"height": 416,
"content": "## Send personalised outreach email\n\nSends a personalised Gmail outreach message to each qualifying business and loops back to the batch node to process the next batch."
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000001",
"name": "Start Workflow",
"type": "n8n-nodes-base.manualTrigger",
"position": [
208,
304
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000002",
"name": "Configure Search Parameters",
"type": "n8n-nodes-base.set",
"position": [
464,
304
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-001",
"name": "businessType",
"type": "string",
"value": "plumbers"
},
{
"id": "cfg-002",
"name": "locationQuery",
"type": "string",
"value": "Austin, TX"
},
{
"id": "cfg-003",
"name": "maxResults",
"type": "number",
"value": 50
},
{
"id": "cfg-004",
"name": "senderName",
"type": "string",
"value": "Your Name"
},
{
"id": "cfg-005",
"name": "senderEmail",
"type": "string",
"value": "user@example.com"
},
{
"id": "cfg-006",
"name": "emailSubject",
"type": "string",
"value": "Quick question about your business"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000003",
"name": "Start Google Maps Scrape",
"type": "n8n-nodes-base.httpRequest",
"position": [
720,
304
],
"parameters": {
"url": "https://app.scrapercity.com/api/v1/scrape/maps",
"method": "POST",
"options": {},
"jsonBody": "={\n \"searchStringsArray\": [\"{{ $json.businessType }}\"],\n \"locationQuery\": \"{{ $json.locationQuery }}\",\n \"maxCrawledPlacesPerSearch\": {{ $json.maxResults }}\n}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000004",
"name": "Store Maps Run ID",
"type": "n8n-nodes-base.set",
"position": [
976,
304
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "store-001",
"name": "mapsRunId",
"type": "string",
"value": "={{ $json.runId }}"
},
{
"id": "store-002",
"name": "businessType",
"type": "string",
"value": "={{ $('Configure Search Parameters').item.json.businessType }}"
},
{
"id": "store-003",
"name": "locationQuery",
"type": "string",
"value": "={{ $('Configure Search Parameters').item.json.locationQuery }}"
},
{
"id": "store-004",
"name": "senderName",
"type": "string",
"value": "={{ $('Configure Search Parameters').item.json.senderName }}"
},
{
"id": "store-005",
"name": "senderEmail",
"type": "string",
"value": "={{ $('Configure Search Parameters').item.json.senderEmail }}"
},
{
"id": "store-006",
"name": "emailSubject",
"type": "string",
"value": "={{ $('Configure Search Parameters').item.json.emailSubject }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000005",
"name": "Wait Before First Maps Poll",
"type": "n8n-nodes-base.wait",
"position": [
1248,
304
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000006",
"name": "Poll Maps Scrape Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
1504,
304
],
"parameters": {
"url": "=https://app.scrapercity.com/api/v1/scrape/status/{{ $('Store Maps Run ID').item.json.mapsRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000007",
"name": "Is Maps Scrape Complete?",
"type": "n8n-nodes-base.if",
"position": [
1760,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-maps-001",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "SUCCEEDED"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000008",
"name": "Wait 60s Before Retry Maps Poll",
"type": "n8n-nodes-base.wait",
"position": [
2016,
768
],
"parameters": {
"amount": 60
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000009",
"name": "Download Maps Results",
"type": "n8n-nodes-base.httpRequest",
"position": [
2240,
304
],
"parameters": {
"url": "=https://app.scrapercity.com/api/downloads/{{ $('Store Maps Run ID').item.json.mapsRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000010",
"name": "Parse and Clean Business Records",
"type": "n8n-nodes-base.code",
"position": [
2512,
304
],
"parameters": {
"jsCode": "// Parse CSV or JSON array returned by ScraperCity Maps download\n// The download endpoint returns a JSON array of business objects\nconst raw = $input.first().json;\n\nlet businesses = [];\n\n// Handle both array response and nested data\nif (Array.isArray(raw)) {\n businesses = raw;\n} else if (Array.isArray(raw.data)) {\n businesses = raw.data;\n} else if (typeof raw === 'string') {\n // Basic CSV parse -- split on newlines, first row is header\n const lines = raw.trim().split('\\n');\n const headers = lines[0].split(',').map(h => h.replace(/\"/g, '').trim());\n businesses = lines.slice(1).map(line => {\n const vals = line.split(',');\n const obj = {};\n headers.forEach((h, i) => {\n obj[h] = (vals[i] || '').replace(/\"/g, '').trim();\n });\n return obj;\n });\n}\n\n// Normalize fields and keep only records with a website\nconst cleaned = [];\nconst seen = new Set();\n\nfor (const biz of businesses) {\n const name = biz.name || biz.title || biz.businessName || '';\n const website = biz.website || biz.url || biz.websiteUrl || '';\n const phone = biz.phone || biz.phoneNumber || '';\n const address = biz.address || biz.fullAddress || '';\n const city = biz.city || '';\n\n if (!name || !website) continue;\n\n // Extract domain from website\n let domain = '';\n try {\n const url = new URL(website.startsWith('http') ? website : 'https://' + website);\n domain = url.hostname.replace(/^www\\./, '');\n } catch (e) {\n domain = website.replace(/^https?:\\/\\/(www\\.)?/, '').split('/')[0];\n }\n\n if (!domain || seen.has(domain)) continue;\n seen.add(domain);\n\n // Try to split name into first/last for email finder\n const nameParts = name.trim().split(' ');\n const firstName = nameParts[0] || 'Owner';\n const lastName = nameParts.slice(1).join(' ') || '';\n\n cleaned.push({\n json: {\n businessName: name,\n website,\n domain,\n phone,\n address,\n city,\n firstName,\n lastName\n }\n });\n}\n\nreturn cleaned;"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000011",
"name": "Filter Businesses With a Website",
"type": "n8n-nodes-base.filter",
"position": [
2736,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "filt-001",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.domain }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000012",
"name": "Remove Duplicate Domains",
"type": "n8n-nodes-base.removeDuplicates",
"position": [
2992,
304
],
"parameters": {
"compare": "selectedFields",
"options": {},
"fieldsToCompare": {
"fields": [
{
"fieldName": "domain"
}
]
}
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000013",
"name": "Batch Businesses for Email Finding",
"type": "n8n-nodes-base.splitInBatches",
"position": [
3216,
304
],
"parameters": {
"options": {},
"batchSize": 10
},
"typeVersion": 3
},
{
"id": "a1000000-0000-0000-0000-000000000014",
"name": "Prepare Email Finder Payload",
"type": "n8n-nodes-base.code",
"position": [
3488,
304
],
"parameters": {
"jsCode": "// Build contacts array for ScraperCity email-finder from current batch\nconst items = $input.all();\n\nconst contacts = items.map(item => ({\n first_name: item.json.firstName || 'Owner',\n last_name: item.json.lastName || '',\n domain: item.json.domain\n}));\n\n// Also keep original business data to merge back later\nreturn [{\n json: {\n contacts,\n originalItems: items.map(i => i.json)\n }\n}];"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000015",
"name": "Start Email Finder Scrape",
"type": "n8n-nodes-base.httpRequest",
"position": [
3712,
304
],
"parameters": {
"url": "https://app.scrapercity.com/api/v1/scrape/email-finder",
"method": "POST",
"options": {},
"jsonBody": "={\n \"contacts\": {{ JSON.stringify($json.contacts) }}\n}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000016",
"name": "Store Email Finder Run ID",
"type": "n8n-nodes-base.set",
"position": [
3968,
304
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "store-ef-001",
"name": "emailRunId",
"type": "string",
"value": "={{ $json.runId }}"
},
{
"id": "store-ef-002",
"name": "originalItems",
"type": "string",
"value": "={{ JSON.stringify($('Prepare Email Finder Payload').item.json.originalItems) }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000017",
"name": "Wait Before First Email Poll",
"type": "n8n-nodes-base.wait",
"position": [
4192,
304
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000018",
"name": "Poll Email Finder Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
4464,
304
],
"parameters": {
"url": "=https://app.scrapercity.com/api/v1/scrape/status/{{ $('Store Email Finder Run ID').item.json.emailRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000019",
"name": "Is Email Finder Complete?",
"type": "n8n-nodes-base.if",
"position": [
4720,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-ef-001",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "SUCCEEDED"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000020",
"name": "Wait 60s Before Retry Email Poll",
"type": "n8n-nodes-base.wait",
"position": [
4976,
768
],
"parameters": {
"amount": 60
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000021",
"name": "Download Email Finder Results",
"type": "n8n-nodes-base.httpRequest",
"position": [
5200,
304
],
"parameters": {
"url": "=https://app.scrapercity.com/api/downloads/{{ $('Store Email Finder Run ID').item.json.emailRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000022",
"name": "Merge Emails With Business Data",
"type": "n8n-nodes-base.code",
"position": [
5456,
304
],
"parameters": {
"jsCode": "// Merge email finder results back with original business records\nconst emailResults = $input.first().json;\nconst originalItems = JSON.parse($('Store Email Finder Run ID').item.json.originalItems || '[]');\n\nlet emailList = [];\n\nif (Array.isArray(emailResults)) {\n emailList = emailResults;\n} else if (Array.isArray(emailResults.data)) {\n emailList = emailResults.data;\n} else if (Array.isArray(emailResults.contacts)) {\n emailList = emailResults.contacts;\n}\n\n// Build a lookup map by domain\nconst emailByDomain = {};\nfor (const record of emailList) {\n const domain = record.domain || record.company_domain || '';\n const email = record.email || record.emailAddress || '';\n if (domain && email) {\n emailByDomain[domain] = email;\n }\n}\n\n// Merge\nconst merged = [];\nfor (const biz of originalItems) {\n const email = emailByDomain[biz.domain] || '';\n if (!email) continue; // skip businesses where no email was found\n merged.push({\n json: {\n ...biz,\n email\n }\n });\n}\n\nreturn merged;"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000023",
"name": "Filter Records With Valid Email",
"type": "n8n-nodes-base.filter",
"position": [
5728,
304
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "filt-email-001",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.email }}",
"rightValue": "@"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000024",
"name": "Send Outreach Email",
"type": "n8n-nodes-base.gmail",
"position": [
5968,
304
],
"parameters": {
"sendTo": "={{ $json.email }}",
"message": "=Hi {{ $json.firstName }},\n\nI came across {{ $json.businessName }} while looking at {{ $json.businessType || 'businesses' }} in {{ $json.city || $('Configure Search Parameters').item.json.locationQuery }} and wanted to reach out.\n\nI work with local businesses like yours and think I could help you get more clients. Would you be open to a quick 15-minute call this week to explore if there's a fit?\n\nLooking forward to hearing from you.\n\nBest regards,\n{{ $('Configure Search Parameters').item.json.senderName }}\n{{ $('Configure Search Parameters').item.json.senderEmail }}",
"options": {
"appendAttribution": false
},
"subject": "={{ $('Configure Search Parameters').item.json.emailSubject }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"Start Workflow": {
"main": [
[
{
"node": "Configure Search Parameters",
"type": "main",
"index": 0
}
]
]
},
"Store Maps Run ID": {
"main": [
[
{
"node": "Wait Before First Maps Poll",
"type": "main",
"index": 0
}
]
]
},
"Send Outreach Email": {
"main": [
[
{
"node": "Batch Businesses for Email Finding",
"type": "main",
"index": 0
}
]
]
},
"Download Maps Results": {
"main": [
[
{
"node": "Parse and Clean Business Records",
"type": "main",
"index": 0
}
]
]
},
"Poll Maps Scrape Status": {
"main": [
[
{
"node": "Is Maps Scrape Complete?",
"type": "main",
"index": 0
}
]
]
},
"Is Maps Scrape Complete?": {
"main": [
[
{
"node": "Download Maps Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 60s Before Retry Maps Poll",
"type": "main",
"index": 0
}
]
]
},
"Poll Email Finder Status": {
"main": [
[
{
"node": "Is Email Finder Complete?",
"type": "main",
"index": 0
}
]
]
},
"Remove Duplicate Domains": {
"main": [
[
{
"node": "Batch Businesses for Email Finding",
"type": "main",
"index": 0
}
]
]
},
"Start Google Maps Scrape": {
"main": [
[
{
"node": "Store Maps Run ID",
"type": "main",
"index": 0
}
]
]
},
"Is Email Finder Complete?": {
"main": [
[
{
"node": "Download Email Finder Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 60s Before Retry Email Poll",
"type": "main",
"index": 0
}
]
]
},
"Start Email Finder Scrape": {
"main": [
[
{
"node": "Store Email Finder Run ID",
"type": "main",
"index": 0
}
]
]
},
"Store Email Finder Run ID": {
"main": [
[
{
"node": "Wait Before First Email Poll",
"type": "main",
"index": 0
}
]
]
},
"Configure Search Parameters": {
"main": [
[
{
"node": "Start Google Maps Scrape",
"type": "main",
"index": 0
}
]
]
},
"Wait Before First Maps Poll": {
"main": [
[
{
"node": "Poll Maps Scrape Status",
"type": "main",
"index": 0
}
]
]
},
"Prepare Email Finder Payload": {
"main": [
[
{
"node": "Start Email Finder Scrape",
"type": "main",
"index": 0
}
]
]
},
"Wait Before First Email Poll": {
"main": [
[
{
"node": "Poll Email Finder Status",
"type": "main",
"index": 0
}
]
]
},
"Download Email Finder Results": {
"main": [
[
{
"node": "Merge Emails With Business Data",
"type": "main",
"index": 0
}
]
]
},
"Filter Records With Valid Email": {
"main": [
[
{
"node": "Send Outreach Email",
"type": "main",
"index": 0
}
]
]
},
"Merge Emails With Business Data": {
"main": [
[
{
"node": "Filter Records With Valid Email",
"type": "main",
"index": 0
}
]
]
},
"Wait 60s Before Retry Maps Poll": {
"main": [
[
{
"node": "Poll Maps Scrape Status",
"type": "main",
"index": 0
}
]
]
},
"Filter Businesses With a Website": {
"main": [
[
{
"node": "Remove Duplicate Domains",
"type": "main",
"index": 0
}
]
]
},
"Parse and Clean Business Records": {
"main": [
[
{
"node": "Filter Businesses With a Website",
"type": "main",
"index": 0
}
]
]
},
"Wait 60s Before Retry Email Poll": {
"main": [
[
{
"node": "Poll Email Finder Status",
"type": "main",
"index": 0
}
]
]
},
"Batch Businesses for Email Finding": {
"main": [
[
{
"node": "Prepare Email Finder Payload",
"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.
gmailOAuth2httpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow is built for agencies, freelancers, and local service businesses who want to automate prospecting. If you sell services to local businesses -- web design, SEO, bookkeeping, HVAC -- this workflow finds your prospects and reaches out for you. A manual trigger kicks…
Source: https://n8n.io/workflows/14448/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow is a powerful, two-phase system designed to automate the entire passive candidate sourcing and engagement cycle.
How It Works Starts with a Manual Trigger Reads lead list from Google Sheet Filter rows where email wasn’t sent Generate personalized email body (AI) Generate email subject line (AI) Merge AI outputs
Hunter Form. Uses stickyNote, formTrigger, noOp, httpRequest. Event-driven trigger; 15 nodes.
Hunter Form. Uses formTrigger, stickyNote, noOp, httpRequest. Event-driven trigger; 12 nodes.
Monitor-Emails. Uses gmailTrigger, gmail, supabase, httpRequest. Event-driven trigger; 12 nodes.