This workflow corresponds to n8n.io template #13753 — 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 →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "1e4e2649-00f0-4696-87cd-327377ef0f51",
"name": "Google Places Search",
"type": "n8n-nodes-base.httpRequest",
"position": [
2160,
16
],
"parameters": {
"url": "https://places.googleapis.com/v1/places:searchText",
"method": "POST",
"options": {
"pagination": {
"pagination": {
"parameters": {
"parameters": [
{
"name": "pageToken",
"type": "body",
"value": "={{ $response.body.nextPageToken }}"
}
]
},
"maxRequests": 3,
"requestInterval": 500,
"limitPagesFetched": true,
"completeExpression": "={{ !$response.body.nextPageToken }}",
"paginationCompleteWhen": "other"
}
}
},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "textQuery",
"value": "={{ $json[\"Search Query\"] }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "X-Goog-Api-Key",
"value": "YourGoogePlacesAPIKeyHere"
},
{
"name": "X-Goog-FieldMask",
"value": "*"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "cc4b593b-3ff5-4eda-aa65-b5041d708351",
"name": "Split Out Places",
"type": "n8n-nodes-base.splitOut",
"position": [
2432,
16
],
"parameters": {
"options": {},
"fieldToSplitOut": "places"
},
"typeVersion": 1
},
{
"id": "e0435af5-c549-4072-91af-856ab936fc96",
"name": "Map Places Fields",
"type": "n8n-nodes-base.set",
"position": [
2608,
16
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "3b8b76a5-9dc1-4a2b-8ff9-99fa37d375d1",
"name": "source_id",
"type": "string",
"value": "={{ $json?.id || $json?.name?.startsWith(\"places/\") ? $json?.name?.split(\"/\")?.[1] : null}}"
},
{
"id": "96651d6f-8cf7-4c3f-ac55-3f782dd5e0ea",
"name": "name",
"type": "string",
"value": "={{ $json?.displayName?.text || $json?.name || null}}"
},
{
"id": "c4c1c4fd-0b35-4876-956f-2a291f346a4d",
"name": "address",
"type": "string",
"value": "={{ $json?.formattedAddress || $json?.shortFormattedAddress || null}}"
},
{
"id": "79607751-c822-4f53-99e9-68903eb78e73",
"name": "phone",
"type": "string",
"value": "={{ $json?.internationalPhoneNumber?.replaceAll(\"+\",\"00\")?.replaceAll(\" \", \"\") || null}}"
},
{
"id": "33e0baaa-e58f-48a7-bbb9-ec82517922eb",
"name": "social_searcher",
"type": "string",
"value": "={{ $json?.displayName ? \"https://www.social-searcher.com/google-social-search/?q=\" + encodeURIComponent($json.displayName.text) : null}}"
},
{
"id": "f1546b0c-a5a5-4244-bee5-c310ffe534a2",
"name": "website",
"type": "string",
"value": "={{ $json?.websiteUri || null}}"
},
{
"id": "cd77bcee-81d2-4147-90b7-5794f17b1b85",
"name": "=website_analysis_url",
"type": "string",
"value": "={{ $json?.websiteUri ? \"https://pagespeed.web.dev/analysis?url=\" + encodeURIComponent($json?.websiteUri) : null}}"
},
{
"id": "fb1ae2b8-69cc-4c6c-8e14-1929e865ac35",
"name": "google_maps_url",
"type": "string",
"value": "={{ ($json?.places?.googleMapsUri || (\"https://www.google.com/maps/place/?q=place_id:\" + ($json?.id || $json?.name?.replace('places/', '')))) }}"
},
{
"id": "e11fe54c-28c0-4d99-a839-e0aec93b1b4b",
"name": "rating",
"type": "string",
"value": "={{ $json?.rating || null }}"
},
{
"id": "040333f4-9398-49b8-bbac-62f51401c523",
"name": "reviews",
"type": "string",
"value": "={{ $json?.userRatingCount || null }}"
},
{
"id": "3e2c5585-a9a3-4cbb-ae64-1282de57cdf2",
"name": "coordinates",
"type": "string",
"value": "={{ $json?.location?.latitude}},{{ $json?.location?.longitude}}"
},
{
"id": "1c658707-21b9-4faf-adb5-ef8269aa19be",
"name": "address_components",
"type": "string",
"value": "={ \"street\": {{ $json?.addressComponents?.find(c => c.types.includes('route'))?.longText?.toJsonString() || null }}, \"city\": {{ $json?.addressComponents?.find(c => c.types.includes('locality'))?.longText?.toJsonString() || null }}, \"state\": {{ $json?.addressComponents?.find(c => c.types.includes('administrative_area_level_1'))?.shortText?.toJsonString() || null }}, \"postal_code\": {{ $json?.addressComponents?.find(c => c.types.includes('postal_code'))?.longText?.toJsonString() || null }}, \"country\": {{ $json?.addressComponents?.find(c => c.types.includes('country'))?.longText?.toJsonString() || null }}, \"country_code\": {{ $json?.addressComponents?.find(c => c.types.includes('country'))?.shortText?.toJsonString() || null }}, \"neighborhood\": {{ $json?.addressComponents?.find(c => c.types.includes('neighborhood'))?.longText?.toJsonString() || null }} }"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ade79b8f-823d-44e9-bb3b-fc6c721c08bc",
"name": "Append or update lead row",
"type": "n8n-nodes-base.googleSheets",
"position": [
2912,
16
],
"parameters": {
"columns": {
"value": {
"name": "={{ $json?.displayName?.text || $json?.name || null}}",
"phone": "={{ $json?.internationalPhoneNumber?.replaceAll(\"+\",\"00\")?.replaceAll(\" \", \"\") || null}}",
"rating": "={{ $json?.rating || null }}",
"address": "={{ $json?.formattedAddress || $json?.shortFormattedAddress || null}}",
"reviews": "={{ $json?.userRatingCount || null }}",
"website": "={{ $json?.websiteUri || null}}",
"category": "={{ $json?.primaryTypeDisplayName?.text || $json?.primaryType || $json?.types?.[0] || null }}",
"source_id": "={{ $json?.id || $json?.name?.startsWith(\"places/\") ? $json?.name?.split(\"/\")?.[1] : null}}",
"coordinates": "={{ $json?.location?.latitude}},{{ $json?.location?.longitude}}",
"google_maps_url": "={{ ($json?.places?.googleMapsUri || (\"https://www.google.com/maps/place/?q=place_id:\" + ($json?.id || $json?.name?.replace('places/', ''))))?.toJsonString() }}",
"social_searcher": "={{ $json?.displayName ? \"https://www.social-searcher.com/google-social-search/?q=\" + encodeURIComponent($json.displayName.text) : null}}",
"address_components": "={\n \"street\": {{ $json?.addressComponents?.find(c => c.types.includes('route'))?.longText?.toJsonString() || null }},\n \"city\": {{ $json?.addressComponents?.find(c => c.types.includes('locality'))?.longText?.toJsonString() || null }},\n \"state\": {{ $json?.addressComponents?.find(c => c.types.includes('administrative_area_level_1'))?.shortText?.toJsonString() || null }},\n \"postal_code\": {{ $json?.addressComponents?.find(c => c.types.includes('postal_code'))?.longText?.toJsonString() || null }},\n \"country\": {{ $json?.addressComponents?.find(c => c.types.includes('country'))?.longText?.toJsonString() || null }},\n \"country_code\": {{ $json?.addressComponents?.find(c => c.types.includes('country'))?.shortText?.toJsonString() || null }},\n \"neighborhood\": {{ $json?.addressComponents?.find(c => c.types.includes('neighborhood'))?.longText?.toJsonString() || null }}\n}",
"website_analysis_url": "={{ $json?.websiteUri ? \"https://pagespeed.web.dev/analysis?url=\" + encodeURIComponent($json?.websiteUri) : null}}"
},
"schema": [
{
"id": "source_id",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "source_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "name",
"type": "string",
"display": true,
"required": false,
"displayName": "name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "category",
"type": "string",
"display": true,
"required": false,
"displayName": "category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "address",
"type": "string",
"display": true,
"required": false,
"displayName": "address",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "phone",
"type": "string",
"display": true,
"required": false,
"displayName": "phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "social_searcher",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "social_searcher",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "website",
"type": "string",
"display": true,
"required": false,
"displayName": "website",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "website_analysis_url",
"type": "string",
"display": true,
"required": false,
"displayName": "website_analysis_url",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "google_maps_url",
"type": "string",
"display": true,
"required": false,
"displayName": "google_maps_url",
"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
},
{
"id": "coordinates",
"type": "string",
"display": true,
"required": false,
"displayName": "coordinates",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "address_components",
"type": "string",
"display": true,
"required": false,
"displayName": "address_components",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "details",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "details",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": [
"source_id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1tS6g55gBQeRfQTe8hCt-t4OLf3BgEtATwr7AR4IdWWM/edit#gid=0",
"cachedResultName": "LEADS"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1tS6g55gBQeRfQTe8hCt-t4OLf3BgEtATwr7AR4IdWWM",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1tS6g55gBQeRfQTe8hCt-t4OLf3BgEtATwr7AR4IdWWM/edit?usp=drivesdk",
"cachedResultName": "Leads from Google Maps"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "1f32af52-4880-4e85-8d93-d881f2fdcc5f",
"name": "Mark Query as Scraped",
"type": "n8n-nodes-base.googleSheets",
"position": [
3360,
0
],
"parameters": {
"columns": {
"value": {
"Scraped": "=TRUE",
"row_number": "={{ $('Get rows in QUERIES').item.json.row_number }}"
},
"schema": [
{
"id": "Search Query",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Search Query",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Scraped",
"type": "string",
"display": true,
"required": false,
"displayName": "Scraped",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "id",
"value": "={{ $('Get rows in QUERIES').params.sheetName.value }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Get rows in QUERIES').params.documentId.value }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"executeOnce": true,
"typeVersion": 4.7
},
{
"id": "ef0657dc-c315-43cc-ac9f-b264587f4b64",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
1872,
16
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "9f5ef015-c6c9-42f5-9f08-cc0dc4849d6f",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1216,
-640
],
"parameters": {
"color": 7,
"width": 384,
"height": 912,
"content": "## 2. Search & Extraction\nThis section fetches data from the Places API (New).\n1.Setup Credentials\n2. Copy [this](https://docs.google.com/spreadsheets/d/1x_GYw6KHgvhe2dgSw1eKKTimIzQYTsRkkWoxkAJEdD8/copy?usp=sharing) spreadsheet\n3. Add queries for the scheduled execution in the QUERIES (i.e. \"Bakeries New York USA\", \"Cafes Berlin Germany\" etc.).\nLeave the **Scraped** column empty or set to 'false'\n\n\nNote: Adjust \"Max Pages\" in the HTTP node to control result volume and API costs.\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "b6ab7238-bb17-47d8-a8c7-9fea35f543b6",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
2048,
-656
],
"parameters": {
"color": 7,
"width": 336,
"height": 928,
"content": "## 2. API Key and Max Pages\n\n1. Provide your Google Places (new) API Key\nin the **X-Goog-Api-Key** body parameter value\n\n- This API key must be generated from a Google Cloud Project with the \"Places API\" enabled. \n\n### Optional configurations:\n\n- Adjust the **Max Pages** to control how many pages of results are fetched for each query. (Mind that more pages means more requests to the Google API and potentially more costs. Googles allows you to easily set alerts and strict quotas per api endpoint to prevent unexpected charges. This particular node is using the **v1/places:searchText** endpoint.\n\n- API Key Security: The API key is currently hardcoded directly into the \"Google Places Search\" HTTP Request node. For production and business critical accounts, it is strongly advised to secure this key using an n8n environment variable to prevent exposure and facilitate easier management AND limit the key access to the v1/places:searchText API request only\n\n"
},
"typeVersion": 1
},
{
"id": "411fad2e-8c9b-4b71-ae90-c186fda006ec",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
496,
-640
],
"parameters": {
"color": 7,
"width": 496,
"height": 912,
"content": "## 1. Entry Points\nChoose between the Form node for on-demand searches or the Schedule trigger to process a bulk list of queries from your Google Sheet.\n\n"
},
"typeVersion": 1
},
{
"id": "b99c76d7-c76d-4d39-9b6b-a01032d74660",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2864,
-208
],
"parameters": {
"color": 7,
"width": 672,
"height": 480,
"content": "## Data Storage\nNodes here handle lead deduplication and write new results to your \"LEADS\" sheet. If the workflow was scheduled, it updates the source query status to prevent duplicates."
},
"typeVersion": 1
},
{
"id": "0100efce-cb5f-493f-8794-2840d065d15c",
"name": "Get rows in QUERIES",
"type": "n8n-nodes-base.googleSheets",
"position": [
1344,
32
],
"parameters": {
"options": {
"returnFirstMatch": true
},
"filtersUI": {
"values": [
{
"lookupValue": "false",
"lookupColumn": "Scraped"
},
{
"lookupColumn": "Scraped"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 35374809,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1tS6g55gBQeRfQTe8hCt-t4OLf3BgEtATwr7AR4IdWWM/edit#gid=35374809",
"cachedResultName": "SEARCH QUERIES"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1tS6g55gBQeRfQTe8hCt-t4OLf3BgEtATwr7AR4IdWWM",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1tS6g55gBQeRfQTe8hCt-t4OLf3BgEtATwr7AR4IdWWM/edit?usp=drivesdk",
"cachedResultName": "Leads from Google Maps"
},
"combineFilters": "OR"
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "0bcc72a4-8ea3-47f7-b750-320dd4b4a765",
"name": "Scrape on schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
720,
32
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "b3efbdad-0a9e-4084-bf91-22d8ac44dad3",
"name": "Scrape on form submission",
"type": "n8n-nodes-base.formTrigger",
"position": [
720,
-432
],
"parameters": {
"options": {},
"formTitle": "Scrape Google Maps leads",
"formFields": {
"values": [
{
"fieldName": "Search Query",
"fieldLabel": "What businesses are you looking for and where",
"placeholder": "Coworking spaces Berlin Germany",
"requiredField": true
}
]
},
"formDescription": "Enter a Google Maps Query to find business leads "
},
"typeVersion": 2.4
},
{
"id": "7bfc3769-56db-4133-b5dd-bfa493bb9e87",
"name": "Discard empty queries",
"type": "n8n-nodes-base.filter",
"position": [
1632,
32
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d481ee97-ed3d-4d39-8954-17a9f5fbc2e5",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json[\"Search Query\"] }}",
"rightValue": false
}
]
}
},
"typeVersion": 2.3
},
{
"id": "a15432a0-72f0-4922-9e10-8cadbb2972b5",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
496,
-1328
],
"parameters": {
"width": 1504,
"height": 592,
"content": "# Google Maps Lead Extractor [Places API (New)]\n\n## How it works\nThis workflow automates lead collection from Google Maps using two entry points:\n* **Manual:** Submit a single query (e.g., \"Cafes in Paris\") via the n8n Form.\n* **Automated:** A Schedule trigger fetches pending queries from a Google Sheet.\n\nThe workflow calls the **Places API (New)**, parses business details (name, phone, website, Social Searcher and Pagespeed links), and saves them to your destination sheet. It includes a deduplication step using the **Google Place ID** to ensure you don't save the same lead twice. If running on a schedule, it automatically marks queries as \"scraped\" in your source sheet.\n\n## Setup steps\n1. **API Key:** Enable the **Places API (New)** in Google Cloud Console and paste your key into the 'Google Places Search' node, `X-Goog-Api-Key` parameter value.\n2. **Sheet Template:** Make a copy of [this Google Sheet](https://docs.google.com/spreadsheets/d/1x_GYw6KHgvhe2dgSw1eKKTimIzQYTsRkkWoxkAJEdD8/copy).\n3. **Credentials:** Connect your Google Sheets OAuth2 account to all Google Sheets nodes.\n4. **Configure Nodes:** Ensure the 'Get rows in QUERIES' and 'Append or update lead row' nodes are pointing to your new spreadsheet copy.\n5. **Security:** For production, move your API key from the HTTP header to an n8n Secret or Environment Variable."
},
"typeVersion": 1
},
{
"id": "e0cfa987-f5ee-44dc-b198-ceab69924df5",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
3120,
16
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "6ac8e0cc-4dd0-4803-9da3-d79e87254a00",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{$('Get rows in QUERIES').isExecuted}}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.3
}
],
"connections": {
"If": {
"main": [
[
{
"node": "Mark Query as Scraped",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Google Places Search",
"type": "main",
"index": 0
}
]
]
},
"Split Out Places": {
"main": [
[
{
"node": "Map Places Fields",
"type": "main",
"index": 0
}
]
]
},
"Map Places Fields": {
"main": [
[
{
"node": "Append or update lead row",
"type": "main",
"index": 0
}
]
]
},
"Scrape on schedule": {
"main": [
[
{
"node": "Get rows in QUERIES",
"type": "main",
"index": 0
}
]
]
},
"Get rows in QUERIES": {
"main": [
[
{
"node": "Discard empty queries",
"type": "main",
"index": 0
}
]
]
},
"Google Places Search": {
"main": [
[
{
"node": "Split Out Places",
"type": "main",
"index": 0
}
]
]
},
"Discard empty queries": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Append or update lead row": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Scrape on form submission": {
"main": [
[
{
"node": "Merge",
"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 is designed for sales teams, marketing professionals, and business developers who want to automate lead generation. It is ideal for budget-conscious individuals or small businesses looking to collect structured contact information from Google Maps without manual…
Source: https://n8n.io/workflows/13753/ — 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
This workflow transforms your Meta Ads creatives into a rich dataset of actionable insights. It's designed for data-driven marketers, performance agencies, and analysts who want to move beyond basic m
I created this workflow with great care to help you simplify your daily reporting routine. If you manage Meta Ads campaigns, you know how time-consuming it can be to open Ads Manager, filter data, and
I built this workflow to remove the daily pain of Meta Ads reporting. If you manage multiple ad accounts, you know how time-consuming it is to open Ads Manager, export campaign data, clean spreadsheet
This workflow monitors Meta Ads and Google Ads campaigns on a daily schedule to detect performance drops. It fetches yesterday’s campaign data, standardizes metrics, and calculates CTR and ROAS agains