Google Maps Email Extractor (MX-Checked) avatar

Google Maps Email Extractor (MX-Checked)

Pricing

from $3.00 / 1,000 leads

Go to Apify Store
Google Maps Email Extractor (MX-Checked)

Google Maps Email Extractor (MX-Checked)

Extract business emails, phones, websites, owner names and social links from Google Maps places by keyword and location. Every email is MX-checked. No login, no browser, pay per lead. Export CSV or JSON, run via API, schedule runs or integrate with other tools.

Pricing

from $3.00 / 1,000 leads

Rating

0.0

(0)

Developer

Mohammed Samaan

Mohammed Samaan

Maintained by Community

Actor stats

0

Bookmarked

95

Total users

10

Monthly active users

5 days ago

Last modified

Features

Limited permissions

Share

Google Maps Email Leads Scraper — MX-Checked Business Emails

Find local businesses on Google Maps and get back a checked business email — not just a scraped address, one whose domain is confirmed to accept mail (MX check) and tagged with its MX status (valid, risky-role or invalid) — plus phone, website, rating, address, coordinates, opening hours and social links, as one clean CSV/JSON row per business.

Already have Google Maps data? Pick that dataset in placesDatasetId (from any Google Maps scraper) and this Actor skips the Maps step entirely — you pay only for our enrichment (email, contact name, socials, website signals) on the places that have a website, not for the Maps scrape again.

Two ways to feed it:

  1. Search — give it search terms + a location and it pulls the places from Google Maps through a maintained Maps actor, then enriches them.
  2. Enrich a dataset you already have — pick any Google Maps scraper run from your account (placesDatasetId) and it skips the Maps step entirely, visiting each business website and adding checked emails and socials. No second Maps bill.

The enrichment step uses no browser and no proxies: plain HTTP requests to each business's own home / contact / about pages, then a DNS MX lookup per domain.

Who it's for

  • Cold-email agencies and SMMAs building local-business lead lists
  • SDR teams tired of cleaning scraped lists before they can send
  • Anyone pitching local businesses (web design, marketing, SaaS) who needs a contact email, not just a name and a phone number
  • Teams that already scrape Google Maps and just want the email layer added

What you get

FieldWhat it is
name / categoryBusiness name and Google Maps category
emailBest available email for the business
emailStatusvalid (the domain has MX records and the address is not a generic role inbox) / risky-role (the domain has MX records and the address starts with a generic role such as info@) / invalid (no MX records found, or the lookup timed out)
emailSourceWhich page the email was found on
emailPickConfidence / contactName / contactRoleOnly filled when the optional AI step runs (see Data handling); null otherwise
phone / websiteContact details
rating / reviewsGoogle rating and review count
address / city / country / lat / lngLocation
placeId / mapsUrlGoogle Maps identifiers (when the Maps data has them)
openingHoursFlattened hours string
facebook / instagram / linkedin / twitter / youtube / tiktokSocial profile links found on the site
allEmails / emailCountEvery email found, each tagged with its status
searchTerm / scrapedAtProvenance

Example output

One real row from a search run (digital marketing agency, Australia). openingHours is shortened here.

