# Google Maps Emails - Add Verified Emails & Socials to Places (`that_red_bird/google-maps-email-enricher`) Actor

Your Google Maps scraper gives you websites, not emails. Paste its dataset ID and get each business's emails (MX-checked), phones and socials, plus 'no website' and 'dead website' lead flags. No re-scraping. About $2.60 per 1,000 places.

- **URL**: https://apify.com/that_red_bird/google-maps-email-enricher.md
- **Developed by:** [mohamed alaya](https://apify.com/that_red_bird) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 place enricheds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Maps Emails - Add Verified Emails & Socials to Places

Turn your Google Maps scraper results into a ready-to-email lead list.
Pick the dataset -> get one row per business with its best email, phones, socials and a website verdict, in about 3 seconds per business.

Google Maps scrapers give you a business's website, but not its email. This Actor opens each business's own website, reads the homepage, the contact page and the about page, and adds what it finds to the row you already have. No re-scraping of Google Maps.

### What you get

| Field | Meaning |
|---|---|
| `bestEmail` | The inbox most likely to reach the business (same-domain first, then `info@`, `hello@`, `owner@`, `booking@` style addresses) |
| `emailStatus` | `mx-valid` (the domain can receive mail), `risky` (disposable domain or likely typo), `no-mx`, `syntax-invalid`, `unknown` |
| `otherEmails` | Every other address found on the site, deduplicated |
| `emailDetails` | Per email: status, role address (`info@`), free provider (Gmail), disposable, typo suggestion, and the page it was found on |
| `phones` | Phone numbers printed on the site, as printed (not guessed) |
| `facebook`, `instagram`, `linkedin`, `x`, `youtube`, `tiktok` | Social profile links found on the site |
| `contactPageUrl` | The contact page, for businesses that show a form instead of an email |
| `websiteStatus` | `ok`, `no-website`, `dead`, `unreachable`, `social-only`, `website-builder`, `blocked`, `robots-disallowed` |
| `leadSignals` | Plain-words flags for outreach: `no website`, `dead website`, `social-only website`, `website-builder`, `contact form only`, `no email found` |
| `siteBuilder` | Wix, Squarespace, Weebly, Shopify, WordPress or Webflow when it can be told from the page |

Every column of your original row is kept as it was. The enrichment is added next to it.

### 3 ways to use it

1. **Enrich a Maps run you already did.** Pick its dataset in the "Google Maps dataset(s)" picker, leave the rest as it is. Use this for a normal lead list.
2. **Only leads I can email today.** Pick the dataset and turn on "Only places with an email". Places with no email are dropped and not charged.
   `{"onlyWithEmail": true, "maxPlaces": 500}`
3. **Web-design and SEO leads: businesses with no site or a bad one.** Pick the dataset, turn "Check emails" off to save money, then filter the output by `leadSignals` (`no website`, `dead website`, `social-only website`, `website-builder`).
   `{"verifyEmails": false}`

You can also paste rows (`{"title":"Joe's Pizza","website":"https://joespizzanyc.com"}`), a CSV with a header row, or just a list of website URLs.

### Input

| Field | What to enter | Default |
|---|---|---|
| Google Maps dataset(s) | Pick the dataset of any Google Maps scraper run. Use the picker, not a typed ID: it gives this Actor permission to read it | empty (demo list) |
| Places (JSON rows) / CSV / Website URLs | Alternatives to the dataset | empty |
| Max places | Stop after N places (also your spending cap) | 1000 |
| Check emails (MX records) | Checks the domain can receive mail. Adds $0.001 per email whose domain passes | on |
| Only places with an email | Drop places with no email | off |
| Pages read per website | Homepage plus contact/about pages | 4 |
| Respect robots.txt | Honour crawl rules and crawl-delay | on |
| Time limit per website | Seconds before moving on | 40 |

An empty input still works: the Actor then runs on a built-in list of 7 demo businesses (6 real New York websites and 1 made-up business with no website), so you can see the full output before you pay for your own data. Demo rows are free. If you do give a dataset ID and the Actor cannot read it, the run stops with a clear message instead of returning demo rows.

### Output example

```json
{
  "title": "Levain Bakery",
  "website": "https://levainbakery.com",
  "bestEmail": "media@levainbakery.com",
  "emailStatus": "mx-valid",
  "otherEmails": [],
  "phones": ["877-932-2161"],
  "facebook": "https://www.facebook.com/LevainBakery",
  "instagram": "https://www.instagram.com/levainbakery",
  "linkedin": "https://www.linkedin.com/company/levain-bakery",
  "tiktok": "https://www.tiktok.com/@levainbakery",
  "contactPageUrl": "https://levainbakery.com/pages/get-in-touch",
  "websiteStatus": "ok",
  "leadSignals": [],
  "siteBuilder": "Shopify"
}
```

### Pricing (pay per event)

| Event | Price |
|---|---|
| Place enriched | $0.002 |
| Email found and domain-checked | $0.001 per email, up to 5 per place |

- 1,000 places, about 600 with one email -> $2.00 + $0.60 = **$2.60**
- 10,000 places with email checks off -> **$20**
- A place with no website still gets the `no website` flag and costs $0.002.
- Rows you drop with "Only places with an email" and rows that failed on our side are not charged.

### Use it with

- **Before:** any Google Maps scraper -> **this Actor** -> Lead List Deduplicator -> your CRM or Instantly / Lemlist.
- Make, Zapier and n8n: trigger on "run succeeded" after your Maps scraper, send the rows to Google Sheets or HubSpot.
- **AI agents / MCP:** call `that_red_bird/google-maps-email-enricher` with `{"datasets": ["<datasetId>"]}`; the agent gets JSON rows with `bestEmail` and `leadSignals`, ready to rank.

### FAQ

- **Is this legal?** It reads public business websites only. You are responsible for GDPR, CAN-SPAM and local rules when you email people.
- **Why is an email missing?** Many small businesses print only a contact form or a phone number. Those rows get `contact form only` or `no email found`, plus the contact page link. This is common: expect an email on roughly half to two thirds of businesses that have a working website.
- **Does "verified" mean the inbox exists?** No. We check that the domain can receive mail (MX records), that the address is well formed, and whether it is a disposable domain, a role address or a likely typo. We never connect to the mail server and never send mail, so we cannot say a specific inbox exists. `mx-valid` is the honest label.
- **Can I run it every week?** Yes. Schedule it after your Maps scraper.
- **What if a website blocks bots?** The row is returned with `websiteStatus: "blocked"` and charged as a normal place.
- **Does it read Facebook or Instagram pages?** No. If a business's "website" is only a social or link-in-bio page, the row gets `social-only website` and the profile link, which is itself a useful lead signal.

### Limits

- Only addresses printed on the business's own website. No guessed emails, no personal LinkedIn data.
- JavaScript-only sites that show their contact details after loading may come back with no email.
- Big chains behind bot protection (Cloudflare and similar) are marked `blocked`.
- Phone numbers are returned as printed on the site, not converted to one international format.

### Support

Open an issue on the Issues tab, we answer within 24 hours. If this saved you time, a short review helps other people find it.

# Actor input Schema

## `datasets` (type: `array`):

Pick the dataset of any Google Maps scraper run (Compass, Lukas Krivka or others). Use the picker rather than typing an ID: it gives this Actor permission to read your dataset. Leave empty to use the other inputs, or the demo list.

## `places` (type: `array`):

Optional. Paste rows as JSON objects, each with a name and a website, e.g. {"title":"Joe's Pizza","website":"https://joespizzanyc.com"}. Extra fields you add are kept in the output.

## `rowsCsv` (type: `string`):

Optional. Paste a CSV with a header row (for example: title,website) or a JSON array. Semicolon-separated CSV also works.

## `websites` (type: `array`):

Optional. A plain list of business websites, one per line. Use this if you only have URLs and no Maps data.

## `websiteField` (type: `string`):

Leave empty to auto-detect (website, websiteUrl, site, url ...). Only fill this in if your rows keep the website under an unusual field name.

## `maxPlaces` (type: `integer`):

Stop after this many places. You are charged per place enriched, so this is also your spending cap.

## `verifyEmails` (type: `boolean`):

Check each email's syntax and that its domain can receive mail (MX lookup). Also flags disposable, role (info@) and free-provider addresses and suggests fixes for typos. No email is ever sent and no mailbox is contacted. Adds $0.001 per email whose domain passes.

## `onlyWithEmail` (type: `boolean`):

Drop places where no usable email was found. Dropped places are not charged.

## `maxPagesPerSite` (type: `integer`):

How many pages to read on each site: the homepage plus contact and about pages found in the menu and footer.

## `respectRobotsTxt` (type: `boolean`):

Skip pages a site asks crawlers to stay away from and honour its crawl-delay. Recommended.

## `concurrency` (type: `integer`):

How many different websites are read at the same time. Each single website is always read gently, one page at a time.

## `siteTimeoutSeconds` (type: `integer`):

Give up on a website after this many seconds and move on. The place is still returned with whatever was found.

## Actor input object example

```json
{
  "places": [],
  "rowsCsv": "",
  "websites": [],
  "websiteField": "",
  "maxPlaces": 1000,
  "verifyEmails": true,
  "onlyWithEmail": false,
  "maxPagesPerSite": 4,
  "respectRobotsTxt": true,
  "concurrency": 5,
  "siteTimeoutSeconds": 40
}
```

# Actor output Schema

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

No description

## `downloadCsv` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `count` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("that_red_bird/google-maps-email-enricher").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("that_red_bird/google-maps-email-enricher").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 '{}' |
apify call that_red_bird/google-maps-email-enricher --silent --output-dataset

```

## MCP server setup

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

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/QGcvgwdoRq1FiV9MS/builds/ybncDZuW8tb3yOOCh/openapi.json
