# Google Maps Extractor - Fast Business Data & Leads (`andrew_babo/google-maps-extractor`) Actor

Extract Google Maps business data at scale: names, categories, addresses, coordinates, phones, websites, opening hours, ratings and review counts. Business data only, no review texts or photo lists, so it runs far faster and cheaper than browser-based extractors. Export to CSV, Excel, JSON.

- **URL**: https://apify.com/andrew\_babo/google-maps-extractor.md
- **Developed by:** [Andrew Babo](https://apify.com/andrew_babo) (community)
- **Categories:** Lead generation, E-commerce, SEO tools
- **Stats:** 449 total users, 0 monthly users, 94.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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 Extractor - Fast Business Data & Leads

Extract Google Maps business listings at scale — **business data only**. No review texts, no photo
galleries, no popular-times histograms: that is exactly why this actor is several times faster and
cheaper than browser-based extractors.

Need review bodies, photos and popular times? Use the heavy sibling,
[Google Maps Scraper](https://apify.com/andrew_babo/google-maps-scraper).

### Proxies (important)

Run with **Apify Proxy + RESIDENTIAL**; it is the default in the input form. Google blocks
datacenter IPs within minutes and the run then returns nothing. If the residential group is not
available on your plan, the actor automatically falls back to the default Apify proxy and flags
`proxyDegraded` in the run summary.

### Empty results are not failures

If nothing matches (rare keyword, empty area) the run **succeeds** with an empty dataset and the
run summary carries `zeroResultReason` (`NO_RESULTS`, `NO_RESULTS_IN_AREA`, `ALL_BLOCKED`,
`UPSTREAM_TEMPORARY`) plus a human-readable `zeroResultMessage`. Set `failOnZeroResults: true`
if you prefer the run to fail instead.

### What you get per place

`title`, `categoryName`, `categories`, `address`, `street`, `neighborhood`, `city`, `postalCode`,
`state`, `countryCode`, `countryName`, `location` (lat/lng), `plusCode`, `totalScore`,
`reviewsCount`, `openingHours`, `permanentlyClosed`, `temporarilyClosed`, `phone`,
`phoneUnformatted`, `website`, `imageUrl`, `placeId`, `fid`, `cid`, `url`, `rank`,
`isAdvertisement`, `searchString`.

### How it works

- **No browser on the main path.** Google Maps' own JSON endpoints are called directly, so a place
  costs a fraction of a second instead of several seconds of page rendering.
- **Breaks the ~120-results-per-viewport limit** by splitting the area into adaptive map tiles and
  deduplicating by place ID.
- **Detail requests only when they add something.** A listing record that already carries website,
  phone, hours and review count never triggers an extra request.
- **Geo guard**: results that fall outside the requested area (Google answering from the egress IP)
  are dropped instead of silently polluting the dataset.

### Input

Minimal run:

```json
{
  "searchStringsArray": ["coffee shop"],
  "locationQuery": "Da Nang, Vietnam",
  "maxCrawledPlacesPerSearch": 100
}
```

Residential proxies are strongly recommended — datacenter IPs get blocked by Google within minutes.

### Benchmarks — head-to-head vs `compass/google-maps-extractor`

Same query, same place cap, same bounding box, run sequentially. Coverage is computed on the
matched-by-place-ID intersection so result-set composition cannot flatter either side.

500 places per query:

| Query | this actor | compass | cost / 1k places (this) | cost / 1k places (compass) |
|---|---|---|---|---|
| coffee shop, Da Nang | 23.1 s | 34.9 s | $0.013 | $4.32 |
| IT services, Singapore | 29.3 s | 65.0 s | $0.015 | $4.12 |
| hotel, Bangkok | 22.5 s | 60.0 s | $0.013 | $5.00 |
| logistics, Ho Chi Minh City | 28.5 s | 52.1 s | $0.002 | $4.75 |

Field coverage is equal or better on every field measured. The consistent wins:

| Field | this actor | compass |
|---|---|---|
| postalCode | 100% | 35–100% |
| reviewsCount | 100% | 54–99% |
| plusCode | 82–98% | 0% |
| imageUrl | 97–100% | 95–99% |

Title, category, address, street, city, state, countryCode, coordinates, rating, opening hours,
phone, website and Maps URL match compass exactly on the intersection.

Full tables: [`docs/poc-vs-compass-500.md`](docs/poc-vs-compass-500.md) and
[`docs/poc-vs-compass-100.md`](docs/poc-vs-compass-100.md), produced by `scripts/compare.mjs`.

### For AI agents (MCP-ready)

This actor is built to be called by AI agents. It works out of the box with the
[Apify MCP Server](https://mcp.apify.com) — add it to Claude Desktop, Cursor or any MCP
client: the agent can pull business listings and leads on its own.

```json
{
  "mcpServers": {
    "google-maps-extractor": {
      "url": "https://mcp.apify.com/?actors=andrew_babo/google-maps-extractor",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

#### Agent skill (paste into your agent's instructions)

```text
Use the "google-maps-extractor" tool for fast, cheap Google Maps BUSINESS
DATA: names, addresses, coordinates, phone, website, rating, review counts,
opening hours. It does NOT return review texts, photo galleries or popular
times — for those use "google-maps-scraper" instead.

HOW TO CALL
- { "searchStringsArray": ["coffee shop"], "locationQuery": "Da Nang, Vietnam",
    "maxCrawledPlacesPerSearch": 100 }
- Always keep the default residential proxy. Datacenter IPs are blocked by
  Google within minutes and the run returns nothing.
- Cap "maxCrawledPlacesPerSearch" to what the user actually needs — it is
  the cost driver.
- Precise areas: "customGeolocation", or bbox / centerPoint + radiusMeters.

OUTPUT CONTRACT
- One row per place: title, categories, full address parts, location
  (lat/lng), totalScore, reviewsCount, openingHours, phone, website,
  placeId, cid, url.
- An empty dataset is a valid SUCCESS: read zeroResultReason
  (NO_RESULTS, NO_RESULTS_IN_AREA, ALL_BLOCKED, UPSTREAM_TEMPORARY) and
  zeroResultMessage in the run summary and report it. ALL_BLOCKED means
  retry with residential proxy, not "no businesses exist".
- Never invent places, phone numbers or emails that are not in the rows.
```

# Actor input Schema

## `searchStringsArray` (type: `array`):

Queries to search on Google Maps, e.g. \["coffee shop", "restaurant"]. Combined with the location below.

## `locationQuery` (type: `string`):

Free-form location (city, region, country) geocoded via OpenStreetMap, e.g. "New York, USA". The area is split into adaptive tiles to bypass the ~120 results per viewport limit.

## `customGeolocation` (type: `object`):

Optional GeoJSON Point/Polygon/MultiPolygon (or {type:'Feature', geometry: ...}). Overrides the location. Point uses a 5 km radius by default.

## `maxCrawledPlacesPerSearch` (type: `integer`):

Maximum number of places to extract per search term. 0 or empty = unlimited.

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

Google Maps interface language (hl), e.g. en, es, vi.

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

Country for the Google geo parameter (gl), lowercase ISO code, e.g. us, gb, sg.

## `categoryFilterWords` (type: `array`):

Keep only places whose categories contain at least one of these words (case-insensitive). Empty = keep all.

## `placeMinimumStars` (type: `string`):

Keep only places rated at or above this value.

## `skipClosedPlaces` (type: `boolean`):

Drop places marked permanently or temporarily closed.

## `scrapePlaceDetailPage` (type: `boolean`):

Fetch the place payload when the listing record is missing website, phone, hours or review count. Turning this off is faster but much less complete.

## `speedMode` (type: `string`):

Trade-off between wall-clock speed and retry depth. Turbo is the default and already reaches 99%+ field coverage.

## `detailMode` (type: `string`):

auto = only when a field is missing, always = every place, never = listing data only.

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

Upper bound on parallel requests. Leave empty to use the speed mode default.

## `seedByCities` (type: `boolean`):

Also seed searches from sub-areas (districts, wards) inside the location for deeper recall.

## `dedupeByPlaceId` (type: `boolean`):

Emit each place once, even when several search terms or tiles return it.

## `failOnZeroResults` (type: `boolean`):

Off by default: an empty result finishes as a successful run and the reason (blocked / no results / outside area) is reported in the run summary. Turn on to mark such runs as failed.

## `includeRunSummary` (type: `boolean`):

Append one final item with per-field coverage, timings and health flags.

## `maxRunTimeSecs` (type: `integer`):

Hard wall-clock limit for the whole run.

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

Apify Proxy with RESIDENTIAL groups is strongly recommended: Google blocks datacenter IPs within minutes. If the group is unavailable on your plan, the run falls back to the default Apify proxy automatically.

## Actor input object example

```json
{
  "searchStringsArray": [
    "coffee shop"
  ],
  "locationQuery": "New York, USA",
  "maxCrawledPlacesPerSearch": 100,
  "language": "en",
  "countryCode": "us",
  "categoryFilterWords": [],
  "placeMinimumStars": "",
  "skipClosedPlaces": true,
  "scrapePlaceDetailPage": true,
  "speedMode": "turbo",
  "detailMode": "auto",
  "seedByCities": true,
  "dedupeByPlaceId": true,
  "failOnZeroResults": false,
  "includeRunSummary": true,
  "maxRunTimeSecs": 3600,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `STATS` (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 = {
    "searchStringsArray": [
        "coffee shop"
    ],
    "locationQuery": "New York, USA",
    "language": "en",
    "countryCode": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("andrew_babo/google-maps-extractor").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 = {
    "searchStringsArray": ["coffee shop"],
    "locationQuery": "New York, USA",
    "language": "en",
    "countryCode": "us",
}

# Run the Actor and wait for it to finish
run = client.actor("andrew_babo/google-maps-extractor").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 '{
  "searchStringsArray": [
    "coffee shop"
  ],
  "locationQuery": "New York, USA",
  "language": "en",
  "countryCode": "us"
}' |
apify call andrew_babo/google-maps-extractor --silent --output-dataset

```

## MCP server setup

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

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/Bxq3C07T8mh5NbPgT/builds/Xim4gJ7jVpBvdgn9z/openapi.json