{
"name": "Potent",
"category": "Marketing agency",
"email": "enquiries@potent.com.au",
"emailStatus": "risky-role",
"emailSource": "https://www.potent.com.au",
"emailPickConfidence": 0.93,
"contactName": null,
"contactRole": null,
"phone": "+61 1300 853 557",
"website": "https://www.potent.com.au/",
"rating": 4.9,
"reviews": 50,
"address": "Suite 409/102 Werribee St N, Werribee VIC 3030, Australia",
"city": "Werribee",
"country": "AU",
"state": "Victoria",
"postalCode": "3030",
"categories": "Marketing agency; Advertising agency; Graphic designer; Internet marketing service; Web hosting company; Website designer",
"priceLevel": null,
"permanentlyClosed": false,
"lat": -37.9012655,
"lng": 144.6534986,
"placeId": "ChIJP4IEKqaF1moRE4umP2CfeJ0",
"mapsUrl": "https://www.google.com/maps/search/?api=1&query=Potent&query_place_id=ChIJP4IEKqaF1moRE4umP2CfeJ0",
"openingHours": "Monday: 8:30 AM to 4 PM | Tuesday: 8:30 AM to 4 PM | … (shortened)",
"facebook": "https://www.facebook.com/potentweb",
"instagram": "https://www.instagram.com/potentweb",
"linkedin": "https://au.linkedin.com/company/potent-web",
"twitter": null,
"youtube": null,
"tiktok": null,
"allEmails": "enquiries@potent.com.au (risky-role); updates@potent.com.au (valid)",
"emailCount": 2,
"siteHttps": true,
"siteMobileFriendly": true,
"siteCopyrightYear": 2026,
"siteLooksOutdated": false,
"siteHasAnalytics": true,
"siteHasFacebookPixel": false,
"siteCms": null,
"searchTerm": "digital marketing agency",
"scrapedAt": "2026-10-04T16:21:41.933Z"
}

emailStatus is an MX check only: valid means the domain can receive mail, not that the mailbox exists.

Input

{
"searchTerms": ["dentist", "orthodontist"],
"location": "Austin, TX",
"maxResults": 50,
"verifyEmails": true,
"onlyWithEmail": true,
"maxEmailsPerSite": 5
}

Or, to enrich an existing Google Maps dataset (skips the Maps search):

