Google Maps to Airtable, clean leads without copy paste
Lead lists fall apart in the boring places. You find businesses on Google Maps, open ten tabs, try to spot an email, paste it into a sheet, then realize you grabbed the same company twice.
This Google Maps Airtable automation hits B2B marketers first, but lead gen freelancers and small sales teams feel it too. You get a clean table of businesses with real emails (when available), fewer duplicates, and far less manual cleanup.
Below, you’ll see exactly how the workflow pulls Google Maps results, visits each site like a real user, extracts a valid email, then upserts it into Airtable so your outreach can start immediately.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Maps to Airtable, clean leads without copy paste
flowchart LR
subgraph sg0["On form submission Flow"]
direction LR
n0@{ icon: "mdi:cog", form: "rounded", label: "Limit", pos: "b", h: 48 }
n1["<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/>On form submission"]
n2@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Run an Actor", pos: "b", h: 48 }
n3@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Get dataset items", pos: "b", h: 48 }
n4@{ icon: "mdi:swap-vertical", form: "rounded", label: "Grab Desired Fields", pos: "b", h: 48 }
n5@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items", pos: "b", h: 48 }
n6["<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/markdown.dark.svg' width='40' height='40' /></div><br/>Markdown"]
n7@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items1", pos: "b", h: 48 }
n8@{ icon: "mdi:cog", form: "rounded", label: "Wait1", pos: "b", h: 48 }
n9["<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/>Merge"]
n10@{ icon: "mdi:swap-vertical", form: "rounded", label: "Grab Desired Fields1", 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/merge.svg' width='40' height='40' /></div><br/>Merge1"]
n12@{ icon: "mdi:swap-vertical", form: "rounded", label: "Parse url/website", pos: "b", h: 48 }
n13["<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/>Remove Query Parameters & Fr.."]
n14@{ icon: "mdi:swap-vertical", form: "rounded", label: "User-Agents", pos: "b", h: 48 }
n15["<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/>Website Scraping"]
n16@{ icon: "mdi:cog", form: "rounded", label: "Random Wait", 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/code.svg' width='40' height='40' /></div><br/>Extract Email Address"]
n18@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Filter Leads with Email only", pos: "b", h: 48 }
n19["<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/airtable.svg' width='40' height='40' /></div><br/>Database"]
n0 --> n5
n9 --> n5
n8 --> n11
n11 --> n7
n6 --> n16
n16 --> n9
n14 --> n15
n2 --> n3
n5 --> n7
n5 --> n12
n5 --> n9
n7 --> n18
n7 --> n17
n7 --> n11
n15 --> n6
n3 --> n4
n12 --> n13
n1 --> n2
n4 --> n0
n10 --> n19
n17 --> n8
n18 --> n10
n13 --> n14
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 n1 trigger
class n2,n3,n18 decision
class n19 database
class n15 api
class n13,n17 code
classDef customIcon fill:none,stroke:none
class n1,n6,n9,n11,n13,n15,n17,n19 customIcon
The Problem: Google Maps leads are messy to collect
Google Maps is great for finding businesses, but it’s not built for building a usable outreach list. You click into a listing, copy a website, open it, hope there’s a contact page, then start hunting for an email in headers, footers, or a buried “About” page. Now do that 20 times. It’s slow, it breaks your focus, and it’s easy to miss obvious emails because you’re rushing. And even when you succeed, your “database” becomes a spreadsheet full of inconsistent formatting, duplicates, and half-filled fields that no one trusts.
It adds up fast. Here’s where it breaks down in real life.
- Collecting 30 leads can take about 2 hours when you include tab switching and cleanup.
- You end up with duplicates because “Acme Plumbing” shows up with slight naming differences.
- Emails get missed because you only checked the homepage and moved on.
- Manual copy-paste invites mistakes, and those mistakes show up later in bounced outreach.
The Solution: Pull leads, scrape emails, and upsert into Airtable
This workflow turns a Google Maps search into an outreach-ready lead list without the tab chaos. It starts with a simple form where you enter a keyword, a location, and how many leads you want (10 by default). n8n then runs an Apify Google Maps actor to fetch matching businesses, pulls the key fields you care about, and processes them in batches so it stays stable. For each business, it visits the website using lightweight HTTP requests, rotates User-Agent headers to look more like normal browsing, and waits a few seconds between requests to reduce blocking. The HTML is converted into clean text, then the workflow extracts the first valid email it can find, filters out empty results, and saves clean records into Airtable using an upsert approach to avoid duplicates.
The workflow starts when you submit the form in n8n. From there, Apify collects the Google Maps business data, then n8n loops through each website to pull content and extract an email. Finally, Airtable receives only the leads that passed validation, formatted and ready for your next campaign.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you want 25 “roofing contractors” in Austin. Manually, you might spend about 5 minutes per business between Google Maps, the website, and your spreadsheet, which is roughly 2 hours total. With this workflow, you submit the form in about 2 minutes, then let the scraper run in the background with built-in delays (a few seconds per site) while it extracts emails and filters junk. You still review the final Airtable table, but that’s usually 10 minutes, not an afternoon.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Apify to scrape Google Maps business data.
- Airtable to store leads with dedupe via upsert.
- Apify API Key (get it from your Apify account settings)
Skill level: Intermediate. You’ll connect credentials and may tweak a couple of field-mapping steps in n8n.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A form submission kicks everything off. You enter a search keyword, a target location, and how many leads you want returned. That single input makes the workflow reusable for different niches and cities.
Google Maps results are collected and capped. Apify searches Google Maps, then n8n retrieves the dataset records, selects the fields you care about (like name, phone, website, category), and limits processing so you don’t accidentally pull hundreds of results.
Websites are visited carefully, not aggressively. Each lead’s website URL is cleaned to remove only tracking junk (query strings and fragments), then fetched via HTTP with rotating User-Agent headers. The workflow converts HTML to readable text and pauses for a few seconds between requests, which helps avoid rate limits and basic bot detection.
Emails are extracted, validated, and stored. The workflow scans the cleaned text for an email address, filters out missing or invalid results, then sends only the good leads into Airtable. The Airtable step uses an upsert pattern, so repeat runs don’t keep creating duplicates.
You can easily modify the fields you store to match your base, like adding ratings or categories from Apify. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Form Submission Trigger
Set up the form that kicks off the workflow and captures user input for keyword, location, and lead count.
- Add and open Form Submission Trigger.
- Set Form Title to
Web Scraper. - Set Form Description to
This scrapes website urls from Google Maps to get company information.. - Add form fields: Keyword (required), Location (required), and No. Of Leads with placeholder
10.
Step 2: Connect Apify for Google Maps data
Run the Apify actor and retrieve dataset items based on the form input.
- Open Launch Apify Task and set Operation to
Run actor. - Set Timeout to
120and Wait for Finish to20. - Paste the Custom Body exactly as:
={ "language": "en", "locationQuery": "{{ $json.Location }}", "maxCrawledPlacesPerSearch": {{ $json['No. Of Leads'] ? Number($json['No. Of Leads']) : 10 }}, "searchStringsArray": [ "{{ $json.Keyword }}" ], "skipClosedPlaces": false, "website": "withWebsite" }. - Credential Required: Connect your apifyApi credentials in Launch Apify Task.
- Open Retrieve Dataset Records and set Resource to
Datasets. - Set Operation to
Get itemsand Dataset ID to={{ $json.defaultDatasetId }}. - Credential Required: Connect your apifyApi credentials in Retrieve Dataset Records.
Step 3: Shape and batch the dataset results
Normalize the dataset output, cap volume, and split items into batches for downstream processing.
- In Select Target Fields, map fields exactly: Company Name to
={{ $json.title }}, Company Category to={{ $json.categoryName }}, Address to={{ $json.address }}, Website to={{ $json.website }}, Phone Number to={{ $json.phoneUnformatted }}, Rating to={{ $json.totalScore }}, and Other Categories to={{ $json.categories }}. - In Cap Items, set Max Items to
30. - Use Batch Iterator as the split node for processing each record.
Step 4: Fetch website HTML and extract emails
Prepare URLs, rotate user agents, scrape HTML, convert to markdown, and extract emails.
- In Parse Site URL, set website to
={{ $json.Website }}. - In Clean URL Parts, keep the provided JavaScript to normalize URLs and strip query strings.
- In Assign User Agents, set website to
={{ $json.website }}and set User-Agent to the random array expression={{ [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36", "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:118.0) Gecko/20100101 Firefox/118.0" ][Math.floor(Math.random() * 5)] }}. - In Assign User Agents, set Accept to
text/html,*/*;q=0.8, Accept-Language toen-US,en;q=0.9, and Referer tohttps://google.com/. - In Fetch Website HTML, set URL to
={{ $json.website }}and enable Send Headers with User-Agent, Accept-Language, Referer, and Accept mapped from the incoming JSON. - In Markdown Converter, set HTML to
={{ $json.data }}. - In Randomized Pause, set Amount to
={{ Math.floor(Math.random() * 6 + 2) }}to throttle requests. - Keep Combine Streams in Mode
combinewith Combine By set tocombineByPosition. - In Extract Email Data, keep the provided JavaScript to extract and de-duplicate emails.
- In Delay Step, set Amount to
1to sequence the merge safely. - Keep Merge Email Results in Mode
combinewith Combine By set tocombineByPosition.
Step 5: Filter valid emails and finalize fields
Remove invalid email results and build the final record fields before sending to Airtable.
- In Secondary Batch Loop, keep the split-in-batches configuration to iterate over each scraped site.
- In Filter Valid Emails, keep the condition: email
notEqualsN/Ausing={{ $json.email }}. - In Finalize Field Set, map fields: Company Name to
={{ $json['Company Name'] }}, Company Category to={{ $json['Company Category'] }}, Address to={{ $json.Address }}, Website to={{ $json.Website }}, Phone Number to={{ $json['Phone Number'] }}, Rating to={{ $json.Rating }}, Email to={{ $json.email }}, and Other Categories to={{ $json['Other Categories'] }}.
Step 6: Configure Airtable output
Upsert the finalized records into Airtable using email as the matching field.
- Open Airtable Upsert and set Operation to
upsert. - Select your Base and Table to replace the
[YOUR_ID]placeholders. - Map Columns to the provided expressions: Email
={{ $json.Email }}, Rating={{ $json.Rating }}, Website={{ $json.Website }}, Category={{ $json['Company Category'] }}, Location={{ $json.Address }}, Phone Number={{ $json['Phone Number'] }}, Business Name={{ $json['Company Name'] }}, and Other Categories={{ $json['Other Categories'].join() }}. - Set Matching Columns to
Email. - Credential Required: Connect your airtableTokenApi credentials in Airtable Upsert.
Step 7: Test and Activate Your Workflow
Validate that the workflow runs end-to-end and sends records to Airtable correctly.
- Click Execute Workflow and submit the form from Form Submission Trigger with a sample Keyword and Location.
- Confirm Retrieve Dataset Records outputs items and Select Target Fields shows mapped fields.
- Verify Extract Email Data outputs a valid email and Filter Valid Emails passes only non-
N/Avalues. - Check Airtable Upsert for successful upserts in your target table.
- Toggle the workflow to Active for production use.
Common Gotchas
- Airtable credentials can expire or need specific permissions. If things break, check your Airtable personal access token scopes and the base/table access 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.
- Apify task settings matter more than most people expect. If you get thin results, verify the actor input (keyword, location format) and confirm the dataset is actually being populated before n8n tries to read it.
Frequently Asked Questions
About 30 minutes if your Apify and Airtable accounts are ready.
No. You’ll mostly connect credentials and map a few fields. The only “code” parts are already built into 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 Apify usage costs based on how many Google Maps results you scrape.
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, but you’ll swap the storage step. Replace the Airtable Upsert node with a Google Sheets append/upsert action, then update the “Finalize Field Set” mapping so column names match your sheet. Common tweaks include saving extra Apify fields (ratings, categories), changing the email rule to capture more than one email, and adjusting batch size when you’re pulling larger lists.
Most of the time it’s the token or scopes. Regenerate your Airtable personal access token, make sure it can read/write to the base you selected, then reselect the base/table in the Airtable node so n8n refreshes its schema. Also check your unique field choice for upsert, because an empty or mismatched key can look like a “connection” issue when it’s really a mapping issue.
Practically, it can handle hundreds per run, but your speed will be limited by the built-in waits (that’s intentional) and by website response times.
For scraping-style lead collection, usually yes. Zapier and Make are great for simple “app to app” syncing, but they’re not as comfortable with multi-step looping, HTML fetching, randomized waits, and filtering logic in one scenario. n8n is also easier to self-host, which matters when you’re running lots of executions and don’t want every step billed. The tradeoff is setup effort: you’ll spend a bit more time dialing in Apify inputs and field mappings. If you want a quick recommendation for your exact volume and tools, Talk to an automation expert.
Once this is running, lead collection stops being a research task and becomes a repeatable system. The workflow handles the tedious parts, and you get to focus on outreach that actually books calls.
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.