This workflow corresponds to n8n.io template #16567 — we link there as the canonical source.
This workflow follows the Google Sheets → 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 →
{
"id": "AbjcSU4CyJzqjsbz",
"meta": null,
"name": "Google Maps Lead Enrichment Workflow",
"nodes": [
{
"id": "3c390cab-3648-4bca-bd94-80ecb2851703",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
0
],
"parameters": {
"width": 480,
"height": 768,
"content": "## Google Maps Lead Enrichment Workflow\n\n### How it works\n\nThis workflow manually launches a Google Maps lead scraping run in Apify, waits for the actor to finish, and then retrieves the resulting business records. It loops through each business, checks whether a website is available, optionally fetches the site HTML to extract an email address, and appends the enriched lead data to Google Sheets.\n\n### Setup steps\n\n- Add or verify the Apify API key used by the configuration node.\n- Set the search query, maximum result count, and target Google Sheets spreadsheet ID in the CONFIGURE HERE node.\n- Configure Google Sheets credentials for the Append to Google Sheets node and ensure the destination sheet has the expected columns.\n- Confirm the Apify actor endpoint and dataset/run ID expressions match the scraper actor being used.\n\n### Customization\n\nAdjust the search query, maxResults value, polling wait times, email extraction code, and Google Sheets column mapping to fit different lead sources or output formats."
},
"typeVersion": 1
},
{
"id": "743c8413-e8a2-42a0-af94-78941c0f5494",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
144
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Manual setup input\n\nStarts the workflow manually and defines the key runtime settings such as Apify API key, search query, result limit, and spreadsheet ID."
},
"typeVersion": 1
},
{
"id": "9d367535-d048-4a80-83a6-96cb4f17399d",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
128
],
"parameters": {
"color": 7,
"width": 432,
"height": 336,
"content": "## Start scraper run\n\nSends the initial request to Apify to launch the Google Maps scraping actor, then stores the returned run and dataset identifiers for later API calls."
},
"typeVersion": 1
},
{
"id": "cefa8ab1-13d3-4016-8f1a-f74198fff8cf",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
160
],
"parameters": {
"color": 7,
"width": 672,
"height": 496,
"content": "## Poll Apify completion\n\nWaits before checking the Apify actor run status, branches based on whether the run is finished, and loops through a retry wait until completion."
},
"typeVersion": 1
},
{
"id": "a94fc1a3-9483-401a-b3f7-7ae712511fbf",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1680,
160
],
"parameters": {
"color": 7,
"width": 672,
"height": 304,
"content": "## Retrieve business results\n\nDownloads completed dataset items from Apify, loops through each business record, and routes records based on whether they include a website URL."
},
"typeVersion": 1
},
{
"id": "9d0c1ba1-1f93-4367-ac65-90228d64adda",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2400,
0
],
"parameters": {
"color": 7,
"width": 672,
"height": 592,
"content": "## Enrich and save leads\n\nHandles both website and no-website branches: it fetches website HTML and extracts an email when possible, preserves basic data when no website exists, then appends the final lead record to Google Sheets."
},
"typeVersion": 1
},
{
"id": "trigger",
"name": "When Triggered Manually",
"type": "n8n-nodes-base.manualTrigger",
"position": [
50,
300
],
"parameters": {},
"typeVersion": 1
},
{
"id": "config",
"name": "Set API and Query Parameters",
"type": "n8n-nodes-base.set",
"position": [
280,
300
],
"parameters": {
"mode": "manual",
"assignments": {
"assignments": [
{
"id": "a1",
"name": "apifyApiKey",
"type": "string",
"value": "YOUR_APIFY_API_KEY_HERE"
},
{
"id": "a2",
"name": "query",
"type": "string",
"value": "plumbers in Madrid, Spain"
},
{
"id": "a3",
"name": "maxResults",
"type": "number",
"value": 20
},
{
"id": "a4",
"name": "spreadsheetId",
"type": "string",
"value": "YOUR_GOOGLE_SPREADSHEET_ID"
}
]
}
},
"typeVersion": 3
},
{
"id": "apifystart",
"name": "Initiate Apify Maps Scraper",
"type": "n8n-nodes-base.httpRequest",
"position": [
520,
300
],
"parameters": {
"url": "https://api.apify.com/v2/acts/compass~crawler-google-places/runs",
"method": "POST",
"jsonBody": "={{ JSON.stringify({ searchString: $('Set API and Query Parameters').first().json.query, maxCrawledPlaces: $('Set API and Query Parameters').first().json.maxResults, proxyConfig: { useApifyProxy: true } }) }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set API and Query Parameters').first().json.apifyApiKey }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "storerunid",
"name": "Save Run and Dataset IDs",
"type": "n8n-nodes-base.set",
"position": [
760,
300
],
"parameters": {
"mode": "manual",
"assignments": {
"assignments": [
{
"id": "r1",
"name": "runId",
"type": "string",
"value": "={{ $json.data.id }}"
},
{
"id": "r2",
"name": "datasetId",
"type": "string",
"value": "={{ $json.data.defaultDatasetId }}"
}
]
}
},
"typeVersion": 3
},
{
"id": "waitbefore",
"name": "Wait 15 Seconds",
"type": "n8n-nodes-base.wait",
"position": [
1000,
300
],
"parameters": {
"unit": "seconds",
"amount": 15
},
"typeVersion": 1.1
},
{
"id": "pollstatus",
"name": "Check Apify Run Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
1240,
300
],
"parameters": {
"url": "={{ 'https://api.apify.com/v2/actor-runs/' + $('Save Run and Dataset IDs').first().json.runId }}",
"method": "GET",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set API and Query Parameters').first().json.apifyApiKey }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "checkdone",
"name": "If Apify Run Complete",
"type": "n8n-nodes-base.if",
"position": [
1480,
300
],
"parameters": {
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.data.status }}",
"rightValue": "SUCCEEDED"
}
]
}
},
"typeVersion": 2
},
{
"id": "waitretry",
"name": "Wait and Retry Polling",
"type": "n8n-nodes-base.wait",
"position": [
1480,
500
],
"parameters": {
"unit": "seconds",
"amount": 10
},
"typeVersion": 1.1
},
{
"id": "getresults",
"name": "Fetch Dataset Results",
"type": "n8n-nodes-base.httpRequest",
"position": [
1720,
300
],
"parameters": {
"url": "={{ 'https://api.apify.com/v2/datasets/' + $('Save Run and Dataset IDs').first().json.datasetId + '/items?limit=100' }}",
"method": "GET",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set API and Query Parameters').first().json.apifyApiKey }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "splitresults",
"name": "Loop Over Business Results",
"type": "n8n-nodes-base.splitInBatches",
"position": [
1960,
300
],
"parameters": {
"options": {},
"batchSize": 1
},
"typeVersion": 3
},
{
"id": "haswebsite",
"name": "If Business Has Website",
"type": "n8n-nodes-base.if",
"position": [
2200,
300
],
"parameters": {
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "w1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.website != null && $json.website !== '' ? 'yes' : 'no' }}",
"rightValue": "yes"
}
]
}
},
"typeVersion": 2
},
{
"id": "fetchhtml",
"name": "Retrieve Website HTML",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
2440,
160
],
"parameters": {
"url": "={{ $json.website }}",
"method": "GET",
"options": {
"timeout": 8000,
"response": {
"response": {
"responseFormat": "text"
}
}
}
},
"typeVersion": 4.2
},
{
"id": "extractemail",
"name": "Extract Email from HTML",
"type": "n8n-nodes-base.code",
"position": [
2680,
160
],
"parameters": {
"jsCode": "const biz = $('Loop Over Business Results').first().json;\nconst inputJson = $input.first().json;\nconst html = (inputJson && !inputJson.error)\n ? (inputJson.body || inputJson.data || '')\n : '';\n\nconst emailRegex = /[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}/g;\nconst raw = typeof html === 'string' ? (html.match(emailRegex) || []) : [];\n\n// Filter noise: system addresses, placeholders, tracking pixels\nconst noisePattern = /^(noreply|no-reply|donotreply|do-not-reply|mailer-daemon|postmaster|privacy|legal|abuse|spam|unsubscribe|bounce|errors@|nombre@|name@|email@|user@|yourname@)/i;\nconst fakeDomains = /@(example|ejemplo|test|domain|yourdomain|sentry)\\.(com|org|net|es)$/i;\n\nconst filtered = raw.filter(e => !noisePattern.test(e) && !fakeDomains.test(e));\nconst unique = [...new Map(filtered.map(e => [e.toLowerCase(), e])).values()];\n\nreturn [{\n json: {\n title: biz.title || '',\n address: biz.address || '',\n phone: biz.phone || '',\n website: biz.website || '',\n email: unique.length > 0 ? unique[0] : '',\n rating: String(biz.totalScore || ''),\n reviewCount: String(biz.reviewsCount || '0'),\n emailFound: unique.length > 0\n }\n}];"
},
"typeVersion": 2
},
{
"id": "nowebsite",
"name": "Handle Missing Website",
"type": "n8n-nodes-base.set",
"position": [
2440,
440
],
"parameters": {
"mode": "manual",
"assignments": {
"assignments": [
{
"id": "n1",
"name": "title",
"type": "string",
"value": "={{ $json.title || '' }}"
},
{
"id": "n2",
"name": "address",
"type": "string",
"value": "={{ $json.address || '' }}"
},
{
"id": "n3",
"name": "phone",
"type": "string",
"value": "={{ $json.phone || '' }}"
},
{
"id": "n4",
"name": "website",
"type": "string",
"value": ""
},
{
"id": "n5",
"name": "email",
"type": "string",
"value": ""
},
{
"id": "n6",
"name": "rating",
"type": "string",
"value": "={{ String($json.totalScore || '') }}"
},
{
"id": "n7",
"name": "reviewCount",
"type": "string",
"value": "={{ String($json.reviewsCount || '0') }}"
},
{
"id": "n8",
"name": "emailFound",
"type": "boolean",
"value": false
}
]
}
},
"typeVersion": 3
},
{
"id": "appendsheets",
"name": "Add Data to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
2920,
300
],
"parameters": {
"columns": {
"value": {
"Name": "={{ $json.title }}",
"Email": "={{ $json.email }}",
"Phone": "={{ $json.phone }}",
"Rating": "={{ $json.rating }}",
"Address": "={{ $json.address }}",
"Reviews": "={{ $json.reviewCount }}",
"Website": "={{ $json.website }}"
},
"schema": [
{
"id": "Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Name",
"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": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Rating",
"type": "string",
"display": true,
"required": false,
"displayName": "Rating",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Reviews",
"type": "string",
"display": true,
"required": false,
"displayName": "Reviews",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set API and Query Parameters').first().json.spreadsheetId }}"
}
},
"typeVersion": 4
}
],
"active": false,
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"createdAt": "2026-06-22T12:03:34.568Z",
"updatedAt": "2026-06-22T12:03:34.568Z",
"versionId": "699b51d5-b8f0-4192-a56b-aa94b3f05301",
"isArchived": false,
"staticData": null,
"connections": {
"Wait 15 Seconds": {
"main": [
[
{
"node": "Check Apify Run Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch Dataset Results": {
"main": [
[
{
"node": "Loop Over Business Results",
"type": "main",
"index": 0
}
]
]
},
"If Apify Run Complete": {
"main": [
[
{
"node": "Fetch Dataset Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait and Retry Polling",
"type": "main",
"index": 0
}
]
]
},
"Retrieve Website HTML": {
"main": [
[
{
"node": "Extract Email from HTML",
"type": "main",
"index": 0
}
]
]
},
"Check Apify Run Status": {
"main": [
[
{
"node": "If Apify Run Complete",
"type": "main",
"index": 0
}
]
]
},
"Handle Missing Website": {
"main": [
[
{
"node": "Add Data to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Wait and Retry Polling": {
"main": [
[
{
"node": "Check Apify Run Status",
"type": "main",
"index": 0
}
]
]
},
"Extract Email from HTML": {
"main": [
[
{
"node": "Add Data to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"If Business Has Website": {
"main": [
[
{
"node": "Retrieve Website HTML",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle Missing Website",
"type": "main",
"index": 0
}
]
]
},
"When Triggered Manually": {
"main": [
[
{
"node": "Set API and Query Parameters",
"type": "main",
"index": 0
}
]
]
},
"Save Run and Dataset IDs": {
"main": [
[
{
"node": "Wait 15 Seconds",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Business Results": {
"main": [
[
{
"node": "If Business Has Website",
"type": "main",
"index": 0
}
]
]
},
"Initiate Apify Maps Scraper": {
"main": [
[
{
"node": "Save Run and Dataset IDs",
"type": "main",
"index": 0
}
]
]
},
"Set API and Query Parameters": {
"main": [
[
{
"node": "Initiate Apify Maps Scraper",
"type": "main",
"index": 0
}
]
]
}
},
"description": null,
"triggerCount": 0,
"versionCounter": 1,
"activeVersionId": null
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs on demand to scrape Google Maps business leads with Apify, optionally fetches each business website to extract an email address, and appends the enriched lead data to a Google Sheets spreadsheet. Starts manually and loads the Apify API key, Google Maps search…
Source: https://n8n.io/workflows/16567/ — 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.
04 - Firecrawl URL List Mini-Batch to Resilient Analyzer. Uses googleSheets, httpRequest, executeWorkflowTrigger. Event-driven trigger; 42 nodes.
Automate LinkedIn lead generation by scraping comments from targeted posts and enriching profiles with detailed data
This automated n8n workflow scrapes job listings from Upwork using Apify, processes and cleans the data, and generates daily email reports with job summaries. The system uses Google Sheets for data st
Transform LinkedIn profile URLs into comprehensive enriched lead profiles, quickly and automatically.
Transform any website into a structured knowledge repository with this intelligent crawler that extracts hyperlinks from the homepage, intelligently filters images and content pages, and aggregates fu