{
"placesDatasetId": "<pick one of your datasets>",
"verifyEmails": true,
"onlyWithEmail": true
}
  • searchTerms — business types to search; location — a city or area.
  • maxResults — places per search term.
  • verifyEmails (on) — the MX check that tags each email valid / risky-role / invalid.
  • onlyWithEmail (on) — only businesses with an email are delivered and billed.
  • placesDatasetId — any dataset whose rows have a website field (compass, lukaskrivka, or this actor's own output all work).

Pricing — read this before your first run

This actor is pay-per-result: one charge per row delivered, and with onlyWithEmail on (the default) only rows with an email are delivered. The per-row price is on the Pricing tab.

In search mode the Google Maps step is a separate actor with its own price. This actor calls compass/crawler-google-places in your account, and Apify bills that run to you directly at that actor's rate (about $4 per 1,000 places on the free plan, less on paid plans) plus its compute. A run that finds 1,000 places and 350 emails therefore costs roughly the Maps actor's price for 1,000 places + this actor's price for 350 rows.

If you already have Google Maps data, use placesDatasetId and pay only for the enrichment rows.

How it compares

Most Google Maps scrapers hand you a raw list; every email here is MX-checked and tagged with an MX status (valid, risky-role or invalid) before it reaches your dataset, and social links are pulled from the business site at no extra step. What this actor does not do: mailbox-level (SMTP) verification or catch-all detection — an MX check confirms the domain accepts mail, not that the exact inbox exists. Run the output through the Bulk Email Verifier if you need that.

Use with n8n / Make / Zapier / MCP

Add an Apify integration node, point it at this actor, run it with your input, then read the finished dataset back into your workflow — feed the rows straight into your CRM or outreach tool. Every input has an example value, so an AI agent can call it via the Apify MCP server without guidance.

Errors and limits

How the actor behaves when things go wrong (read from its code):

  • No usable input. In search mode, an empty searchTerms or location fails the run with "Provide searchTerms + location, or pick a Google Maps dataset in placesDatasetId". A placesDatasetId that is empty or unreadable fails the run with a message saying the dataset needs a website field per row.
  • Google Maps step fails. In search mode the actor starts compass/crawler-google-places in your account (30-minute timeout). A failed start is retried once after 5 seconds. If your Apify account is already at its concurrent-run limit, the actor waits and retries for about 3 minutes before failing with a message telling you to wait or use placesDatasetId. If the Maps run ends in any status other than SUCCEEDED (for example out of credits) or returns no dataset, this actor fails with that status and the run id. The Maps run is billed to your account by its own actor, whether or not this actor then delivers rows.
  • Slow, blocked or broken business sites. Each place's site is checked at /, /contact, /contact-us and /about in parallel; each page has an 8-second timeout and a 1.5 MB size cap. Any HTTP error, timeout, block (403) or non-text response yields no content for that page, silently. One place gets at most 45 seconds in total; after that it is skipped (with a warning in the log) and the run continues. Social-media, Google, Yelp, delivery-app and link-in-bio websites are not crawled. There are no proxies and no retries, and JavaScript-only pages are read as static HTML.
  • Places with no website or no email. With onlyWithEmail on (the default), places where no email was found are not delivered and not charged. Turn it off to also get rows without an email, which are charged like any other row. A repeated name-and-website pair is processed once.
  • Email status. verifyEmails is a DNS MX lookup (5-second timeout): invalid = no mail server found for the domain, risky-role = generic inbox such as info@, valid = the domain accepts mail and the address is not a generic role. Mailbox-level (SMTP) verification and catch-all detection are not done. An address whose domain MX lookup times out is marked invalid.
  • One bad lead does not stop the run. If enriching or writing a single lead fails, a warning is logged and the run continues with the next one.
  • Charging. One charge per row delivered to the dataset (the paid event is apify-default-dataset-item), applied when the row is written. A run that fails before any row is written delivers and charges nothing from this actor. If your max-charge limit is reached the run stops early with what it has. With placesDatasetId, at most 5,000 places are enriched per run.

FAQ

Does it need my own Google Maps API key? No. In search mode it runs a maintained Google Maps actor in your account; that actor's usage is billed to you by Apify separately from this actor's per-row price (see Pricing above). In dataset mode there is no Maps step at all.

What does "risky-role" mean? The domain accepts mail but the address is a generic inbox like info@ or sales@ (the actor tests only the start of the address, not whether a person owns it) — still worth having, just a lower reply rate.

Why is email sometimes null? Not every business site has a discoverable email on its home / contact / about pages, or the domain has no mail servers. With onlyWithEmail off those businesses still come through with phone, website, rating and socials.

Can I run it against a whole country? Run it per city — Google Maps results are location-bound, and splitting by city keeps runs fast and easy to re-run.

Does it respect site terms? It only visits each business's own public site pages. You are responsible for lawful use of the contact data you collect.

Send results to Notion (optional)

Authorize Notion once in Apify Console → Settings → API & Integrations → MCP connectors (choose Notion, sign in). Then pick that connector in Notion connector and paste the link of any Notion page in Notion page to create the table in. The first run creates a table inside that page and fills it with the Maps leads (up to 500 rows per run; the full set is always in the dataset). To keep adding to the same table on later runs, paste the table ID from the log into Existing Notion table ID. Only these lead fields are sent: business name, category, email, email status, contact name and role, phone, website, city, rating and scrape date. This Actor never sees your Notion login: Apify's connector proxy holds it. If the Notion step fails, your dataset results are still delivered and the reason is logged.

Data handling

The email, social and site checks use plain HTTP requests to each business's own website plus DNS lookups. Your data leaves the actor in three ways:

  1. Google Maps step (search mode only). Your search terms, location and maxResults are passed to the Apify actor compass/crawler-google-places, which runs in your account and is billed to you. If you supply placesDatasetId instead, this step is skipped and nothing is sent to it.
  2. Notion export (only if you pick a Notion connector). The lead rows (up to 500) go to your Notion through the Apify MCP connector. Fields sent: business name, category, email, email status, contact name and role, phone, website, city, rating and scrape date.
  3. TypeSafe AI step (only if the publisher's TYPESAFE_API_KEY environment variable is set; it is not an input). Text is sent to the TypeSafe API (api.typesafe.ai, model Jev). If the key is not set, nothing is sent and the AI-filled fields stay null.
TypeSafe stepRuns whenWhat is sent
Pick the main email, and check the site belongs to the businessthe site yielded 2 or more emailsfor each place: business name, category and city (from your search results or dataset), the website URL, and each candidate email with the page it was found on
Pick the owner / contactat least 1 email and at least 1 candidate namethe same name, category, city and URL, plus up to 12 candidate names, each with about 160 characters of surrounding page text

The TypeSafe calls apply to every place enriched, whether it came from search or from your own dataset. They have a 15-second timeout; if one fails the actor ignores it and uses its normal rule, so a TypeSafe outage cannot fail a run. Your search terms and the rest of the page content are not sent to TypeSafe. This README does not cover how TypeSafe handles the data it receives; check their terms before using it on data you may not share.

Use it from an AI agent (MCP)

Add this Actor to any MCP client (Claude, Cursor, VS Code) with one URL:

https://mcp.apify.com?tools=leadharbor/google-maps-verified-email-scraper

Or call it through the Apify API with an input like this:

{
"searchTerms": [
"dentist"
],
"location": "Austin, TX",
"maxResults": 20
}

Returns one row per local business: name, category, MX-checked email, phone, website, address, owner name when found, social links. Results land in the run's default dataset (JSON, CSV or Excel). No login or API key is needed for the sites it reads, and pay-per-event pricing works with Apify's x402 agent payments.

API usage

Run it from your own code with an Apify API token. The input below is this actor's prefilled example.

curl (runs the actor and returns the dataset items):

curl -X POST "https://api.apify.com/v2/acts/leadharbor~google-maps-verified-email-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"searchTerms": [
"dentist"
],
"location": "Austin, TX",
"maxResults": 20,
"onlyWithEmail": true
}'

