This workflow corresponds to n8n.io template #15960 — we link there as the canonical source.
This workflow follows the Form Trigger → Google Sheets recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "XISpgVifGchaNB1g",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Google Maps Leads - Fully Dynamic City Grid Scraper",
"tags": [],
"nodes": [
{
"id": "fba8f6eb-dbe1-48b5-a41d-bd7152ecaf08",
"name": "On form submission",
"type": "n8n-nodes-base.formTrigger",
"position": [
4480,
1696
],
"parameters": {
"options": {},
"formTitle": "Dynamic Business Leads Generator",
"formFields": {
"values": [
{
"fieldLabel": "What is the Search Term?",
"requiredField": true
},
{
"fieldLabel": "What City / Location?",
"placeholder": "e.g., Amman, Dubai, London",
"requiredField": true
},
{
"fieldType": "number",
"fieldLabel": "Max Results?",
"requiredField": true
},
{
"fieldLabel": "Google Maps API Key",
"requiredField": true
}
]
}
},
"typeVersion": 2.5
},
{
"id": "45d586e8-82b9-429d-9bba-564fafa252ce",
"name": "Set Initial Parameters",
"type": "n8n-nodes-base.set",
"position": [
4672,
1696
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "assign_api",
"name": "api_key",
"type": "string",
"value": "={{ $json['Google Maps API Key'] }}"
},
{
"id": "assign_keyword",
"name": "keyword",
"type": "string",
"value": "={{ $json['What is the Search Term?'] }}"
},
{
"id": "assign_city",
"name": "targetCity",
"type": "string",
"value": "={{ $json['What City / Location?'] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "9945f6d5-159a-4d82-aa5f-fafe6e5c6f7a",
"name": "Geocode Target City",
"type": "n8n-nodes-base.httpRequest",
"position": [
4880,
1696
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/geocode/json",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "address",
"value": "={{ $json.targetCity }}"
},
{
"name": "key",
"value": "={{ $json.api_key }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "870fc807-fe7d-475d-a29b-2bb31584abc7",
"name": "Generate Dynamic Grid",
"type": "n8n-nodes-base.set",
"position": [
5088,
1696
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "grid_calc",
"name": "locations",
"type": "array",
"value": "={{ \n (function() {\n const lat = parseFloat($json.results[0].geometry.location.lat);\n const lng = parseFloat($json.results[0].geometry.location.lng);\n \n const offset = 0.025;\n \n return [\n `${lat},${lng}`,\n `${(lat + offset).toFixed(4)},${(lng - offset).toFixed(4)}`,\n `${(lat - offset).toFixed(4)},${(lng + offset).toFixed(4)}`,\n `${(lat + offset).toFixed(4)},${(lng + offset).toFixed(4)}` \n ];\n })()\n}}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "85d994f0-cf12-4d35-b465-7eafb1eed257",
"name": "Split Locations",
"type": "n8n-nodes-base.splitOut",
"position": [
5280,
1696
],
"parameters": {
"options": {},
"fieldToSplitOut": "locations"
},
"typeVersion": 1
},
{
"id": "7c1247cc-f45c-4eb7-af1e-8cdf215f337e",
"name": "Search Page 1",
"type": "n8n-nodes-base.httpRequest",
"position": [
4480,
2016
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/nearbysearch/json",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "location",
"value": "={{ $json.locations }}"
},
{
"name": "radius",
"value": "5000"
},
{
"name": "keyword",
"value": "={{ $('Set Initial Parameters').item.json.keyword }}"
},
{
"name": "key",
"value": "={{ $('Set Initial Parameters').item.json.api_key }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "11e45d28-0a07-424d-b821-76b3969189ff",
"name": "Save Page 1",
"type": "n8n-nodes-base.set",
"position": [
4672,
2016
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "p1_res",
"name": "page1_results",
"type": "array",
"value": "={{ $json.results }}"
},
{
"id": "p1_tok",
"name": "next_page_token_1",
"type": "string",
"value": "={{ $json.next_page_token ?? '' }}"
},
{
"id": "p1_key",
"name": "api_key",
"type": "string",
"value": "={{ $('Set Initial Parameters').item.json.api_key }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2c527621-b1e5-4590-8eab-5a40d59289f6",
"name": "Wait for Page 2",
"type": "n8n-nodes-base.wait",
"position": [
4880,
2016
],
"parameters": {
"amount": 2
},
"typeVersion": 1.1
},
{
"id": "e7d8bca5-1e44-4765-9c0c-6c01207583ef",
"name": "Search Page 2",
"type": "n8n-nodes-base.httpRequest",
"position": [
5088,
2016
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/nearbysearch/json",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "pagetoken",
"value": "={{ $json.next_page_token_1 }}"
},
{
"name": "key",
"value": "={{ $json.api_key }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "f40fa975-221a-4e18-9a56-e8b3414f003a",
"name": "Save Page 2",
"type": "n8n-nodes-base.set",
"position": [
5280,
2016
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "p2_p1",
"name": "page1_results",
"type": "array",
"value": "={{ $('Save Page 1').item.json.page1_results }}"
},
{
"id": "p2_res",
"name": "page2_results",
"type": "array",
"value": "={{ $json.results }}"
},
{
"id": "p2_tok",
"name": "next_page_token_2",
"type": "string",
"value": "={{ $json.next_page_token ?? '' }}"
},
{
"id": "p2_key",
"name": "api_key",
"type": "string",
"value": "={{ $('Save Page 1').item.json.api_key }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f0b1816e-e670-4de4-b428-481fa4fe60fe",
"name": "Wait for Page 3",
"type": "n8n-nodes-base.wait",
"position": [
5472,
2016
],
"parameters": {
"amount": 2
},
"typeVersion": 1.1
},
{
"id": "36a2cf89-3901-40a2-a228-a2adb6193b74",
"name": "Search Page 3",
"type": "n8n-nodes-base.httpRequest",
"position": [
5664,
2016
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/nearbysearch/json",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "pagetoken",
"value": "={{ $json.next_page_token_2 }}"
},
{
"name": "key",
"value": "={{ $json.api_key }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "2dba8fb8-2271-46df-b63e-afe26d8ffbe0",
"name": "Merge All Pages",
"type": "n8n-nodes-base.set",
"position": [
5856,
2016
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "merge_all",
"name": "all_results",
"type": "array",
"value": "={{ [...($('Save Page 2').item.json.page1_results || []), ...($('Save Page 2').item.json.page2_results || []), ...($json.results || [])] }}"
},
{
"id": "merge_key",
"name": "api_key",
"type": "string",
"value": "={{ $('Save Page 2').item.json.api_key }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "c5764931-1ed8-4a41-b734-58ff675ebc42",
"name": "Split All Results",
"type": "n8n-nodes-base.splitOut",
"position": [
4480,
2368
],
"parameters": {
"options": {},
"fieldToSplitOut": "all_results"
},
"typeVersion": 1
},
{
"id": "2b10909d-2b81-4236-9b45-273ae0e555b2",
"name": "Collect All Place IDs",
"type": "n8n-nodes-base.aggregate",
"position": [
4672,
2368
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData",
"destinationFieldName": "places"
},
"typeVersion": 1
},
{
"id": "e8aef02e-a3ff-40fa-ad31-2cdc32a58526",
"name": "Split Places",
"type": "n8n-nodes-base.splitOut",
"position": [
4880,
2368
],
"parameters": {
"options": {},
"fieldToSplitOut": "places"
},
"typeVersion": 1
},
{
"id": "37a2b718-0a34-44bd-8fda-955c067e45d9",
"name": "Loop Over Places",
"type": "n8n-nodes-base.splitInBatches",
"position": [
5088,
2368
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "7b0397e3-2ff5-40c3-9119-aee2d2455b19",
"name": "Get Place Details",
"type": "n8n-nodes-base.httpRequest",
"position": [
5280,
2448
],
"parameters": {
"url": "https://maps.googleapis.com/maps/api/place/details/json",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "place_id",
"value": "={{ $json.place_id }}"
},
{
"name": "fields",
"value": "name,formatted_address,formatted_phone_number,international_phone_number,website,rating,user_ratings_total,types,business_status"
},
{
"name": "key",
"value": "={{ $('Set Initial Parameters').first().json.api_key }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "44c99c26-377e-49ab-a91c-a2988a470a76",
"name": "Map Fields",
"type": "n8n-nodes-base.set",
"position": [
5472,
2448
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "map_name",
"name": "Name",
"type": "string",
"value": "={{ $json.result.name ?? '' }}"
},
{
"id": "map_address",
"name": "Address",
"type": "string",
"value": "={{ $json.result.formatted_address ?? '' }}"
},
{
"id": "map_phone",
"name": "Phone",
"type": "string",
"value": "={{ $json.result.formatted_phone_number ?? '' }}"
},
{
"id": "map_int_phone",
"name": "International Phone",
"type": "string",
"value": "={{ $json.result.international_phone_number ?? '' }}"
},
{
"id": "map_web",
"name": "Website",
"type": "string",
"value": "={{ $json.result.website ?? '' }}"
},
{
"id": "map_rating",
"name": "Rating",
"type": "string",
"value": "={{ $json.result.rating ?? '' }}"
},
{
"id": "map_reviews",
"name": "Total Reviews",
"type": "string",
"value": "={{ $json.result.user_ratings_total ?? '' }}"
},
{
"id": "map_status",
"name": "Business Status",
"type": "string",
"value": "={{ $json.result.business_status ?? '' }}"
},
{
"id": "map_types",
"name": "Types",
"type": "string",
"value": "={{ ($json.result.types ?? []).join(', ') }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "9c5b9e9c-d910-49cf-b502-96e2e5998fd6",
"name": "Append row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
5280,
2272
],
"parameters": {
"columns": {
"value": {
"Website": "={{ $json.Website }}",
"Category": "={{ $('Set Initial Parameters').item.json.keyword }}",
"Full Number": "={{ $json['International Phone'] }}",
"Company Name": "={{ $json.Name }}",
"Phone Number": "={{ $json.Phone }}",
"Company Address": "={{ $json.Address }}"
},
"schema": [
{
"id": "Company Name",
"type": "string",
"displayName": "Company Name"
},
{
"id": "Company Address",
"type": "string",
"displayName": "Company Address"
},
{
"id": "Category",
"type": "string",
"displayName": "Category"
},
{
"id": "Website",
"type": "string",
"displayName": "Website"
},
{
"id": "Phone Number",
"type": "string",
"displayName": "Phone Number"
},
{
"id": "Full Number",
"type": "string",
"displayName": "Full Number"
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1c7q0UWW6Y1rbqNsrXEColIMqaykJYV8Be6a9TcDuIL4/edit#gid=0",
"cachedResultName": "Dump"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1c7q0UWW6Y1rbqNsrXEColIMqaykJYV8Be6a9TcDuIL4",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1c7q0UWW6Y1rbqNsrXEColIMqaykJYV8Be6a9TcDuIL4/edit?usp=drivesdk",
"cachedResultName": "Telesales Master Sheet "
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "d85eae39-7fbf-4fa6-bf3f-0cf86a11a127",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
3952,
1616
],
"parameters": {
"width": 400,
"height": 672,
"content": "## Automate business lead generation with a dynamic city grid scraper\nThis workflow helps sales teams and lead generation specialists automatically extract targeted business details from Google Maps without manual searching. It uses a dynamic geographic grid system to bypass standard API result limitations, gathers rich contact information, and streams leads automatically into a master spreadsheet.\n \n**How it works**\n**Initialize search parameters** The workflow triggers via a user-facing form, capturing the target business type, city, and API key.\n\n**Generate dynamic geographic** grid The target city is geocoded into precise coordinates. A custom script automatically builds a 4-point overlapping coordinate grid to maximize search coverage.\n\n**Deep 3-page paginated search** The workflow loops through each grid point and executes up to 3 consecutive pages of deep searches, respecting mandatory API delays to extract every available lead.\n\n**Extract rich profile details** It isolates unique Place IDs to avoid duplicates, then automatically fetches advanced fields like websites, formatted phone numbers, ratings, and business status.\n\n**Stream directly to sheet** All sanitized data is mapped perfectly and streamed row-by-row into a master Google Sheet in real time."
},
"typeVersion": 1
},
{
"id": "26567ebe-1e07-40aa-af45-e121983394d5",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
3952,
2304
],
"parameters": {
"width": 400,
"height": 288,
"content": "**Setup steps**\n**Connect Google Sheets** and select your master telesales spreadsheet and target tab.\n\nEnsure your **Google Cloud Console project** has the Geocoding API and Places API enabled.\n\n**Generate a Google Maps API key** to use in the form input.\n\n**Run a test execution** to generate the unique form URL for daily use.\n\n**(Optional)** Customize the geographic coordinate offset inside the grid calculator node to expand or narrow the search area."
},
"typeVersion": 1
},
{
"id": "a01fd9e4-39c0-4ea2-a87b-e264c602b267",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
4432,
1632
],
"parameters": {
"color": 4,
"width": 1024,
"height": 256,
"content": "## Input & File Handling ##"
},
"typeVersion": 1
},
{
"id": "ee4b2f4b-bb11-4149-b7c6-23c9c39d435e",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
4432,
1936
],
"parameters": {
"color": 3,
"width": 1600,
"height": 272,
"content": "## Deep Search & Pagination ##"
},
"typeVersion": 1
},
{
"id": "12ed1553-5807-4321-b801-0aa37f70ec5d",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
4432,
2256
],
"parameters": {
"color": 6,
"width": 1216,
"height": 384,
"content": "## Extraction & Delivery ##"
},
"typeVersion": 1
}
],
"active": true,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "a8b85d8a-af27-43eb-802d-d6be9a522ede",
"connections": {
"Map Fields": {
"main": [
[
{
"node": "Loop Over Places",
"type": "main",
"index": 0
}
]
]
},
"Save Page 1": {
"main": [
[
{
"node": "Wait for Page 2",
"type": "main",
"index": 0
}
]
]
},
"Save Page 2": {
"main": [
[
{
"node": "Wait for Page 3",
"type": "main",
"index": 0
}
]
]
},
"Split Places": {
"main": [
[
{
"node": "Loop Over Places",
"type": "main",
"index": 0
}
]
]
},
"Search Page 1": {
"main": [
[
{
"node": "Save Page 1",
"type": "main",
"index": 0
}
]
]
},
"Search Page 2": {
"main": [
[
{
"node": "Save Page 2",
"type": "main",
"index": 0
}
]
]
},
"Search Page 3": {
"main": [
[
{
"node": "Merge All Pages",
"type": "main",
"index": 0
}
]
]
},
"Merge All Pages": {
"main": [
[
{
"node": "Split All Results",
"type": "main",
"index": 0
}
]
]
},
"Split Locations": {
"main": [
[
{
"node": "Search Page 1",
"type": "main",
"index": 0
}
]
]
},
"Wait for Page 2": {
"main": [
[
{
"node": "Search Page 2",
"type": "main",
"index": 0
}
]
]
},
"Wait for Page 3": {
"main": [
[
{
"node": "Search Page 3",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Places": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Place Details",
"type": "main",
"index": 0
}
]
]
},
"Get Place Details": {
"main": [
[
{
"node": "Map Fields",
"type": "main",
"index": 0
}
]
]
},
"Split All Results": {
"main": [
[
{
"node": "Collect All Place IDs",
"type": "main",
"index": 0
}
]
]
},
"On form submission": {
"main": [
[
{
"node": "Set Initial Parameters",
"type": "main",
"index": 0
}
]
]
},
"Append row in sheet": {
"main": [
[]
]
},
"Geocode Target City": {
"main": [
[
{
"node": "Generate Dynamic Grid",
"type": "main",
"index": 0
}
]
]
},
"Collect All Place IDs": {
"main": [
[
{
"node": "Split Places",
"type": "main",
"index": 0
}
]
]
},
"Generate Dynamic Grid": {
"main": [
[
{
"node": "Split Locations",
"type": "main",
"index": 0
}
]
]
},
"Set Initial Parameters": {
"main": [
[
{
"node": "Geocode Target City",
"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.
googleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow collects business leads from Google Maps by geocoding a user-provided city, searching across a small coordinate grid with Places Nearby Search pagination, and writing enriched business details (name, address, phone, website, ratings) to a Google Sheets spreadsheet.…
Source: https://n8n.io/workflows/15960/ — 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 repository contains an SLA-based lead routing workflow built in n8n, designed to ensure fast lead response, fair sales distribution, and controlled escalation without relying on a full CRM system
How it works A form trigger accepts an Industry + Location query (e.g. Accountants London). Text Search Page 1 calls Google Places Text Search to return results and a nextpagetoken. Conditional checks
Agencies, sales teams, and service businesses who want to instantly qualify inbound leads with an AI-powered phone call — no manual follow-up needed.
This n8n template automates lead generation by scraping Google Maps using the Olostep API. It extracts business names, locations, websites, phone numbers, and decision-maker names (CEO, Founder, etc.)
This workflow is a powerful B2B Lead Generation engine designed specifically for SDRs (Sales Development Representatives). It automates the entire process of finding, enriching, and qualifying prospec