# Google Maps Scraper & API: Whole-City Leads with Emails (`brainfoods_studio/google-maps-places-scraper`) Actor

Every business in a city or neighbourhood from Google Maps, with emails and social links, free lead scores and a coverage report that shows the list is complete. No login. $3 per 1,000 places, less on paid plans; filtered-out places cost a fraction.

- **URL**: https://apify.com/brainfoods_studio/google-maps-places-scraper.md
- **Developed by:** [Brainfoods Studio](https://apify.com/brainfoods_studio) (community)
- **Categories:** Lead generation, Travel, SEO tools
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 places

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 Places Scraper

> **Yes, we're late to the party.** The Store already has good Google Maps scrapers. We built one more for a simple reason: you asked for *every* café in a city, and the usual answer was the first few hundred Google felt like showing you. This one keeps searching until the area is covered, then tells you how complete the list is.

**Google Maps Places Scraper** gives you **complete lead lists for a whole city or neighbourhood** from Google Maps: name, category, address, phone, website, rating, reviews, opening hours, claimed or not, and a map link. Each place comes with **free lead scores** for web design, local SEO and reputation agencies, plus map files for Google My Maps, QGIS and kepler.gl.

**No login · No Google API key · From $0.001 per place · Filtered-out places cost a fraction**

### What does Google Maps Places Scraper do?

- **Covers the whole area.** It keeps searching until Google has nothing new to show for the area.
- **Tells you how complete it is.** A free coverage row per area and search term says how much was searched, how many places were found, and whether the search finished.
- **Scores every lead, free.** Web-design, local-SEO and reputation scores from 0 to 100, each with the reasons in plain words.
- **Filters that save you money.** Minimum rating, minimum reviews, only unclaimed, only without a website, skip closed, minimum score. You pay the full price only for places that pass.
- **Finds emails and social links (opt-in).** Turn on website contacts and it visits each delivered place's own website for business emails, social profiles, and whether the site loads, uses HTTPS and works on phones. You pay only for places where an email or social profile is found.
- **Exports maps.** `places.geojson` and `places.kml`, ready for Google My Maps, Google Earth, QGIS and kepler.gl.
- **Optional extras:** an area report per area (demand, quality, leaders, gaps) and an interactive **marketing kit** dashboard.

### Who is it for?

- **Agencies** finding clients: unclaimed listings, businesses with no website, places with a reputation problem.
- **Sales teams** building a territory list that's actually complete, not the top 120.
- **Founders opening a shop** who want to see where the competition is thin before signing a lease.
- **Analysts and AI agents** that need clean rows with stable IDs, not a screenshot of a map.

### Use cases

| You want | What to set |
| --- | --- |
| Every café in a city | Search terms `coffee shop`, Locations `Jaipur` |
| Web-design clients | Only places without a website, or Minimum web-design score `60` |
| Local-SEO clients | Only unclaimed listings |
| Well-reviewed places only | Minimum reviews `50`, Minimum rating `4` |
| One neighbourhood | Locations `C Scheme, Jaipur` |
| An exact area | Custom area: GeoJSON, a centre and radius, or a box |
| A market overview to send a client | Marketing kit |

### How it finds your area

You type a place name. **Google Maps decides which place you mean**, so "Malviya Nagar, Jaipur" is the neighbourhood, not a power station with the same name.

- **Cities** are searched to their boundary.
- **Neighbourhoods** without a published boundary are searched within 2 km of their centre. Change it with **Neighbourhood radius**.
- **Need an exact shape?** Give a **Custom area** instead.
- The run log shows what each location was matched to and how many km² were searched.

### What we measured

Live runs on Apify Proxy, November 2026, search term `coffee shop`, 4 GB memory.

| Run | Result |
| --- | --- |
| Input form prefill (20 places in C Scheme, Jaipur) | about 6 seconds |
| Whole city of Jaipur | about 4,070 places in 2 to 4.5 minutes |
| Same city, two runs | 4,068 places in both |
| Coverage vs a much denser search of the same area | 98.6% of places found |
| 3 Jaipur neighbourhoods, at least 50 reviews | 273 places kept, 379 checked and removed, 3.6 minutes |
| Website contacts, 150 cafés in Austin | 35 with an email, 79 with a social profile; about 1 minute extra |
| Website contacts, 150 cafés in C Scheme, Jaipur (100 list no website) | 15 with an email, 26 with a social profile; about 20 seconds extra |

A city search takes minutes, not seconds, because it covers every street. The status line shows progress as it goes.

### How to use it

1. Type one or more **Search terms**, such as `coffee shop` or `dentist`.
2. Add **Locations** by name, or draw a **Custom area**.
3. Set **Max places** and any filters.
4. Run it, then download the results as JSON, CSV or Excel, or open the map files.

#### Input example

```json
{
  "searchTerms": ["coffee shop"],
  "locations": ["Jaipur"],
  "country": "IN",
  "maxPlaces": 5000,
  "minReviews": 20,
  "includeAreaReport": true
}
```

#### Use it from an AI agent

Call it from the Apify MCP server or API with the input above. Every row has a `type` (`place`, `coverage`, `area-report`, `summary` or `error`), so an agent can read the coverage row first and know whether the list is complete.

### What you get

| Row type | What's in it | Cost |
| --- | --- | --- |
| `place` | Name, categories, address, postal code, coordinates, phone, website, rating, reviews, hours, claimed or not, status, price level, plus code, Google Maps link, nearest competitor, rating percentile, 3 lead scores with reasons | per place |
| `coverage` | How much was searched, places found, whether the area was finished | free |
| `area-report` | Per area and search term: density, rating spread, unclaimed share, leaders, gaps | optional |
| `summary` | Places delivered and removed, charges, how the run ended | free |
| `error` | A location that couldn't be found, or a page that failed | free |

#### Lead scores

Each place gets three scores from 0 to 100, with the reasons listed next to them:

- **Web design:** high when there's no website or only a social or listing page, and higher for established places with good ratings. With website contacts on, it also counts sites that don't load, lack HTTPS or don't work on phones.
- **Local SEO:** high for unclaimed listings, few reviews, missing hours or a thin profile.
- **Reputation:** high when a well-reviewed place has a rating under 4, so it has something to fix.

#### The marketing kit

Turn on **Marketing kit** and you get `MARKETING_KIT.html`, an interactive market dashboard to open in a desktop browser or send to a client:

- **Buyer view:** switch between *opening a business* and *finding clients as an agency*.
- **Clickable map:** every neighbourhood opens its own numbers.
- **Market charts:** demand vs supply, leaders, who holds the market, quality gaps, whitespace.
- **The top 50 prospects** and the reason each one is on the list.

`MARKETING_KIT.json` has every number behind it, for your own tools or AI agent.

### Pricing

You pay per place delivered. Places your filters remove cost a small check fee instead of the full price.

| Event | Free plan | Bronze | Silver | Gold and up |
| --- | --- | --- | --- | --- |
| Place delivered | $0.003 | $0.002 | $0.0015 | $0.001 |
| Place checked and removed by your filters | $0.0002 | $0.00015 | $0.0001 | $0.00008 |
| Area report, per area and search term | $0.03 | $0.025 | $0.02 | $0.015 |
| Marketing kit, up to 2 locations | $1 | $1 | $1 | $1 |
| Each extra kit location | $0.25 | $0.25 | $0.25 | $0.25 |
| Website contacts, per place where an email or social profile is found | $0.0015 | $0.0012 | $0.001 | $0.0008 |

- **Max places is how many you get.** With filters on, only places that pass count, so the run keeps searching until it has that many or the area is covered.
- **Charged only after rows are saved.** Coverage, summary and error rows are free.
- **Never past your spending limit.** When the limit is reached the search stops and the run still succeeds with what it found.
- **Start fee:** Apify's standard $0.00005 per GB of memory, so $0.0002 at the default 4 GB.

What typical runs cost on the Free plan:

| Run | Cost |
| --- | --- |
| 100 places | $0.30 |
| A whole city, about 4,000 places | about $12 |
| 3 neighbourhoods, 273 kept and 379 removed by a filter | about $0.90 |
| Add an area report for each of those 3 | +$0.09 |
| Add a marketing kit for those 3 | +$1.25 |

### FAQ

#### Do I need a Google account or an API key?

No. The actor reads public Google Maps results the way a browser does. You don't sign in to anything.

#### Why does a whole city take a few minutes?

Google shows at most a couple of hundred places per search, so covering a whole city takes hundreds of searches. That's how the list ends up complete. The prefill run finishes in seconds.

#### How do I know the list is complete?

Read the `coverage` row. It says whether the search finished or stopped at Max places, the time limit or your spending limit.

#### Why was my neighbourhood searched as a circle?

Many neighbourhoods have no published boundary. Then the honest answer is a circle around the centre Google gives. Set **Neighbourhood radius**, or give a **Custom area** for an exact shape.

#### Does it get emails?

Yes, as an opt-in. Turn on **Website contacts: emails and social links** and the actor visits each delivered place's own website (the homepage, plus up to 3 contact or about pages when the homepage has no email). It returns business emails, social profiles and whether the site loads, uses HTTPS and works on phones.

- **Business emails only, by default.** Addresses like info@ or hello@ on the business's own domain. Personal-looking emails (jamie@, tom.smith@, a name at Gmail) come only if you also turn on **Include personal-looking emails**. You are responsible for having a lawful reason to contact people (GDPR, CAN-SPAM, India's DPDP Act).
- **Respectful.** Each site's robots.txt is honoured. Sites with bot protection are skipped, not bypassed, and count as unreachable.
- **What to expect.** About half of business websites give an email, and three quarters a social profile. Many places list no website at all, or only an Instagram or Facebook page; those get a social link at best.
- **You pay only for finds.** Website contacts are charged per place where an email or social profile is delivered, never for a site that gave nothing.
- **A little slower.** It adds about 5 seconds per website, run in parallel.

