Google Maps to Google Sheets, clean leads you can trust
You find a promising niche on Google Maps, copy a few businesses into a sheet, and then reality hits. Half the “websites” are Google redirects, emails are missing, and you’ve already pasted the same company twice. Again.
This is where Maps leads automation pays for itself. Marketing managers trying to fill the pipeline feel it first, but agency owners and solo consultants doing research for clients get stuck in the same loop. You want a lead list you can actually trust, not a spreadsheet of maybes.
This n8n workflow pulls prospects from Google Maps, cleans and dedupes URLs, enriches business profiles via Apify, and logs everything into Google Sheets. You’ll see how it works, what you need, and what you can customize for your market.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Maps to Google Sheets, clean leads you can trust
flowchart LR
subgraph sg0["Scraper Workflow Flow"]
direction LR
n0@{ icon: "mdi:cog", form: "rounded", label: "Eliminate Repeated Links", pos: "b", h: 48 }
n2["<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 Maps Search"]
n3["<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/>Extract Links from Results"]
n4@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Exclude Unrelated Links", pos: "b", h: 48 }
n5@{ icon: "mdi:swap-vertical", form: "rounded", label: "Iterate URL Batches", pos: "b", h: 48 }
n6@{ icon: "mdi:play-circle", form: "rounded", label: "Scraper Workflow Trigger", pos: "b", h: 48 }
n9@{ icon: "mdi:robot", form: "rounded", label: "Business Insight Agent", pos: "b", h: 48 }
n10@{ icon: "mdi:brain", form: "rounded", label: "OpenRouter Chat Engine", pos: "b", h: 48 }
n11["<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/>Assemble Prompt Text"]
n12@{ icon: "mdi:cog", form: "rounded", label: "Combine Prompt Data", pos: "b", h: 48 }
n16@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Branch Logic Check", 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/merge.svg' width='40' height='40' /></div><br/>Combine Data Streams"]
n18["<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/>Parse JSON Output"]
n19@{ icon: "mdi:database", form: "rounded", label: "Append Data to Sheet", pos: "b", h: 48 }
n20["<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/>Invoke Apify Scraper"]
n21@{ icon: "mdi:database", form: "rounded", label: "Lookup Existing Records", pos: "b", h: 48 }
n22["<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/>Check Domain Presence"]
n23@{ icon: "mdi:cog", form: "rounded", label: "Aggregate URL Fields", pos: "b", h: 48 }
n16 --> n20
n16 --> n5
n11 --> n9
n22 --> n17
n17 --> n16
n9 --> n18
n12 --> n11
n20 --> n12
n23 --> n22
n5 --> n17
n5 --> n21
n21 --> n23
n10 -.-> n9
n0 --> n5
n4 --> n0
n6 --> n2
n3 --> n4
n19 --> n5
n2 --> n3
n18 --> n19
end
subgraph sg1["Scheduled Automation Flow"]
direction LR
n1@{ icon: "mdi:swap-vertical", form: "rounded", label: "Iterate Query Batches", pos: "b", h: 48 }
n7@{ icon: "mdi:cog", form: "rounded", label: "Pause Between Runs", pos: "b", h: 48 }
n8@{ icon: "mdi:cog", form: "rounded", label: "Run Sub-Workflow Required", pos: "b", h: 48 }
n13@{ icon: "mdi:play-circle", form: "rounded", label: "Scheduled Automation Trigger", pos: "b", h: 48 }
n14@{ icon: "mdi:database", form: "rounded", label: "Retrieve Sheet Rows", pos: "b", h: 48 }
n15@{ icon: "mdi:database", form: "rounded", label: "Modify Sheet Row", pos: "b", h: 48 }
n13 --> n14
n1 --> n8
n14 --> n1
n15 --> n7
n7 --> n1
n8 --> n15
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 n6,n13 trigger
class n9 ai
class n10 aiModel
class n4,n16 decision
class n19,n21,n14,n15 database
class n2,n20 api
class n3,n11,n18,n22 code
classDef customIcon fill:none,stroke:none
class n2,n3,n11,n17,n18,n20,n22 customIcon
The Problem: Google Maps leads get messy fast
Google Maps is a goldmine for local lead gen, but collecting that data manually is a slow grind. You run a search, open profiles, copy names, websites, phone numbers, maybe the address, and then you try to “standardize it later.” Later never comes. Meanwhile, redirects sneak into your sheet, different locations of the same brand show up as separate leads, and contact fields are missing right when you need them. The worst part is the mental load: you’re doing detective work when you should be doing outreach.
It adds up fast. Here’s where it breaks down once you try to scale beyond a handful of searches.
- You waste about 2 hours building a list that still needs cleaning.
- Duplicate domains slip in, so outreach looks sloppy and you burn goodwill.
- Missing emails and inconsistent fields force you into another research pass.
- You can’t reliably re-run the process because you don’t track what’s already processed.
The Solution: Search Google Maps, clean the data, and store it automatically
This workflow turns Google Maps searches into a repeatable lead pipeline that runs on a schedule (every hour). It starts by reading keyword queries from a dedicated Google Sheets tab, then searches Google Maps for each query via an HTTP request. From those results, it extracts external website URLs, filters out junk domains (like Google-owned pages or placeholder sites), and removes duplicates before doing anything expensive. Next, it checks your Google Sheet to see if that domain already exists, which prevents repeats from earlier runs. For each new domain, it calls an Apify scraper to pull a full business profile, and it can optionally run an AI Agent (Gemini via OpenRouter) to generate structured business insights you can actually use. Finally, everything is appended into a “Sheet1” tab, and the original keyword row gets marked as processed so the workflow doesn’t keep spinning its wheels.
The workflow begins on a schedule and grabs unprocessed search terms from Google Sheets. It then collects and cleans website URLs from Google Maps results, scrapes and enriches each business, and writes a deduped row back to your output sheet. Simple input, clean output.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you’re building a list for “roofing companies in Austin” and “emergency plumbers in Phoenix,” and you want about 40 solid prospects per query. Manually, you might spend about 3 minutes per business to copy details and double-check the website, which is roughly 2 hours for 40 leads. With this workflow, you drop the keywords into the sheet once, and the hourly run does the searching, cleaning, and logging while you do other work. Your “time spent” becomes a quick review and maybe a few edits, not an afternoon of copy-paste.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets to store keywords and results.
- Apify for scraping complete business profiles.
- OpenRouter API key (get it from your OpenRouter dashboard)
Skill level: Intermediate. You’ll connect accounts, paste API keys, and adjust a couple of sheet/tab names.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A scheduled run pulls your next keywords. Every hour, n8n reads rows from your Google Sheets “keywords” tab and selects the ones you haven’t processed yet.
Google Maps search results get turned into a usable URL list. The workflow runs the Maps query via HTTP request, then parses the response to extract external website links. After that, it filters out obvious noise (Google-owned domains, placeholders) and removes duplicates.
Each domain is checked before it becomes a “lead.” n8n looks up the domain in your output sheet first. If it’s already there, it skips it. If it’s new, the workflow calls an Apify actor to scrape business details and can send that data to an AI Agent to generate structured insights (like services, market position, and suggested angles).
Clean rows land in Google Sheets, and keywords get marked done. The scraped and AI-enriched fields are appended to “Sheet1,” then the workflow updates the keyword row so it won’t run again next hour.
You can easily modify the keyword list and filtering rules to match your niche and geography based on your needs. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Schedule Trigger
This workflow starts on a schedule to pull unprocessed queries and then hands off map scraping to a sub-workflow trigger.
- Open Scheduled Automation Trigger and set the schedule rule to run on your desired interval (currently set to hourly).
- Verify Scraper Workflow Trigger exists as an entry point for the sub-workflow; it should be connected to Query Maps Search.
- Confirm the flow order: Scheduled Automation Trigger → Retrieve Sheet Rows → Iterate Query Batches.
Step 2: Connect Google Sheets
Google Sheets powers query intake, de-duplication checks, and output storage.
- In Retrieve Sheet Rows, select your spreadsheet and sheet: Document set to the Google Sheet ID and Sheet Name set to
keywords. Credential Required: Connect yourgoogleSheetsOAuth2Apicredentials. - In Lookup Existing Records, select the output sheet (e.g.,
Sheet1) for de-duplication checks. Credential Required: Connect yourgoogleSheetsOAuth2Apicredentials. - In Modify Sheet Row, confirm Operation is set to
updateand map query to{{$json.query}}with processed set toTRUE. Credential Required: Connect yourgoogleSheetsOAuth2Apicredentials. - In Append Data to Sheet, confirm Operation is
appendand the destination sheet is set toSheet1. Credential Required: Connect yourgoogleSheetsOAuth2Apicredentials.
Step 3: Configure Maps Search and URL Filtering
This section turns queries into candidate business domains and removes irrelevant URLs.
- In Query Maps Search, set URL to
=https://www.google.com/maps/search/{{ $json.query }}. - In Extract Links from Results, keep the JavaScript as provided to extract domains from HTML results.
- In Exclude Unrelated Links, confirm the filter uses leftValue
={{ $json.url }}and excludes the provided regex list vianotRegex. - In Eliminate Repeated Links, leave defaults to remove duplicates before batching.
- In Iterate URL Batches, keep the batch loop for per-domain processing and ensure it routes to both Combine Data Streams and Lookup Existing Records on the second output.
Step 4: Set Up Domain Checks and Branch Logic
This logic prevents re-processing domains already stored in your sheet.
- In Aggregate URL Fields, ensure it aggregates the website_url field (as configured).
- In Check Domain Presence, keep the provided JavaScript, which compares Iterate URL Batches domains with stored URLs.
- In Combine Data Streams, set Mode to
combineBySqland Query toSELECT * FROM input1 LEFT JOIN input2 ON 1=1. - In Branch Logic Check, verify the condition checks
={{ $json.dontExist }}is true before calling the scraper. - In Run Sub-Workflow Required, set the Workflow ID to the target scraper workflow and keep Mode as
each.
Step 5: Set Up AI Enrichment and Prompt Assembly
The Apify scraper extracts content, which is turned into a prompt for the AI agent and parsed into structured JSON.
- In Invoke Apify Scraper, set URL to
=https://api.apify.com/v2/acts/mohamedgb00714~firescraper-ai-prompt-website-content-markdown-scraper/run-sync-get-dataset-items?token=[CONFIGURE_YOUR_TOKEN]and keep JSON Body as provided (note it uses{{$json.url}}). - In Combine Prompt Data, set Aggregate to
aggregateAllItemData. - In Assemble Prompt Text, keep the script that joins
promptResultvalues into a single prompt. - In Business Insight Agent, set Text to
={{ $json.prompt }}and keep the system message provided. - OpenRouter Chat Engine is connected as the language model for Business Insight Agent — ensure credentials are added to OpenRouter Chat Engine. Credential Required: Connect your
openRouterApicredentials. - In Parse JSON Output, keep the code that strips code fences and parses valid JSON.
[CONFIGURE_YOUR_TOKEN] in Invoke Apify Scraper with your Apify token; leaving the placeholder will cause 401 errors.Step 6: Configure Output Updates and Rate Limiting
After enrichment, the workflow appends data, updates the query row, and pauses before the next batch.
- Verify Append Data to Sheet receives parsed JSON from Parse JSON Output and maps fields automatically.
- Ensure Run Sub-Workflow Required → Modify Sheet Row marks each query as processed with
processed = TRUE. - In Pause Between Runs, set Unit to
minutesand Amount to20to throttle Google Maps requests. - Confirm the batch loop closes with Pause Between Runs → Iterate Query Batches for the next query.
Step 7: Test and Activate Your Workflow
Run a controlled test before turning the schedule on.
- Click Execute Workflow on Scheduled Automation Trigger to run a manual test using a small set of unprocessed queries.
- Verify that Query Maps Search returns HTML data, Extract Links from Results produces URLs, and Branch Logic Check routes new domains to Invoke Apify Scraper.
- Confirm that Parse JSON Output produces valid JSON and Append Data to Sheet adds rows to your output sheet.
- Validate that Modify Sheet Row sets
processedtoTRUEand the loop waits in Pause Between Runs. - Once the test is successful, toggle the workflow to Active to enable scheduled production runs.
Common Gotchas
- Google Sheets credentials can expire or need specific permissions. If things break, check the n8n credential connection status and the target spreadsheet sharing 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.
- OpenRouter (Gemini) prompts are generic by default. Add your targeting rules and brand voice early or you’ll be editing outputs forever.
Frequently Asked Questions
About an hour if your Sheets and API keys are ready.
No. You’ll mostly connect accounts and paste API keys. The only “technical” part is making sure your sheet tabs match the workflow.
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 OpenRouter and Apify usage costs, which depend on how many businesses you enrich.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and it’s one of the best reasons to use this workflow. Update the queries in the Google Sheets “keywords” tab to swap locations, categories, and intent (for example, “dentist + Invisalign” versus “emergency dentist”). You can also tweak the URL filtering logic so it blocks specific directories or known aggregator domains in your niche. If you use the AI Agent step, adjust the prompt assembly so it outputs exactly what your sales team needs, like “best outreach angle” or “likely ad spend level.”
Usually it’s an expired credential or the spreadsheet permissions changed. Reconnect the Google Sheets credential in n8n, then confirm the Google account still has access to the exact file and tabs the workflow references. Also check that the tab names haven’t been renamed (for example, “Sheet1” or “keywords”), because that can look like a connection issue even when auth is fine.
On a typical n8n setup, hundreds of leads per day is realistic, and self-hosting scales further if your server can handle it.
Often, yes, because this workflow needs looping, deduping, and multi-step enrichment that gets awkward (and pricey) in simpler automation tools. n8n handles branching logic and batch processing cleanly, so you can check “already exists?” before you scrape or enrich anything. The HTTP and code steps also give you more control over filtering Google Maps results, which matters if you care about clean domains. Zapier or Make can still be fine if you only want to push a small number of pre-cleaned records into a sheet. If you’re unsure, Talk to an automation expert and get a quick recommendation.
Once this is running, your spreadsheet stops being a messy draft and starts being a lead engine. Set it up once, then spend your time on outreach and offers instead of cleaning rows.
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.