Bright Data to Google Sheets, clean Maps leads
Copying Google Maps listings into a spreadsheet sounds simple until you do it for three cities, two service types, and a team that needs the data by Friday.
Bright Data leads automation hits hardest when outreach has to scale. A marketing manager trying to feed campaigns, a sales lead trying to keep reps busy, and an agency owner building lists for multiple clients all run into the same wall: manual collection and messy rows.
This workflow pulls Google Maps listings through Bright Data, expands searches into more cities using AI, removes duplicates, and writes clean leads into Google Sheets. You’ll see how the flow works, what you need, and where teams usually trip up.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Bright Data to Google Sheets, clean Maps leads
flowchart LR
subgraph sg0["Intake Form Flow"]
direction LR
n0["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/form.svg' width='40' height='40' /></div><br/>Intake Form Trigger"]
n1@{ icon: "mdi:brain", form: "rounded", label: "City LLM Provider", pos: "b", h: 48 }
n2@{ icon: "mdi:robot", form: "rounded", label: "Generate City Roster", pos: "b", h: 48 }
n3@{ icon: "mdi:brain", form: "rounded", label: "Category LLM Provider", pos: "b", h: 48 }
n4@{ icon: "mdi:robot", form: "rounded", label: "Classify Service Type", pos: "b", h: 48 }
n5["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Split Records by City"]
n6@{ icon: "mdi:swap-vertical", form: "rounded", label: "Batch Cities Iterator", pos: "b", h: 48 }
n7@{ icon: "mdi:database", form: "rounded", label: "Retrieve Sheet Records", pos: "b", h: 48 }
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Detect Duplicate Records"]
n9@{ icon: "mdi:swap-vertical", form: "rounded", label: "Batch Duplicate Handler", pos: "b", h: 48 }
n10@{ icon: "mdi:database", form: "rounded", label: "Locate Duplicate Row", pos: "b", h: 48 }
n11@{ icon: "mdi:database", form: "rounded", label: "Remove Duplicate Row", pos: "b", h: 48 }
n12@{ icon: "mdi:cog", form: "rounded", label: "Maps Data Scraper", pos: "b", h: 48 }
n13@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Verify Scrape Response", pos: "b", h: 48 }
n14["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Query Collection Progress"]
n15@{ icon: "mdi:cog", form: "rounded", label: "Throttle Pause", pos: "b", h: 48 }
n16@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Confirm Data Readiness", pos: "b", h: 48 }
n17["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Download Scrape Results"]
n18@{ icon: "mdi:database", form: "rounded", label: "Append Leads to Sheet", pos: "b", h: 48 }
n17 --> n18
n2 --> n4
n16 --> n17
n16 --> n14
n11 --> n9
n13 --> n14
n13 --> n18
n5 --> n6
n15 --> n16
n4 --> n5
n0 --> n2
n10 --> n11
n6 --> n7
n6 --> n12
n1 -.-> n2
n7 --> n8
n8 --> n9
n18 --> n6
n14 --> n15
n9 --> n10
n3 -.-> n4
n12 --> n13
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0 trigger
class n2,n4 ai
class n1,n3 aiModel
class n13,n16 decision
class n7,n10,n11,n18 database
class n14,n17 api
class n5,n8 code
classDef customIcon fill:none,stroke:none
class n0,n5,n8,n14,n17 customIcon
The Problem: Google Maps lead lists get messy fast
Manual Google Maps lead generation is a grind. You search, open listings, copy names, paste phone numbers, grab websites, then realize half the listings don’t have the field you need. Next you repeat the whole thing for “Austin” and “Dallas” and “Houston,” and by the end your sheet has duplicates, inconsistent categories, and missing context like the Maps URL or review count. The worst part is the mental load. You can’t tell what’s already been collected, what’s new, and what’s safe to hand off to a sales rep without re-checking it.
It adds up fast. Here’s where it breaks down once you try to scale beyond a single city and a single keyword.
- You spend about 2 hours just compiling a “decent” list for one service in one region.
- Duplicates creep in when you expand cities, which means reps call the same business twice.
- Categories are inconsistent because humans label things differently under time pressure.
- There’s no reliable way to re-run the process weekly without starting from scratch.
The Solution: Bright Data → Google Sheets lead list automation
This workflow turns “find me plumbers in California” into a repeatable pipeline that produces a clean Google Sheet your team can actually use. It starts with a simple intake form where you enter a service type, state, and country. From there, an AI agent generates a roster of relevant cities (so you don’t miss surrounding areas) and another AI step classifies the service type into a consistent category. Then Bright Data’s Google Maps dataset collects listings per city, with batching and checks so you don’t overwhelm the scraper or your workflow. Finally, the results are formatted, duplicates are removed by business name and phone number, and the cleaned rows are appended to Google Sheets with key fields like website, address, ratings, and the direct Google Maps URL.
The flow begins at the form submission, expands the search across a city list, then scrapes Google Maps data through Bright Data in controlled batches. After that, it verifies the scrape is ready, downloads the results, and writes them into Sheets while cleaning duplicates as it goes.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you need “coffee shops” across one state and you want results for 10 cities. Manually, if you spend about 10 minutes per city collecting and cleaning listings, that’s roughly 100 minutes, plus another 30 minutes removing duplicates and fixing categories. With this workflow, you submit one form (about 2 minutes), let the scrape run in the background with waits and progress checks (often 20–40 minutes), and the finished list lands in Google Sheets already deduped.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Bright Data for Google Maps dataset scraping access
- Google Sheets to store and share the lead list
- Anthropic API key (get it from the Anthropic Console)
Skill level: Intermediate. You’ll connect credentials, replace placeholder tokens, and test with sample inputs.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A form submission kicks things off. You enter the service type, state, and country in the intake form trigger, so the workflow starts with structured inputs instead of random notes.
AI expands and normalizes your targeting. An AI agent generates a list of relevant cities for your location, then another AI step categorizes the service type so your sheet stays consistent even when different people submit requests.
Bright Data collects listings in controlled batches. The workflow loops over cities, runs the Bright Data Google Maps dataset scrape, and uses checks, waits, and progress polling (HTTP requests) so you don’t write partial results or crash mid-run.
Google Sheets becomes the clean source of truth. Before appending new leads, it pulls existing sheet rows, identifies duplicates by business name and phone, removes duplicates, and then appends fresh results with fields like address, hours, rating, and review count.
You can easily modify the city expansion prompt to target nearby metros, counties, or zip clusters based on your needs. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Form Trigger
This workflow starts with a form that collects the service and location details used for scraping.
- Add or open Intake Form Trigger and set Form Title to
Extract Business Data Using Service Name and Location. - Set Form Description to
Fill details to extract business leads. - Confirm the form fields include Service, State, and Country with the dropdown options
USandIndia.
Step 2: Set Up AI City and Category Generation
Two AI steps generate city lists and classify the service type. Each agent uses a connected Anthropic model.
- Open City LLM Provider and select your Anthropic model. Credential Required: Connect your anthropicApi credentials.
- Open Generate City Roster and set Prompt to the existing expression:
=Provide a list of cities or sub areas located inside the Location of {{ $json.State }}, within the country {{ $json.Country }}. This data will be used to search on Google maps for businesses in that region. The output should be a plain text list, without bullets, numbering, or any special characters. Do not include any introduction, explanation, or concluding text—only the list of city names. - Open Category LLM Provider and select your Anthropic model. Credential Required: Connect your anthropicApi credentials.
- Open Classify Service Type and set Prompt to:
=Determine the appropriate category based on the following input: "{{ $('Intake Form Trigger').item.json.Service }}" The output must contain only the category name that best describes the given service. Examples of categories include but are not limited to: Electronics, Healthcare, Education, Food & Beverage, Automotive, Finance, Real Estate, etc.
City LLM Provider is connected as the language model for Generate City Roster and Category LLM Provider is connected to Classify Service Type — ensure credentials are added to those provider nodes (not the agent nodes).
Step 3: Split and Batch City Records
The workflow turns the AI city list into structured items and batches them for downstream processing.
- Open Split Records by City and keep the existing JavaScript that reads
Service,State,Country, and the AI city list to build per-city items. - Ensure Batch Cities Iterator is connected after Split Records by City to process cities in manageable chunks.
- Confirm the execution path: Classify Service Type → Split Records by City → Batch Cities Iterator.
Batch Cities Iterator outputs to both Retrieve Sheet Records and Maps Data Scraper in parallel.
Step 4: Configure Google Sheets Deduplication
This section checks for duplicates and deletes existing rows before adding new leads.
- Open Retrieve Sheet Records and set Document ID to
YOUR_GOOGLE_SHEET_IDand Sheet Name togid=0. Credential Required: Connect your googleSheetsOAuth2Api credentials. - In Detect Duplicate Records, keep the JavaScript that compares
Business Name,Phone Number,Address, andGoogle Maps URLto output duplicates. - Open Locate Duplicate Row and confirm filters: Address uses
={{ $json.Address }}and Google Maps URL uses={{ $json['Google Maps URL'] }}. Credential Required: Connect your googleSheetsOAuth2Api credentials. - Open Remove Duplicate Row and set Operation to
deletewith Start Index={{ $json.row_number }}. Credential Required: Connect your googleSheetsOAuth2Api credentials.
⚠️ Common Pitfall: Replace YOUR_GOOGLE_SHEET_ID in all Google Sheets nodes to avoid writing to a placeholder spreadsheet.
Step 5: Configure Scraping and Status Polling
The workflow scrapes Google Maps via Bright Data and polls until results are ready.
- Open Maps Data Scraper and keep the URLs expression:
=[ { "url": "https://www.google.com/maps/search/{{ $json.name }}+in+{{ $json.city }} {{ $json.state }}", "category": "{{ $json.category }}", "country_name": "{{ $json.country }}", "sample": false } ]. Credential Required: Connect your brightdataApi credentials. - Open Verify Scrape Response and keep the condition checking
={{ $json.message }}exists. - Open Query Collection Progress and set URL to
=https://api.brightdata.com/datasets/v3/progress/{{ $json.snapshot_id }}. Credential Required: Connect your httpHeaderAuth credentials. - Open Throttle Pause and set Amount to
25seconds to control polling frequency. - Open Confirm Data Readiness and keep the status check
={{ $json.status }}equalsready. - Open Download Scrape Results and set URL to
=https://api.brightdata.com/datasets/v3/snapshot/{{ $json.snapshot_id }}with query parameterformat=json. Credential Required: Connect your httpHeaderAuth credentials.
Tip: The loop is driven by Query Collection Progress → Throttle Pause → Confirm Data Readiness. Ensure your Bright Data dataset ID is valid to avoid infinite polling.
Step 6: Append Results to Google Sheets
Scraped results are mapped into your spreadsheet fields.
- Open Append Leads to Sheet and confirm Operation is
append. Credential Required: Connect your googleSheetsOAuth2Api credentials. - Verify the column mappings use expressions like City
={{ $('Split Records by City').item.json.city }}, Business Name={{ $json.name }}, and Google Maps URL={{ $json.url }}. - Confirm the flow Download Scrape Results → Append Leads to Sheet → Batch Cities Iterator to continue processing remaining cities.
Step 7: Test and Activate Your Workflow
Run a manual test to validate form input, AI output, scraping, and sheet updates before activating.
- Click Execute Workflow and submit the Intake Form Trigger with a known service (e.g., Laptop Store) and location.
- Watch for successful data passing from Generate City Roster and Classify Service Type into Split Records by City.
- Confirm Download Scrape Results returns JSON and Append Leads to Sheet appends rows with city, business name, address, and URLs.
- Once verified, toggle the workflow to Active to accept live form submissions.
Common Gotchas
- Google Sheets OAuth credentials can expire or need specific permissions. If things break, check the n8n Credentials screen and the Google Cloud OAuth consent/settings first.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Default prompts in AI nodes are generic. Add your brand voice early or you’ll be editing outputs forever.
Frequently Asked Questions
Usually about an hour, assuming your Bright Data and Google credentials are ready.
No. You’ll mostly connect accounts and paste API keys into the right credential fields.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Bright Data dataset usage and AI API costs.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. This specific Bright Data leads automation is commonly run self-hosted because long scrapes and batching are easier to manage when you control the runtime. If you go that route, plan on basic updates, backups, and keeping your credentials secure.
Yes, but you’ll want to adjust the intake form to accept a list, then loop through it before the “Classify Service Type” and “Split Records by City” logic. Common customizations include running several services per state, changing the city-generation prompt to focus on specific metro areas, and appending extra fields from Bright Data into the “Append Leads to Sheet” step.
Most of the time it’s an invalid token, missing dataset access, or a workspace/project mismatch inside Bright Data. Update the Bright Data credential in n8n and confirm your Google Maps dataset is enabled. If it runs for a bit and then fails, you may be hitting rate limits or pulling too much per batch, so lower batch sizes and lean on the workflow’s wait/progress checks.
A lot, but it depends on your Bright Data limits and how big your city roster is.
For this use case, usually yes. The batching, retry logic, and “check progress then download” pattern is awkward in simpler automation tools, and you’ll often pay more as volume grows. n8n also gives you the option to self-host, which is useful when scrapes run longer or you need tighter control over execution. Zapier or Make can still be fine for lightweight list-building, but this workflow is closer to a mini data pipeline than a two-step zap. If you’re unsure, Talk to an automation expert and we’ll map it to your volume and budget.
Once this is running, you stop “building lists” and start feeding outreach. The workflow handles the repetitive cleanup so your team can focus on contacting the right businesses.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.