# Google Maps Email & Leads Extractor - Fast & Cheap (`aolmo/google-maps-leads`) Actor

Business leads from Google Maps in one step: for any search such as "dentists in Madrid" you get each business with phone, address, rating and website, plus the emails and social profiles found on its website. No browser, no API key. $0.0015 per lead with contacts included.

- **URL**: https://apify.com/aolmo/google-maps-leads.md
- **Developed by:** [Antonio del Olmo Tena](https://apify.com/aolmo) (community)
- **Categories:** Lead generation, Business
- **Stats:** 6 total users, 0 monthly users, 88.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 lead scrapeds

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

Turn any Google Maps search into a list of business leads with emails, in one step. Type searches such as `dentists in Madrid` or `plumbers in Austin, TX` and the Actor returns one row per business with its Google Maps data (name, category, address, phone, website, rating, reviews, opening hours, coordinates) plus the emails, extra phones and social profiles (Instagram, Facebook, LinkedIn, X, YouTube, TikTok, WhatsApp) found on its own website. Ready to export to CSV or Excel and load into your CRM or outreach tool.

No browser, no proxies on your side, no API keys: the Actor reads Google Maps through the same endpoint the web app uses, then visits each business website (home, contact and about pages) to collect contacts. You pay $0.0015 per business returned, contacts included.

### What you get

One Dataset row per business:

- `email` (first one found) and `emails` (all), checked against the domain's mail servers when **Validate email domains** is on.
- `phone` and `phoneInternational` from Google Maps, `sitePhones` found on the website.
- `facebook`, `instagram`, `linkedin`, `twitter`, `youtube`, `tiktok`, `whatsapp`: profile links, one column each.
- `name`, `category`, `categories`, `website`, `domain`, `address`, `street`, `neighborhood`, `city`, `postalCode`, `countryCode`, `latitude`, `longitude`, `rating`, `reviewsCount`, `openingHours`, `timezone`, `placeId`, `url`.
- `status`: what the contact search found. `found` (emails, phones or socials), `none` (website read, nothing found), `no-website`, `social-only` (the website on Maps is a social profile, kept in its column), `skipped` (booking or link-in-bio pages) or `error` (the website could not be read; `error` says why).
- `isNew`: `true` when the business was not in the previous run of that query.

### How to use it

1. Open the **Input** tab.
2. In **Search queries** type one per line, exactly as in Google Maps: `dentists in Madrid`, `marketing agency London`, `coworking near Lisbon`.
3. Optionally set **Location** (added to queries that do not mention one), **Language** and **Country**.
4. Set **Max businesses per query** (default 20, up to about 120). Turn on **Skip businesses without website** if you only want businesses you can reach by email.
5. Click **Start**. Read the rows in **Output** or export them as CSV, Excel or JSON.
6. To catch new businesses in your market, schedule the Actor (weekly or monthly). New ones come with `isNew: true`.

### Input

```json
{
  "queries": ["dentists in Madrid", "physiotherapists in Madrid"],
  "country": "es",
  "maxPlaces": 50,
  "maxPagesPerSite": 3,
  "validateEmails": true,
  "skipWithoutWebsite": true
}
```

| Field                | Description |
|----------------------|-------------|
| `queries`            | Google Maps searches, one per line. Up to 50 per run. |
| `location`           | Optional. Appended to queries that do not mention it. |
| `language`, `country`| Google's edition, default `en` and `us`. |
| `maxPlaces`          | Businesses per query, 1 to 120. Default 20. |
| `maxPagesPerSite`    | Website pages read per business, 1 to 10. Default 3. |
| `validateEmails`     | Drop emails whose domain cannot receive mail. Default on. |
| `skipWithoutWebsite` | Return only businesses with a website. Default off. |

### Output example

```json
{
  "query": "dentists in Madrid",
  "name": "Dental Clinic Navarro",
  "email": "clinica@dentalnavarro.com",
  "emails": ["clinica@dentalnavarro.com"],
  "phone": "+34 915 00 00 00",
  "phoneInternational": "+34915000000",
  "sitePhones": ["+34915000000"],
  "website": "https://dentalnavarro.com/",
  "domain": "dentalnavarro.com",
  "facebook": "https://www.facebook.com/dentalnavarro",
  "instagram": "https://www.instagram.com/dentalnavarro",
  "linkedin": null,
  "twitter": "https://twitter.com/dentalnavarro",
  "youtube": null,
  "tiktok": null,
  "whatsapp": "https://wa.me/34600000000",
  "status": "found",
  "pagesCrawled": 3,
  "error": null,
  "category": "Dentist",
  "address": "C. de Alcalá, 100, Salamanca, 28009 Madrid, Spain",
  "city": "Madrid",
  "postalCode": "28009",
  "countryCode": "ES",
  "rating": 4.9,
  "reviewsCount": 812,
  "placeId": "ChIJ...",
  "url": "https://www.google.com/maps/place/?q=place_id:ChIJ...",
  "isNew": false,
  "timestamp": "2026-10-05T09:00:00.000Z"
}
```

### Pricing

Pay per event, no subscription:

- **Lead scraped**: $0.0015 per business row, with the website contact search included. 1,000 leads cost $1.50.
- **New lead**: $0.003 per business flagged as new in a scheduled run.

Queries that cannot be read or return nothing are free. Compare with Google Maps scrapers that charge about $1.50 per 1,000 places plus extra for contact details, or $2.10 per 1,000 with contacts.

In our tests, 18 of 20 dentists in Madrid and 10 of 20 plumbers in Austin came with at least one email, and almost all with social profiles.

### Use it as an API

```bash
curl -X POST "https://api.apify.com/v2/acts/aolmo~google-maps-leads/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries": ["plumbers in Austin, TX"], "maxPlaces": 40, "skipWithoutWebsite": true}'
```

You can also call it from AI agents through the Apify MCP server. For Google Maps data without the website visit, use the cheaper `aolmo/google-maps-places`.

### FAQ

**Why do some businesses have no email?** Many small businesses only publish a phone or a contact form, or protect their website against automated visits. Those rows keep all the Google Maps data and whatever social profiles were found.

**Does it bypass website protections?** No. Websites behind anti-bot walls are reported with `status: error` and their Google Maps data is still returned.

**How long does a run take?** About 30 to 80 seconds per 20 businesses, depending on how fast their websites respond.

**Is this GDPR compliant?** The Actor only collects contact details that businesses publish themselves. How you use them (for example, sending marketing emails) is your responsibility under the laws that apply to you.

**Where is the data stored between runs?** In a Key-Value Store of your account named `micro-saas-state-google-maps-leads`, one key per query. Delete it to start fresh.

# Actor input Schema

## `queries` (type: `array`):

One per line, as you would type it in Google Maps: dentists in Madrid, plumbers in Austin TX, marketing agency London.

## `location` (type: `string`):

Optional. Added to every query that does not mention it, e.g. Austin, TX turns plumbers into plumbers in Austin, TX.

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

Language of names and categories: en, es, fr, de...

## `country` (type: `string`):

Two-letter country code for Google's edition: us, es, gb, de...

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

Google Maps serves up to about 120 places per search. Also caps what you pay per query.

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

How many pages of each business website are read to find contacts: home first, then contact and about pages. 3 finds most emails.

## `validateEmails` (type: `boolean`):

Drops emails whose domain cannot receive mail (DNS MX check).

## `skipWithoutWebsite` (type: `boolean`):

Only return (and charge) businesses that have a website on Google Maps.

## `product` (type: `string`):

Internal. Leave as is.

## Actor input object example

```json
{
  "queries": [
    "dentists in Madrid"
  ],
  "language": "en",
  "country": "us",
  "maxPlaces": 20,
  "maxPagesPerSite": 3,
  "validateEmails": true,
  "skipWithoutWebsite": false,
  "product": "google-maps-leads"
}
```

# Actor output Schema

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

Dataset items: url, changed, events, data, timestamp and error (when the target failed).

# 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 = {
    "queries": [
        "dentists in Madrid"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aolmo/google-maps-leads").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 = { "queries": ["dentists in Madrid"] }

# Run the Actor and wait for it to finish
run = client.actor("aolmo/google-maps-leads").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 '{
  "queries": [
    "dentists in Madrid"
  ]
}' |
apify call aolmo/google-maps-leads --silent --output-dataset

```

## MCP server setup

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

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/9DWiRz1AIQ8DjeWRq/builds/419GbOxv5Yph0gXSa/openapi.json