#### Which countries work?

Anywhere Google Maps works. Set **Country** to bias the place lookup, and **Language** for the result text.

### Legal note

This actor collects publicly available business listings. You're responsible for how you use the data, including local rules on marketing and personal data.

### More from Brainfoods Studio

- [Google Trends Scraper](https://apify.com/brainfoods_studio/google-trends-scraper)
- [Google News Scraper](https://apify.com/brainfoods_studio/google-news-scraper)
- [Google Ads Transparency Scraper](https://apify.com/brainfoods_studio/google-ads-transparency-scraper)
- [Google Play Scraper](https://apify.com/brainfoods_studio/google-play-scraper)

### Support

Found a bug or missing a field? Open an issue on the actor's Issues tab. We're the new kids here, so we read every one and usually reply within a day.

***

<sub>Area boundaries © OpenStreetMap contributors (ODbL).</sub>

# Actor input Schema

## `searchTerms` (type: `array`):

What to search for, up to 20 terms, for example "coffee shop" or "dentist". Each term is searched in every area. Duplicates are removed.

## `locations` (type: `array`):

Places to cover completely, up to 20, such as "Jaipur" or "C Scheme, Jaipur". Google Maps decides which place you mean. Cities are searched to their boundary; neighbourhoods without a published boundary are searched within Neighbourhood radius of their centre. You get the whole area, not just the first few results.

## `neighbourhoodRadiusM` (type: `integer`):

How far from its centre to search a neighbourhood that has no published boundary. 2,000 m covers most neighbourhoods.

## `customArea` (type: `object`):

Optional. Your own area instead of, or as well as, the locations. Give one of: {"geojson": a Polygon, MultiPolygon or Feature}, {"center": {"lat": 26.91, "lng": 75.79}, "radiusKm": 3} (up to 100 km), or {"bbox": {"south": 26.9, "west": 75.78, "north": 26.93, "east": 75.82}}. It appears in the results as "custom area".

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

Optional two-letter country code, for example IN, US or GB. It makes Google rank results for that country and helps read postal codes. Leave empty if unsure.

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

Two-letter language code for the results, for example en, de or hi.

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

How many places you get. With filters on, only places that pass them count: the run keeps searching until this many pass, and each removed place costs only the small check fee. Your spending limit also stops the run. For a full-city marketing kit, keep this high: the kit covers only what the search found.

## `minRating` (type: `number`):

Keep only places rated at least this, from 0 to 5. Places with no rating are removed.

## `minReviews` (type: `integer`):

Keep only places with at least this many reviews.

## `onlyUnclaimed` (type: `boolean`):

Keep only places whose Google listing is not claimed by an owner. Strong local-SEO leads.

## `onlyWithoutWebsite` (type: `boolean`):

Keep only places that list no website. Strong web-design leads.

## `skipClosed` (type: `boolean`):

Remove places marked closed. Google does not always show this in search results, so some closed places can remain.

## `minWebDesignScore` (type: `integer`):

Keep only places scoring at least this, 0 to 100. 70 and above is a hot lead. Rows say which rules fired.

## `minLocalSeoScore` (type: `integer`):

Keep only places scoring at least this, 0 to 100. 70 and above is a hot lead.

## `minReputationScore` (type: `integer`):

Keep only places scoring at least this, 0 to 100. 70 and above is a hot lead.

## `includeAreaReport` (type: `boolean`):

Adds one report per area and search term: places per km2, category mix, rating spread, share unclaimed or without a website, hot spots, under-served cells, a breakdown by postal code and market leaders. Charged once per area and search term that delivered places.

## `includeMarketingKit` (type: `boolean`):

Adds MARKETING_KIT.html, a self-contained report you can open offline or send to a client: each city compared, competitors, under-served areas and the top 25 leads. $1 covers up to 2 locations, plus $0.25 for each extra location. City scale or smaller only; larger areas are still searched but left out of the kit. Charged only when places are delivered.

## `includeContacts` (type: `boolean`):

Visits each delivered place's own website (homepage, plus up to 3 contact or about pages when the homepage has no email) and adds business emails, social profiles, whether the site loads, uses HTTPS and works on phones. Charged per place only when an email or social profile is found. Adds about 5 seconds per website, run in parallel.

## `includeNamedEmails` (type: `boolean`):

Also deliver emails that look like a person's (jamie@, tom.smith@, a name at Gmail). You are responsible for having a lawful reason to contact people (GDPR, CAN-SPAM, India's DPDP Act).

## `exportFiles` (type: `boolean`):

Save places.geojson and places.kml in the run's key-value store, ready for Google My Maps, QGIS and kepler.gl. Free.

## `maxCellsPerArea` (type: `integer`):

Upper limit on the map cells searched for one area and search term. Dense areas are split into smaller cells; this caps the work. The coverage row tells you if it was reached.

## `maxConcurrency` (type: `integer`):

Cells searched in parallel, 1 to 24. The default of 16 covered a whole city (Jaipur, 4,000 places) without a single blocked page; lower it only if the log shows blocked pages.

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

Apify Proxy is strongly recommended. Datacenter is tried first; residential is used automatically after repeated 429s if your plan has it.

## Actor input object example

```json
{
  "searchTerms": [
    "coffee shop"
  ],
  "locations": [
    "C Scheme, Jaipur"
  ],
  "neighbourhoodRadiusM": 2000,
  "country": "",
  "language": "en",
  "maxPlaces": 20,
  "onlyUnclaimed": false,
  "onlyWithoutWebsite": false,
  "skipClosed": false,
  "includeAreaReport": false,
  "includeMarketingKit": false,
  "includeContacts": false,
  "includeNamedEmails": false,
  "exportFiles": true,
  "maxCellsPerArea": 1500,
  "maxConcurrency": 16,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All rows: places with lead scores, a free coverage row per area and search term, the optional area report, a free summary row, and error rows for any location or page that failed.

## `marketingKit` (type: `string`):

MARKETING_KIT.html: the interactive market dashboard. Open it in a desktop browser, offline is fine. Written when Marketing kit is on and places were delivered.

## `marketingKitData` (type: `string`):

MARKETING_KIT.json: every number behind the dashboard, for your own tools or AI agent.

## `geojson` (type: `string`):

All delivered places as a GeoJSON FeatureCollection, for QGIS, kepler.gl and Google My Maps. Written when Export map files is on.

## `kml` (type: `string`):

All delivered places as a KML document, for Google My Maps and Google Earth. Written when Export map files is on.

## `stats` (type: `string`):

Requests, successes, rate limits, retries, session changes and a summary of rows and charges for this run.

# 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 = {
    "searchTerms": [
        "coffee shop"
    ],
    "locations": [
        "C Scheme, Jaipur"
    ],
    "maxPlaces": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("brainfoods_studio/google-maps-places-scraper").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 = {
    "searchTerms": ["coffee shop"],
    "locations": ["C Scheme, Jaipur"],
    "maxPlaces": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("brainfoods_studio/google-maps-places-scraper").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 '{
  "searchTerms": [
    "coffee shop"
  ],
  "locations": [
    "C Scheme, Jaipur"
  ],
  "maxPlaces": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call brainfoods_studio/google-maps-places-scraper --silent --output-dataset

```

## MCP server setup

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

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/0SDeio4p2hgaGfrkK/builds/LnQ1kQYgWrxB7VDwb/openapi.json
