Google Maps Lead Cleaner – Dedupe, Email MX & Phone Format
Pricing
from $0.40 / 1,000 cleaned leads
Google Maps Lead Cleaner – Dedupe, Email MX & Phone Format
Clean Google Maps and scraped lead lists before CRM import: dedupe, E.164 phones, email syntax + domain MX check, role/free-mail/disposable flags, website check and a lead score. No SMTP probing. $0.60 per 1,000 leads; duplicates and dropped rows are free.
Pricing
from $0.40 / 1,000 cleaned leads
Rating
0.0
(0)
Developer
Chris Elder
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Share
Google Maps Lead Cleaner turns a raw Google Maps (or any scraped) lead list into a clean, CRM-ready table. It removes duplicates, formats every phone number to E.164, splits and checks emails (syntax + domain MX records), flags role, free-mail and disposable addresses, checks each website is up, and gives every lead a 0–100 quality score with a plain list of issues.
Store page: apify.com/chris_elder/google-maps-lead-cleaner
✅ Pay per result: $0.60 per 1,000 cleaned leads · 🆓 Merged duplicates, dropped rows and input errors are never charged · 🔑 No API key, no sign-ups
▶ Try it with one click · 📄 Real example output · 💲 Pricing · 🔗 Run it after your Google Maps scraper · 🔌 API & integrations · ❓ FAQ · 🛟 Support
What does Google Maps Lead Cleaner do?
- 🧹 Removes duplicates: by Google
placeId, else website (domain + page path), else phone, else name + address. Contacts from the duplicates are merged into the row it keeps, so nothing is lost. - 📞 Formats phones to E.164 (
+442077308191) using each row's country or your default country. You also get national format, phone type (mobile, fixed line, toll-free) and a list of numbers that failed validation. Cells holding several numbers are split. - 📧 Cleans emails: splits lists, lowercases, fixes
name @domain.comtypos, stripsmailto:and checks the syntax. Junk such aslogo@2x.pngoruser@domain.comis flagged. - 🌐 Checks the email domain's MX records once per domain (cached), so you know the domain is set up to receive mail.
- 🏷️ Flags role addresses (info@, sales@, reservations@), free-mail (gmail, outlook…) and disposable domains, plus emails whose domain differs from the website.
- 🔎 Checks websites with one light request each: alive or not, HTTP status, final URL after redirects, and whether it redirects to another domain.
- ⭐ Scores each lead 0–100 (high / medium / low) and lists every problem in
issues, so you can sort, filter or drop weak rows. - 🔗 Plugs straight into your scraper: reads an Apify dataset, pasted JSON or a CSV link, and runs automatically after a Google Maps scraper run as an integration.
What it does not do: it does not open an SMTP connection or probe mailboxes, so it never claims an address is "verified deliverable". valid-mx means the syntax is valid and the domain publishes MX records. Mailbox-level checks are unreliable from cloud IPs and can hurt your sender reputation, so they are deliberately left out.
Use cases
- Agencies and freelancers selling lead lists: send clients a deduplicated, consistently formatted sheet instead of a raw scrape.
- Sales teams importing into HubSpot, Pipedrive or Salesforce: E.164 phones and one best email per row import cleanly, without duplicate contacts.
- Cold-email operators: drop addresses whose domain has no MX before they bounce, and send only the cleaned
primaryEmaillist to a paid mailbox verifier if you use one. - Local-business researchers: find closed or broken websites (
website-unreachable,website-page-not-found) and listings with no way to contact them. - Data teams: one consistent schema, whichever Google Maps or contact-extractor Actor produced the data.
How to use it
- Click Try for free. The prefill holds 8 real public hotel listings (from OpenStreetMap, shaped like Google Maps scraper output). They include one duplicate, an email typed with a space before the @, and a domain without MX. A run takes a few seconds and costs about $0.004.
- To clean your own data, put your Google Maps scraper run's Dataset ID in
datasetId(Storage tab of the run). You can also paste rows intoitems, or give a public CSV link incsvUrl. - Set
defaultCountry(e.g.US) if your rows have nocountryCodeand phones are in national format. - Download the cleaned leads as CSV, Excel or JSON from the run's Storage tab. Pick the CRM import (flat) view for a narrow sheet.
RUN_SUMMARYin the key-value store has the totals.
Input example
{"datasetId": "YOUR_GOOGLE_MAPS_RUN_DATASET_ID","defaultCountry": "GB","dedupeMode": "auto","checkMx": true,"checkWebsites": true,"dropRowsWithoutContact": false}
Other options:
items: paste a JSON array instead ofdatasetId.csvUrl: a CSV or JSON file link. Apify exports withemails/0style columns are understood.fieldMapping: e.g.{"name": "Company", "phone": "Tel"}, only needed for unusual column names.dedupeMode:auto: one row per place (the default).company: one row per website domain, so chain branches are merged.none: keep every row.
minLeadQuality: drop rows below this score.passthroughFields: extra source fields to keep. The default keeps the category, rating, review count and Maps URL.includeEmailDetails,includeOriginal,websiteTimeoutSecs,maxRows.
Fields detected automatically include those from compass/crawler-google-places (title, website, phone, phoneUnformatted, emails, address, city, countryCode, placeId), from Google Maps contact-detail extractors (emails, phones), from Website Contact Extractor, and common CSV headers (Name, Company, Email, Phone Number…).
Output example (from a real public run)
Input: 149 real hotel listings from OpenStreetMap (© OpenStreetMap contributors, ODbL) in London and Manhattan, shaped like a Google Maps scraper export. Two overlapping London searches give real duplicates, the way overlapping Maps searches do. Named-person and personal webmail addresses were removed from the sample before the run. The run read 149 rows and returned 133 cleaned leads in about 25 seconds at 512 MB:
- 16 duplicates merged.
- 127 emails with MX records.
- 6 domains with no MX (A record only).
- 128 phones in E.164.
- 10 websites down or broken.
Public data (no login needed): input dataset · cleaned leads JSON · cleaned leads CSV · RUN_SUMMARY · console run
First dataset item (the second London search had the same hotel again, so duplicatesMerged is 1):
{"name": "DoubleTree by Hilton Hotel London - Marble Arch","website": "https://www.hilton.com/en/hotels/lonmadi-doubletree-london-marble-arch/","websiteDomain": "hilton.com","primaryEmail": "doubletreelondonmarblearch.reservations@hilton.com","primaryEmailStatus": "valid-mx","primaryEmailIsRole": true,"emails": ["doubletreelondonmarblearch.reservations@hilton.com"],"phone": "+442079352361","phoneNational": "020 7935 2361","phoneType": "FIXED_LINE","address": "4 Bryanston Street, London, W1H 7BY","countryCode": "GB","websiteAlive": true,"websiteStatus": 200,"leadQuality": 95,"leadQualityLabel": "high","issues": ["email-role-address-only"],"duplicatesMerged": 1,"emailDetails": [{ "email": "doubletreelondonmarblearch.reservations@hilton.com", "status": "valid-mx", "role": true, "freeMail": false, "disposable": false, "matchesWebsite": true, "mxHosts": ["hilton-com.mail.protection.outlook.com"] }],"categoryName": "Hotel",...}
The source website URL carried a ?WT.mc_id=… tracking parameter, which was removed. Two more real rows from the same run:
"NYCTS_HOTEL @hilton.com"(typed with a space) becamenycts_hotel@hilton.com, statusvalid-mx, issueemail-repaired.reservations@thetrafalgar.com: the domain publishes no MX record, so the status isno-mx-a-record, issues areno-usable-emailandemail-domain-a-record-only, and the score is 60 (medium).
| Field | Meaning |
|---|---|
primaryEmail / primaryEmailStatus | Best email: one with MX, preferring a named person, the website's own domain, and not free-mail |
emails | All usable emails (syntax OK, domain has MX, not disposable or junk) |
emailDetails[].status | valid-mx, no-mx, no-mx-a-record, invalid-syntax, disposable, placeholder, unknown (DNS failed), not-checked (MX check off) |
phone / phones | E.164 primary and all valid numbers; invalidPhones holds the numbers that failed |
websiteAlive / websiteStatus / websiteFinalUrl | Website check result; sites that block bots (401/403/429) count as alive |
leadQuality / leadQualityLabel | 0–100 score: usable email 40 (+5 named person, +5 same domain as website), valid phone 25, live website 15, address 5, name 5 |
issues | Everything worth knowing, e.g. no-contact-method, email-domain-no-mx, phone-invalid, website-unreachable, website-is-social-or-directory |
duplicatesMerged / mergedRowNumbers | How many input rows were folded into this one, and which |
📊 Want these results in Google Sheets? Send them straight to a Sheet with Google Sheets Sync. Run it after this Actor, or add it on this Actor's Integrations tab so every run exports automatically.
How much does it cost to clean a Google Maps lead list?
| Event | Free / Bronze | Silver | Gold+ |
|---|---|---|---|
| Actor start (once per run, per GB of memory; default 512 MB = 1) | $0.00005 | $0.00005 | $0.00005 |
| Cleaned lead (one row written to the dataset; MX, phone and website checks included) | $0.0006 | $0.0005 | $0.0004 |
Never charged: duplicates merged into another row, rows removed by dropRowsWithoutContact or minLeadQuality, and runs that stop on an input error. Apify's automatic $0.00005 start event still applies to those runs.
| Job | Arithmetic | Approx. cost |
|---|---|---|
| One-click prefill (8 rows → 7 leads) | 7 × $0.0006 + $0.00005 | $0.0043 |
| Real example above (149 rows → 133 leads) | 133 × $0.0006 + $0.00005 | $0.080 |
| Typical Maps export: 1,000 rows, ~10% duplicates | 900 × $0.0006 + $0.00005 | $0.54 |
| Large job on Gold: 10,000 rows → 9,000 leads, rows without contact dropped first | 9,000 × $0.0004 + $0.00005 | $3.60 or less |
Set Max cost per run to cap spend. When the cap is reached the Actor stops writing rows and sets budgetHit: true in RUN_SUMMARY. For comparison, mailbox-level email verifiers on the Store charge roughly $0.80–$1.50 per 1,000 emails at Bronze for the email alone. Clean and dedupe here first, and you send them fewer addresses.
Use it as an integration after a Google Maps scraper run
Clean every scrape automatically, with no code:
- Open your Google Maps scraper (or a saved task of it) → Integrations tab → Add integration → search Google Maps Lead Cleaner.
- Start when: Run succeeded.
- Input: set
datasetIdto{{resource.defaultDatasetId}}. Apify swaps in the triggering run's dataset and gives this Actor read access to that dataset only. AdddefaultCountryand any other options. - Click Test with a past run, then save.
{"datasetId": "{{resource.defaultDatasetId}}","defaultCountry": "US","dedupeMode": "auto","dropRowsWithoutContact": true}
RUN_SUMMARY.triggeredBy records which run started the clean-up. This path was tested end to end: a real Apify integration webhook started this Actor after another Actor's run finished, and the Actor read and cleaned that run's 30 rows. The Actor uses Apify's limited permissions, so it can read only the dataset passed in datasetId. Chain Google Sheets Sync after it to land the clean list in a Sheet.
Integrations (API, AI agents, Make, Zapier)
curl -X POST "https://api.apify.com/v2/acts/chris_elder~google-maps-lead-cleaner/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"datasetId":"YOUR_DATASET_ID","defaultCountry":"US","dropRowsWithoutContact":true}'
Python (pip install apify-client):
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")maps_run = client.actor("compass/crawler-google-places").call(run_input={"searchStringsArray": ["dentist"], "locationQuery": "Leeds, UK", "maxCrawledPlacesPerSearch": 50})run = client.actor("chris_elder/google-maps-lead-cleaner").call(run_input={"datasetId": maps_run["defaultDatasetId"], "defaultCountry": "GB"})for lead in client.dataset(run["defaultDatasetId"]).iterate_items():print(lead["name"], lead["primaryEmail"], lead["phone"], lead["leadQuality"])
- MCP:
https://mcp.apify.com/?tools=chris_elder/google-maps-lead-cleaner - Make / Zapier / n8n: module Run Actor →
chris_elder/google-maps-lead-cleaner, passing the previous run'sdefaultDatasetIdasdatasetId - Schedules: run your scraper on a schedule and add this Actor as its integration (see above)
Limits and good to know
- No mailbox verification.
valid-mxmeans valid syntax plus a domain with MX records. The mailbox itself may still not exist. Catch-all domains accept everything, so nobody can fully verify them. no-mx-a-recordmeans the domain has no MX record but has a web server address. Some mail servers will still try to deliver there, but treat these addresses as risky.- Role addresses (info@, sales@) are flagged, not removed. For local businesses they are often the only public address.
- Disposable domains come from the community-maintained CC0 list disposable-email-domains. A mailbox on the business's own website domain is never flagged as disposable.
- The website check sends one HEAD request per unique site (GET if HEAD is refused), 10 s timeout, 10 at a time. Pages behind bot protection return 401/403/429; they count as alive and get the
website-blocks-botsissue. autodedupe keeps chain branches apart when each has its own page on a shared website (e.g. hilton.com/…/branch). Usecompanyfor one row per website domain. Social and directory links (facebook.com, linktr.ee, yelp…) are never merged by domain.- Speed from the real run above: 149 rows in about 25 s at 512 MB, including 111 MX lookups and 133 website checks. Turning off
checkWebsitesmakes large lists much faster. - Default
maxRowsis 100,000 per run. For bigger lists raise memory, or run per city or keyword.
Is it legal?
The Actor processes only data you provide. It makes public DNS lookups for email domains and one light request to each business website. It does not scrape Google, log in anywhere or contact any mailbox. Lead lists can contain personal data (for example a named person's work email). You are responsible for having a lawful basis under GDPR / UK GDPR, and for following PECR, CAN-SPAM and similar rules when you contact anyone. See Apify's guide to the legality of web scraping.
FAQ
Does this verify that an email address exists?
No. It checks syntax, that the domain has MX records, and flags role, free-mail, disposable and junk addresses. It does not probe mailboxes over SMTP and never labels an address "verified deliverable". If you need mailbox-level checks, run a dedicated verifier on the cleaned primaryEmail column afterwards.
Which Google Maps scrapers does it work with?
Any that write to an Apify dataset or export CSV/JSON. Field names from compass/crawler-google-places and the common "Google Maps with contact details" extractors are detected automatically. For anything else, use fieldMapping.
Why were two branches of the same chain kept as separate rows?
In auto mode the key is the Google placeId, or the full website page (domain + path), so different locations stay separate. Choose dedupeMode: "company" for one row per website domain.
My phones are in national format with no country column. What do I do?
Set defaultCountry (e.g. US, GB, AU). Numbers that already start with + or 00 are read without it.
Am I charged for duplicates or rows I drop?
No. Only rows written to the dataset are charged (lead-cleaned). Merged duplicates, rows removed by dropRowsWithoutContact or minLeadQuality, and input errors are free.
Can I get a narrow CSV for my CRM?
Yes. Choose the CRM import (flat) view in the Storage tab, or set includeEmailDetails: false.
Support, reliability and feature requests
- Failed items are free.
- Every release is tested on real public inputs; the one-click example works.
- Issues answered within 2 business days via the Actor Issues tab.
- Breaking input or price changes are announced in this README first.
Built by GPT Shop Ltd with AI-assisted development; every release is reviewed and tested. Tested on 149 real OpenStreetMap hotel listings (London and Manhattan) read through datasetId, the same data through csvUrl, a real Apify integration webhook run, input-error runs (no input, missing dataset, HTML instead of CSV), and 18 unit tests (dedupe, phone, email, MX and website logic).
More tools from GPT Shop Ltd
- Website Contact Extractor: find emails, phones and social links on company websites, then clean them here
- Google Sheets Sync: send the cleaned leads to a Google Sheet
- Dataset Transformer: filter, rename, flatten and convert datasets
- Bulk File Downloader
- All tools: apify.com/chris_elder