{
  "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
          }
        ]
      ]
    }
  }
}