# 📍 Google Maps Email Scraper - Business Leads with Emails (`renzomacar/google-maps-leads-with-emails`) Actor

Scrape emails from Google Maps: turn any niche + city into ready-to-use leads. One row per business with name, phone, website, emails found on the company site, socials and tech stack. No API key, no duplicates. Built for B2B lead generation and cold outreach. Pay only per lead delivered.

- **URL**: https://apify.com/renzomacar/google-maps-leads-with-emails.md
- **Developed by:** [Renzo Madueno](https://apify.com/renzomacar) (community)
- **Categories:** Lead generation, Business
- **Stats:** 16 total users, 2 monthly users, 98.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $18.60 / 1,000 lead with emails

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Email Scraper - Business Leads with Emails

Scrape emails from Google Maps in one run. Type a niche + city (like `dentists in Miami, FL`) and get **one consolidated row per business**: name, address, phone, rating, reviews, category, website — plus **emails found on the business's own website**, extra phone numbers, social profiles (LinkedIn, Facebook, Instagram…) and the site's tech stack.

![Real output: one row per business with email found on the website, phone, rating and tech stack](https://api.apify.com/v2/key-value-stores/oVglg9gfEMc29bKLa/records/google-maps-leads-with-emails_output.png)

Most Google Maps scrapers stop at the listing — no email, because Google Maps itself doesn't show emails. This actor goes one step further: it visits every business website for you, finds the contact emails published there, and delivers everything merged. No chaining two scrapers, no exporting CSVs and matching rows by hand.

### Scrape emails from Google Maps: what you get

One row per business:

| Field | Example |
|---|---|
| `businessName` | Bright Smile Dental |
| `category` | Dentist |
| `address` | 1200 Brickell Ave, Miami, FL 33131 |
| `phone` | (305) 555-0182 |
| `website` | https://brightsmilemiami.com |
| `rating` / `reviewCount` | 4.8 / 312 |
| `emailFound` | `true` |
| `email` | info@brightsmilemiami.com |
| `emails` | all emails found on the site |
| `websitePhones` | extra phones found on the site |
| `socialLinks` | `{ "facebook": "...", "instagram": "...", "linkedin": "..." }` |
| `techStack` | `["WordPress", "Google Analytics"]` |
| `contactPageUrl` | https://brightsmilemiami.com/contact |
| `latitude` / `longitude`, `placeUrl`, `searchQuery`, `scrapedAt` | included |

Export to CSV, Excel, JSON or push straight into your CRM via the Apify API. Filter instantly on `emailFound` to keep only emailed leads.

### How this Google Maps email extractor works

![How it works: search query, Google Maps businesses, website visit, enriched leads](https://api.apify.com/v2/key-value-stores/oVglg9gfEMc29bKLa/records/google-maps-leads-with-emails_howitworks.png)

1. **Google Maps search** — scrapes the live results feed for each query (up to ~120 businesses per search; set `maxResults` per query).
2. **Website email extraction** — every business with a website gets its homepage, contact and about pages checked for emails, phones and social links (set `maxPagesPerSite`). Emails come from the business's own public site, so they are real, current addresses — not guessed patterns.
3. **One consolidated row** per business, with an `emailFound` flag.

Tip: enable `onlyWithWebsite` to drop businesses without a website — you only pay for enrichable leads. Keep `maxResults` ≤ 100 per run for the fastest turnaround; for bigger campaigns, split by city or niche.

### Building a prospect list for one city, ZIP code or state

A search query is free text passed to Google Maps, so anything Maps understands works as a geography: `plumbers in Austin, TX`, `dentists in 33131`, `HVAC contractors near Scottsdale AZ`, `med spas in Brooklyn NY`. Category + place is what produces clean lists; a bare category with no place returns whatever Maps thinks is near the datacenter.

Practical limits and behaviour:

- Google Maps stops feeding new results at roughly **120 businesses per search**, no matter what `maxResults` says. To cover a whole metro or state, split it: one query per city, per suburb, or per ZIP code, all pasted into `searchQueries` as separate lines in the same run.
- **Overlapping queries are deduplicated inside the run.** Businesses are keyed on their Google Maps place URL (falling back to name + address), so a clinic that shows up in both `dentists in Miami, FL` and `dentists in 33131` is scraped, enriched and charged **once**.
- Every row carries the `searchQuery` that found it, so you can still see which city or ZIP a lead came from after the merge.
- Deduplication is per run. If you split a campaign across several runs, dedupe on `placeUrl` (or `website`) when you merge the exports.

### Getting the list into your cold email tool

The run writes to an Apify dataset, which exports as **CSV, Excel, JSON, XML or HTML** from the run's Storage tab, or over the API:

```
https://api.apify.com/v2/datasets/<datasetId>/items?format=csv&clean=true
```

For a mail-merge upload the columns that matter are `email` (the primary address), `businessName`, `website`, `phone` and `address`; `emails` holds every address found on that site if you want to fan out. Filter on `emailFound = true` to drop the rows you cannot mail — in the Apify UI, or by adding `&filter=` logic downstream. Because `email` is a single string and `emails` an array, most CSV importers (Instantly, Smartlead, Lemlist, Mailshake, HubSpot) map `email` directly with no cleanup.

### Google Maps gives you a phone — this fills in the email

A Maps listing publishes name, address, phone, hours, rating and a website link. It never publishes an email, which is why phone-only lists are easy to buy and emailable lists are not. The gap is closed by following the website link: each business site is fetched over plain HTTP and up to eight paths are checked (`/`, `/contact`, `/contact-us`, `/about`, `/about-us`, `/team`, `/contacto`, `/impressum`), with an 8-second request timeout and a 1 MB cap per page, so a dead or slow site cannot stall the run. Set `maxPagesPerSite` to control how deep that goes: `1` is homepage-only and fastest, `4`–`8` finds noticeably more addresses on sites that keep contact details on a dedicated page.

### Google Maps lead generation: who uses this

- **Agencies & freelancers selling to local businesses** — web design, SEO, ads, social media management. Search your niche + city, export, start outreach the same day.
- **B2B sales teams** prospecting local markets — dental clinics, gyms, law firms, restaurants, contractors, med spas.
- **Cold email senders** who need fresh, verified-source contact data instead of stale purchased lists.

### Local business emails: pricing (pay per event)

| Event | Price |
|---|---|
| Actor start (per GB of memory, 4 GB default) | $0.005 / GB |
| Business delivered **without** email | $0.006 |
| Lead delivered **with** email | $0.02 |

**Cost examples:**

1. **Quick test** — "coffee shops in Austin, TX", 8 results, 3 emails found:
   start $0.02 + 3 × $0.02 + 5 × $0.006 = **$0.11**
2. **One niche, one city** — 100 dentists in Miami, 40 with email:
   start $0.02 + 40 × $0.02 + 60 × $0.006 = **$1.18** → **under 3 cents per emailed lead**
3. **Campaign** — 5 cities × 100 plumbers, ~200 emails found:
   start $0.02 + 200 × $0.02 + 300 × $0.006 = **$5.82** for 500 leads

Compare that with $0.05–$0.25 per contact on typical lead databases — and this data is scraped fresh at run time, not resold from a stale list.

### How does it compare to other Google Maps scrapers?

The most popular contact-details scraper on Apify is excellent at what it does, and if you need its specific feature set, use it. This actor makes a different trade-off:

| | This actor | Typical alternatives |
|---|---|---|
| Emails | ✅ From the business's own website, with `emailFound` flag | Varies; often listing data only |
| Pricing | Pay **per lead delivered** — from $0.006/lead, $0.02 with email | Often priced per result regardless of email |
| Output | One merged row per business (Maps + website data) | May require combining runs |
| Extras | Tech stack, contact page URL, extra site phones | Varies |

Our focus is simple: the **cheapest path from "niche + city" to a usable cold-outreach list with emails**.

### Why emails are not found for every business

Roughly 30–60% of local businesses publish an email on their website (varies by niche). Businesses with no website, or whose site hides contact info behind forms, are delivered as **basic** rows (charged at the lower rate) — you still get name, address, phone, rating and the Google Maps link.

### Use with AI agents — Claude, n8n, Make

The output is clean JSON (one object per business), so it drops straight into automation and AI-agent workflows. Trigger a run and read the dataset via the [Apify API](https://docs.apify.com/api/v2) or the official Apify integrations for **n8n**, **Make**, and **Zapier** — then let an agent draft and send the outreach.

Working with the **Model Context Protocol (MCP)**? This actor is also reachable through the [multi-scraper MCP server](https://apify.com/renzomacar/multi-scraper-mcp), so an MCP-enabled assistant (Claude, etc.) can search Google Maps and pull enriched leads as a tool call — no glue code.

### Public business data, GDPR and cold outreach

This actor collects **only publicly available business information** — data that businesses themselves publish on Google Maps and on their own public websites to be contacted by customers. No login-protected, private or personal-account data is accessed.

You are responsible for using the output in compliance with applicable laws in your jurisdiction (e.g. GDPR, CAN-SPAM, CASL), including consent and opt-out rules for commercial outreach. Use the data to make relevant, professional contact — not to spam.

### FAQ

**Can you scrape emails from Google Maps directly?**
No — Google Maps listings don't display email addresses. Any "Google Maps email scraper" must follow the listing's website link and extract emails there. That is exactly what this actor automates: Maps search → website visit → merged row.

**Are the emails verified?**
Emails are extracted from the business's own live website at run time — they are published, current addresses, not pattern-guessed (`firstname@domain`) contacts. Generic addresses (info@, hello@) can be excluded with the `includeGenericEmails` toggle.

**Does it work for any country/language?**
Yes — queries can target any city Google Maps covers; set `language` for localized results.

**How long does a run take?**
A 50-business query with enrichment typically takes a few minutes. Websites are crawled over plain HTTP with high concurrency and a hard per-site time limit, so one slow or dead website never stalls the run.

**What if a search returns nothing?**
The run succeeds with an empty dataset and the query is recorded under `EMPTY_QUERIES` in the key-value store. Hard failures (blocked IPs, etc.) are stored under `FAILURES` — never mixed into your lead list.

**How is this different from buying a lead list?**
Purchased lists decay ~2-3% per month and are resold to your competitors. This scrapes fresh data at run time for a fraction of the price, and you choose exactly the niche and geography.

# Actor input Schema

## `searchQueries` (type: `array`):

Google Maps searches, one per line (e.g. 'dentists in Miami, FL', 'plumbers in Austin, TX'). Tip: niche + city = best leads.

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

Maximum number of businesses to extract per search query. Google Maps typically returns up to ~120 per search.

## `onlyWithWebsite` (type: `boolean`):

If enabled, businesses without a website are dropped from the output (you are not charged for them). Useful when you only want enrichable leads.

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

How many pages to check per business website when looking for emails (homepage, /contact, /about...). More pages = more emails found but slower.

## `includeGenericEmails` (type: `boolean`):

Include addresses like info@, hello@, contact@. Disable to keep only personal/role emails (fewer results).

## `language` (type: `string`):

Language code for Google Maps results (e.g. 'en', 'es', 'fr').

## Actor input object example

```json
{
  "searchQueries": [
    "dentists in Miami, FL"
  ],
  "maxResults": 5,
  "onlyWithWebsite": false,
  "maxPagesPerSite": 1,
  "includeGenericEmails": true,
  "language": "en"
}
```

# Actor output Schema

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

Rows produced by Google Maps Email Scraper, one item per result in the default 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 = {
    "searchQueries": [
        "dentists in Miami, FL"
    ],
    "maxResults": 5,
    "maxPagesPerSite": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("renzomacar/google-maps-leads-with-emails").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 = {
    "searchQueries": ["dentists in Miami, FL"],
    "maxResults": 5,
    "maxPagesPerSite": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("renzomacar/google-maps-leads-with-emails").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 '{
  "searchQueries": [
    "dentists in Miami, FL"
  ],
  "maxResults": 5,
  "maxPagesPerSite": 1
}' |
apify call renzomacar/google-maps-leads-with-emails --silent --output-dataset

```

## MCP server setup

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

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/b4HxmAs6Yd1qhV1dI/builds/d3GGKGawqf5rwkqLC/openapi.json
