# Google Maps Email Scraper - Pay Only Per Email Delivered (`sourabhbgp/google-maps-enrichment`) Actor

Find business emails from Google Maps by category and location. Every row is a full listing: email, phone, address, opening hours, categories, rating and coordinates. The email matches the business own website domain and is mail-domain checked. Businesses that yield no email are never charged.

- **URL**: https://apify.com/sourabhbgp/google-maps-enrichment.md
- **Developed by:** [Sourabh Kumar](https://apify.com/sourabhbgp) (community)
- **Categories:** Lead generation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 business email delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### Google Maps email scraper: business emails by category and location

Search Google Maps the way you already do, by business type and place, and get back the email addresses. Every address is checked against the business's own website domain before it reaches you.

**$10.00 per 1,000 emails delivered. Businesses that yield no email are never charged.**

### Why this scraper?

- 📧 **You pay per email, not per business.** Most Maps scrapers bill you for every place they crawl, whether or not it produces a contact. Here a business that yields nothing is free.
- 🎯 **The address belongs to the business.** An email only ships if its domain matches the company's own website. No corporate head-office address glued onto a local franchise.
- ✅ **We check the mail domain.** Each address carries a status showing whether its domain can receive mail at all.
- 🔍 **We show our work.** Every row includes the exact page the address was read from, so you can check it yourself in one click.
- 🚫 **No duplicates.** Each business and each address appears once, across every query in the run.
- 📞 **The whole listing comes along.** Phone, address, categories, opening hours, neighborhood, rating, coordinates and timezone, not just an email.
- 🌍 **Works in 26 countries, verified in 11 across six continents.** Results, categories and opening hours come back in the local language.

### What you get

One row per business that produced a usable email:

```json
{
  "email": "info@austindentalworks.com",
  "emailMxStatus": "mx",
  "emailIsRoleAccount": true,
  "emailSourceUrl": "https://www.austindentalworks.com/",
  "name": "Austin Dental Works",
  "phone": "(512) 877-9822",
  "phoneUnformatted": "+15128779822",
  "website": "https://www.austindentalworks.com/",
  "address": "4601 N Lamar Blvd #503, Austin, TX 78751",
  "fullAddress": "Austin Dental Works, 4601 N Lamar Blvd #503, Austin, TX 78751",
  "neighborhood": "Rosedale",
  "countryCode": "US",
  "timezone": "America/Chicago",
  "categories": ["Dentist", "Cosmetic dentist", "Dental clinic"],
  "description": null,
  "openingHours": [
    { "day": "Monday", "hours": "8 AM–5 PM" },
    { "day": "Saturday", "hours": "Closed" }
  ],
  "rating": 4.9,
  "latitude": 30.3150192,
  "longitude": -97.7355807,
  "websiteDomain": "austindentalworks.com",
  "additionalEmails": [],
  "placeId": "0x8644ca638c284765:0x76576315d12cc3cd",
  "googlePlaceId": "ChIJZUcojGPKRIYRzcMs0RVjV3Y",
  "searchString": "dentist in Austin TX",
  "scrapedAt": "2026-08-04T14:38:22.096Z"
}
```

You get the full business listing, not just the address. `openingHours` and `categories` come back in
the language you searched in, so a German search returns `Dienstag` and `Zahnarzt`. `googlePlaceId` is
the canonical `ChIJ...` identifier, which is the one other Google tools expect.

`description` is Google's own short blurb, for example "Gastropub with focus on pork". Google only
writes these for some kinds of business: hotels and restaurants usually have one, dentists and
plumbers almost never do, and Google sometimes leaves it out of a response entirely. Treat null as
"not provided this time" rather than "this business has none".

When a site exposes several addresses, the most personal-looking one becomes `email` and the rest ride along in `additionalEmails`, free:

```json
{
  "email": "emma@tyacklaw.com",
  "emailIsRoleAccount": false,
  "additionalEmails": [
    { "email": "laura@tyacklaw.com", "mxStatus": "mx", "isRoleAccount": false, "sourceUrl": "https://tyacklaw.com/contact/" },
    { "email": "info@tyacklaw.com", "mxStatus": "mx", "isRoleAccount": true, "sourceUrl": "https://tyacklaw.com/contact/" }
  ]
}
```

### Understanding the email quality fields

| field | what it means |
|---|---|
| `emailMxStatus` | `mx` means the domain publishes a mail record and is set up to receive email. `a-only` means it has no mail record but still resolves, so mail may still be accepted. |
| `emailIsRoleAccount` | `true` for shared addresses like `info@`, `contact@` and `sales@`. Roughly half of business emails on the web are these. Perfectly usable for business outreach, but now you know which is which. |
| `emailSourceUrl` | The page the address was read from. |
| `websiteDomain` | The email's domain, always matching the business website. |

**On the word "verified":** we do not use it. Checking a domain is not the same as proving a specific mailbox exists, and we will not claim otherwise. What you get is an address published on the company's own site, on the company's own domain, whose domain can receive mail. If you need mailbox-level proof, run the output through a dedicated validation service.

### Getting started

1. Enter one or more searches, like `dentist in Austin TX` or `plumber in Phoenix AZ`.
2. Set how many emails you want.
3. Run it and export to JSON, CSV, or Excel.

```json
{
  "searchStrings": ["dentist in Austin TX", "plumber in Phoenix AZ"],
  "maxResults": 80,
  "deepCrawl": true,
  "requireMailDomain": true,
  "excludeRoleAccounts": false,
  "countryCode": "us",
  "languageCode": "en"
}
```

### Input options

| option | what it does |
|---|---|
| `searchStrings` | Your queries, one per line. Narrow beats broad: `italian restaurant in Brooklyn` returns better matched businesses than `restaurant in New York`. |
| `maxResults` | How many emails you want. This counts delivered emails, not businesses visited. |
| `maxPlacesCrawled` | Safety ceiling on businesses visited, so a low yield search cannot run on forever. Defaults to ten times your email target. |
| `deepCrawl` | Also reads the contact and about pages a site links to, not just the homepage. Finds noticeably more emails. On by default. |
| `requireMailDomain` | Only deliver addresses whose domain can receive mail. On by default. |
| `excludeRoleAccounts` | Drop `info@` style addresses. Off by default, because turning it on cuts your results roughly in half. |
| `countryCode`, `languageCode` | Which country and language to search. |

### Use cases

- **Local agency prospecting.** Pull every dentist, plumber, or law firm in a metro with a contactable address attached.
- **Cold outreach lists.** Feed straight into your sequencer, with the role-account flag letting you write differently to `info@` than to a named person.
- **Territory research.** Category plus rating plus coordinates tells you where the good operators cluster before you commit to a market.
- **CRM enrichment.** `phoneUnformatted` is already in the format your CRM wants.

### How many emails will I actually get?

Not every business has a website, and not every website publishes an address. In measured runs across US metros, roughly **3 to 4 businesses are visited per email delivered**, and yield lands between **25 and 39 emails per 100 businesses**.

Category matters more than anything else here. Law firms and dentists sit at the top of that range because professional services publish contact details openly. Restaurants, gyms and coffee shops sit lower, since many route everything through social media instead. Treat the low end as your planning number for a category you have not tried before.

You are only charged for what arrives, so a low yield category costs you less, not more.

### How much does this cost?

**$10.00 per 1,000 emails delivered**, plus $0.005 per run start.

- Apify's $5 free plan credit gets you about **500 emails**.
- The $29 Starter plan gets you about **2,900 emails**.

There is no charge for businesses visited that produce nothing, no charge for pages read, and no separate fee for the mail-domain check.

### Limitations

- **Only businesses with a website can produce an email.** About 80% of places have one. The rest are skipped, and never billed.
- **Search depth is limited by Google.** A single query returns a few hundred businesses, not thousands. To go deeper, split by neighbourhood, suburb, or a narrower category rather than raising the limit on one broad query.
- **Role addresses are common.** Around half of what you get will be `info@` or similar. Use `excludeRoleAccounts` if you only want named people, and expect a much smaller list.
- **We do not test individual mailboxes.** See the note above on the word "verified".
- **Yield is measured most thoroughly in the United States.** Eleven countries were tested end to end across six continents and all returned local results, but the hit rate varies by market and by how openly businesses there publish contact details. Treat non-US volumes as less predictable than US ones.

### FAQ

#### Is it legal to scrape Google Maps?

Scraping publicly available data is generally legal. Business contact details published openly on a company's own website are about as public as data gets. You are still responsible for how you use it: check the marketing and privacy rules that apply where you and your recipients are, including GDPR in Europe and CAN-SPAM in the US.

#### Can I use this as an API?

Yes. Run it through the Apify API from any language, or use the `apify-client` packages for Node.js and Python. Schedule it, chain it, or trigger it from a webhook.

#### Can I integrate it with my other tools?

Yes. It connects to Zapier, Make, Slack, Google Sheets, Airbyte, GitHub, and most other services through Apify integrations, plus webhooks for run events.

#### Why did I get fewer emails than I asked for?

Because a search ran out of businesses before it ran out of budget. The run's status message always says which happened, and how many businesses were visited. Try a broader area or extra queries.

#### Your feedback

Found a bug, or want a field that is not here? Open an issue at [the Actor's Issues tab](https://console.apify.com/actors/ogpPcaBh4BCniFKyV/issues). Feature requests get read by a human.

# Actor input Schema

## `searchStrings` (type: `array`):

What to look for, one query per line, in the form "<business type> in <place>". Narrow queries return better matched businesses than broad ones.

## `maxResults` (type: `integer`):

How many business emails you want. This counts DELIVERED EMAILS, not businesses looked at. Roughly 4 businesses are checked per email found, so 100 emails means about 400 businesses are visited. You are only charged for the emails you receive. If you omit this field entirely, the platform uses 30.

## `maxPlacesCrawled` (type: `integer`):

Safety ceiling on how many businesses are visited while looking for emails. Stops a low yield search from running on and on. Leave empty for 10 times your email target.

## `requireMailDomain` (type: `boolean`):

Checks that the email's domain publishes a mail record (MX), or at minimum resolves. This is a domain level check, not mailbox verification: we never probe mail servers to test individual addresses.

## `excludeRoleAccounts` (type: `boolean`):

Drops addresses starting with a known shared inbox word such as info, contact, hello, admin, office, sales, support, mail, team, reception, booking, service, help, general or orders. This is a fixed word list, not a test for real human names, so shared addresses like webmaster@, marketing@ or hr@ still come through. About half of business emails on the web are role addresses, so turning this on will substantially reduce your result count. Off if you omit this field.

## `deepCrawl` (type: `boolean`):

Follows the contact and about pages a website links to, not just its homepage. Finds noticeably more emails. Turn off for a faster, cheaper, shallower pass.

## `countryCode` (type: `string`):

Two letter country code used for the Google Maps search.

## `languageCode` (type: `string`):

Two letter language code for the Google Maps results.

## `proxyConfiguration` (type: `object`):

Leave the default unless you have a reason to change it. Datacenter proxies are enough for this Actor.

## Actor input object example

```json
{
  "searchStrings": [
    "dentist in Austin TX",
    "plumber in Phoenix AZ"
  ],
  "maxResults": 30,
  "requireMailDomain": true,
  "excludeRoleAccounts": false,
  "deepCrawl": true,
  "countryCode": "us",
  "languageCode": "en",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

View the delivered business emails in the dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchStrings": [
        "dentist in Austin TX"
    ],
    "maxResults": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("sourabhbgp/google-maps-enrichment").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchStrings": ["dentist in Austin TX"],
    "maxResults": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("sourabhbgp/google-maps-enrichment").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchStrings": [
    "dentist in Austin TX"
  ],
  "maxResults": 30
}' |
apify call sourabhbgp/google-maps-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sourabhbgp/google-maps-enrichment"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ogpPcaBh4BCniFKyV/builds/65ogj6T7mU7HVFkdo/openapi.json