Python (pip install apify-client):

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("leadharbor/google-maps-verified-email-scraper").call(run_input={
"searchTerms": [
"dentist"
],
"location": "Austin, TX",
"maxResults": 20,
"onlyWithEmail": True
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)

Changelog

28 Sep 2026 — More place fields (state, postal code, all categories, price level); works with a dataset alone; optional Notion output; MCP agent section. Plus 5 public example tasks with their own pages.

More from leadharbor


Using this actor? A quick review helps other buyers find it: google-maps-verified-email-scraper

Site signals and contact name

FieldWhat it is
contactName, contactRoleThe owner / founder / principal when the website clearly names one (e.g. "Dr Adil Sarwar, Founder"). Filled only when the optional AI step is on (see Data handling): code finds candidate names on the site and an AI model only selects among them, it never invents one. null when the site names nobody, when the model is not confident, or when the AI step is off.
siteLooksOutdated, siteCopyrightYearCopyright year in the footer; true when it is 3+ years old — a common sign of a neglected site.
siteMobileFriendlyWhether the homepage declares a mobile viewport.
siteHasAnalytics, siteHasFacebookPixel, siteCmsGoogle Analytics / Tag Manager present, Meta pixel present, and the site builder (wordpress, wix, squarespace, shopify or webflow; null if none is recognised).
siteHttpsWhether the website link starts with https.

These signals are read from the homepage HTML the actor already fetched, with simple text patterns. They are hints, not an audit.

When a site lists several emails, form placeholders (example@mysite.com, you@company.com) and platform addresses are filtered out by code. If the optional AI step is on and two or more emails remain, the model also picks the one that looks like the business's own main inbox (not an agency, platform or jobs address) and emailPickConfidence shows its confidence; below 0.5 the actor falls back to the first valid email, else the first found.

  • Disposable Email & Domain Checker — Flags throwaway and free-provider email domains using 5 merged public lists. $0.30 per 1,000 checks.
  • Domain Trust Check — 0-100 trust score for a domain from domain age, SSL, SPF/DKIM/DMARC and disposable-domain lists. $2.50 per 1,000 domains.
  • Bulk Email Verifier — Verifies email addresses: syntax, MX, disposable and role flags, and a 0-100 score. $0.50 per 1,000.
  • Website Contact Extractor — Pulls emails, phone numbers and social links from website URLs.
  • Website Technology Detector — Detects the CMS, frameworks, analytics and 100+ other technologies a website uses from its homepage. $9 per 1,000 sites.
⌂ 换网址