# Google Maps Places API – Area Coverage (`datarelay/google-maps-api`) Actor

Search Google Maps places and businesses in a circle, a polygon or by text. Adaptive grid for dense areas, one record per place, and a coverage report that shows what was searched.

- **URL**: https://apify.com/datarelay/google-maps-api.md
- **Developed by:** [DataRelay](https://apify.com/datarelay) (community)
- **Categories:** Lead generation, Marketing, Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 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 API – Area Coverage

Search Google Maps for places and businesses – **in a circle, in any polygon, or by text** – and get one clean record
per place: name, categories, address, coordinates, phone, website, rating, reviews count, opening hours, accessibility,
main photo, booking links, Place ID and CID. Or get the **details of places you already know** by Place ID, CID or
Google Maps URL.

### Why this Actor

- **Covers the area, not just Google's first list.** Google Maps shows at most 100–200 places per search. For a
  circle or a polygon, the Actor searches an adaptive grid: dense districts are split into smaller cells, empty land
  stays in large cells. Coffee shops within 1.5 km of Prague's Old Town Square: one search shows 75, the grid finds ~580–600.
- **Exact boundaries.** Only places inside your circle or polygon (holes respected) are delivered and charged.
- **Coverage report.** The run summary (`OUTPUT`) lists per query the cells searched, cells that were not searched
  (time limit, blocking) and cells where Google may still know more places – so you know how complete the data is.
- **One record per place.** A place found by several queries or cells is delivered and charged once.
- **Filters before charging**: closed businesses, minimum rating / reviews, categories, website or phone only.
- **Details on demand**: rating distribution (1–5 stars), the owner's description, the topics people mention in reviews and the
  delivery / pickup services (DoorDash, Wolt, Uber Eats…) or the place's own online shop – for every
  search result (`includeDetails`) or for a list of known places (`details` mode).
- **Monitoring** (with a ready-made digest for Slack / e-mail and the rating / review history of each place): repeat a search under a sync key and get only new places, or new and changed ones (phone, hours,
  status…) with their previous values – places that disappear are reported too. Unchanged places are free. "New"
  means first found under the key: Google's lists vary from run to run, so the first runs still add places that
  existed before; `firstSeenAt` and the review count tell them apart from newly opened ones.
- **Business status done right**: temporarily and permanently closed places are told apart and skipped by default.
- **Areas by name**: `Praha 2`, `10115 Berlin`, `Kraków` – boundaries from OpenStreetMap, no drawing needed.
- **How complete is it?** An optional second pass on a shifted grid estimates how many places Google lists in the
  area (capture–recapture) – the summary says e.g. "584 found, ~620 estimated, 94 %".
- **More places with less guessing**: query expansion adds the categories the results have (`dentist` → `Dental
  clinic`, `Orthodontist`).
- **Lead tools**: exclude places you already have (CRM), filter by signals (popular without a website, unclaimed
  profile, open late…), phone type (mobile / landline), distance from a point.
- **Match your list** (`mode: match`): find the Google Maps place of each business in your CRM list (name + address,
  phone or website), with a confidence – unmatched ones are free.
- **Competitors within a radius** of each place: how many of the same type, their average rating, the nearest one.
- **Free estimate** of places and cost before a big run (`mode: estimate`).
- **Everything in an area** (`allPlaces`: ~50 broad categories merged) and **shops inside malls, airports and
  stations** from their directories (`includePlacesInside`).
- **Many areas in one run**: a GeoJSON collection of postal codes, districts or circles, each result tagged with its
  area name, and places per km² for each area.
- **Map with the search coverage** (where the grid searched, where Google may know more, what was not searched),
  **GeoJSON**, and a **flat view** for CSV / spreadsheets (hours and categories as one line).
- **Market summary** in the run's OUTPUT: places per km², top categories, rating distribution, chains vs independent
  places (by shared website, name or business profile owner).
- **Resumes after a platform migration** without delivering a place twice.

### Who uses it

- **Sales and lead generation** – every business of a kind in a city, district or postal code, with phone, website
  and signals such as "no website" or "unclaimed profile"; your existing customers excluded.
- **Market research and location planning** – how many places of a kind per km², ratings, chains vs independents,
  competitors around each place.
- **Data and CRM teams** – match your list of businesses to Google Maps (Place ID, phone, website, hours) and refresh
  known places by Place ID.
- **Monitoring** – new, changed and closed places in an area every week, as a ready-made Slack / e-mail digest.
- **AI agents and automations** – a documented input schema, one record per place, statuses for every input and a run
  summary for decisions.

### Quick start

```json
{
    "searchQueries": ["dentist"],
    "areaNames": ["Praha 2"]
}
```

More examples:

| Goal                                                  | Input                                                                                                              |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| A circle                                              | `{"searchQueries": ["coffee shop"], "centerLat": 52.52, "centerLng": 13.405, "radiusKm": 2}`                       |
| A quick text search (no area)                         | `{"searchQueries": ["pizza"], "location": "Naples, Italy", "maxPlaces": 50}`                                       |
| Several districts, tagged by name                     | `{"searchQueries": ["gym"], "areaNames": ["Praha 2", "Praha 3", "Praha 10"]}`                                      |
| Leads without a website                               | `{"searchQueries": ["plumber"], "areaNames": ["Kraków"], "requireSignals": ["no_website"], "onlyWithPhone": true}` |
| Everything in a small area                            | `{"allPlaces": true, "centerLat": 50.0755, "centerLng": 14.4426, "radiusKm": 0.3}`                                 |
| With details (rating distribution, delivery services) | `{"searchQueries": ["restaurant"], "areaNames": ["10115 Berlin"], "includeDetails": true}`                         |
| Shops inside a mall                                   | `{"searchQueries": ["Westfield Chodov"], "location": "Prague", "includePlacesInside": true}`                       |
| Known places by Place ID, CID or URL                  | `{"mode": "details", "places": ["ChIJ3XqKYCGVC0cRYE5lAu_5hSY", "https://maps.app.goo.gl/…"]}`                      |
| Match a CRM list                                      | `{"mode": "match", "matchPlaces": [{"name": "Café Ebel", "city": "Prague", "id": "crm-1"}]}`                       |
| New places every week                                 | `{"searchQueries": ["cafe"], "areaNames": ["Praha 2"], "syncKey": "praha2-cafes", "syncMode": "newOnly"}`          |
| How many places and what cost, first                  | `{"mode": "estimate", "searchQueries": ["restaurant"], "areaNames": ["Prague"]}`                                   |

### Measured

| Search                                                                                   | Places |    Time |
| ---------------------------------------------------------------------------------------- | -----: | ------: |
| Coffee shops within 1.5 km of Prague's Old Town Square (one Google Maps search shows 75) |    582 |   4 min |
| Dentists in Praha 2 (district by name, 4.2 km²)                                          |    139 | 1.7 min |
| Car repair shops within 8 km of Kladno (201 km²)                                         |    224 | 1.5 min |
| Restaurants in the whole of Prague (city by name, 495 km²)                               |  7,673 |  41 min |

Runs of 2026-10-29 and 2026-10-30 on the Apify platform, default settings (256 MB).

### Input

| Field                                                                                     | Example                            |                                                                        |
| ----------------------------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------- |
| `mode`                                                                                    | `search`                           | `search` (default), `details`, `match` or `estimate`.                  |
| `searchQueries`                                                                           | `["dentist", "dental clinic"]`     | What you would type into Google Maps.                                  |
| `allPlaces`, `expandQueries`                                                              | `true`                             | ~50 broad categories instead of queries; add the categories found.     |
| `areaNames`                                                                               | `["Praha 2", "10115 Berlin"]`      | Districts, cities or postal codes; boundaries from OpenStreetMap.      |
| `areas`                                                                                   | GeoJSON `FeatureCollection`        | Several named polygons or points with `radiusKm`.                      |
| `centerLat`, `centerLng`, `radiusKm`                                                      | `52.52`, `13.405`, `2`             | A circle (up to 150 km radius, 50,000 km²).                            |
| `polygon`                                                                                 | GeoJSON `Polygon` / `MultiPolygon` | `[longitude, latitude]`; draw one at geojson.io.                       |
| `location`                                                                                | `Prague, Czechia`                  | Text search instead of an area: quick, up to 100–200 places per query. |
| `language`, `countryCode`                                                                 | `de`, `DE`                         | Language of names, categories, addresses and hours.                    |
| `maxPlaces`, `maxPlacesPerQuery`, `maxRunSecs`                                            | `500`                              | Limits (0 = none).                                                     |
| `skipClosed`, `minRating`, `minReviews`, `categories`, `onlyWithWebsite`, `onlyWithPhone` |                                    | Filters; filtered places are not charged.                              |
| `requireSignals`, `excludePlaces`, `nameMatch`, `distanceFromLat` / `Lng`                 | `["no_website"]`                   | Lead filters, your known places, name must contain the query.          |
| `includeDetails`, `includePlacesInside`, `competitorRadiusMeters`, `estimateCompleteness` | `true`, `300`                      | Details per place, places inside malls, competitors, completeness.     |
| `places` (details), `matchPlaces` (match)                                                 | Place IDs / `{"name", "city"}`     | Known places to refresh; a business list to match.                     |
| `syncKey`, `syncMode`, `syncBaselineRuns`, `syncReset`                                    | `"praha2-cafes"`, `newOnly`        | Monitoring: only new or new + changed places on repeated runs.         |
| `fields`                                                                                  | `["phone", "website"]`             | Deliver only these fields (plus `fid`, `placeId`, `name`).             |

Every field is described in the input form.

### Output

One dataset item per place:

```json
{
    "name": "Die Praxis Mitte - Zahnarztpraxis Dr. Christina Erdmann & Dr. Anna Trojan",
    "placeId": "ChIJm7m4g-1PqEcR9qybv-6X6Ps",
    "cid": "18151925349789445366",
    "fid": "0x47a84fed83b8b99b:0xfbe897eebf9bacf6",
    "categories": ["Zahnarzt", "Zahnästhetische Praxis", "Kinderzahnarzt"],
    "address": "Hinterhaus, Münzstraße 5, 10178 Berlin",
    "street": "Hinterhaus, Münzstraße 5",
    "neighborhood": "Mitte",
    "locality": "Berlin",
    "postalCode": "10178",
    "countryCode": "DE",
    "location": { "lat": 52.5240799, "lng": 13.4086904 },
    "rating": 4.7,
    "reviewCount": 152,
    "website": "https://www.die-praxis.berlin/",
    "phone": "030 40367600",
    "phoneInternational": "+493040367600",
    "openingHours": [
        {
            "day": "wednesday",
            "date": "2026-10-28",
            "text": "08:00–20:00",
            "intervals": [{ "open": "08:00", "close": "20:00" }]
        },
        { "day": "saturday", "date": "2026-10-31", "text": "Geschlossen", "intervals": [] }
    ],
    "timezone": "Europe/Berlin",
    "closed": false,
    "googleMapsUrl": "https://www.google.com/maps/search/?api=1&query=…&query_place_id=ChIJm7m4g-1PqEcR9qybv-6X6Ps",
    "searchQuery": "dentist",
    "rank": 1,
    "missingFields": [],
    "fetchedAt": "2026-10-28T13:17:56.934Z"
}
```

Also in every record: `phoneType`, `leadSignals`, `distanceMeters` (with a circle or a point), `reviewHistory` (with a
sync key), `categoriesText` and `openingHoursText` (one line each, for CSV), `openingHoursSpecification`
(schema.org), `areaName` (with `areas`), `businessStatus` (`OPERATIONAL`, `CLOSED_TEMPORARILY`, `CLOSED_PERMANENTLY`), `googleType`
(language-independent: `restaurant`, `dentist`, `lodging`…), `hotelStars`, `claimThisBusiness`, `openNow`, and with
details `plusCode`. With a sync key: `change` (`new`, `changed`, `missing`), `changedFields`, `previousValues`,
`firstSeenAt`.

More fields in every record: `attributes` – accessibility for now (`{ "group": "Accessibility", "key": "has_wheelchair_accessible_entrance",
"name": "Wheelchair-accessible entrance", "value": true }`), `imageUrl`, `reservationLinks`
(`{ "provider": "OpenTable", "url": "…" }`) and `specialHours` (brunch, delivery…). With details:

```json
{
    "ratingDistribution": { "1": 19, "2": 25, "3": 60, "4": 120, "5": 1129 },
    "ownerDescription": "The Miners Old Town lies right in the heart of Prague, next to Staroměstské náměstí. …",
    "reviewTopics": ["specialty coffee", "flat white", "outdoor seating"],
    "photoCount": 1048,
    "photos": ["https://lh3.googleusercontent.com/…=w1024-k-no"],
    "orderLinks": [
        { "provider": "DoorDash", "url": "https://www.doordash.com/store/…", "services": ["delivery", "pickup"] },
        { "provider": "Wolt", "url": "https://wolt.com/…", "services": ["delivery", "pickup"] }
    ]
}
```

In the details mode every input gets a record: `status` `ok` (charged), or `not_found` / `invalid_input` / `failed`
(only `input` and `status`, not charged).

`null` means Google shows no value for the place; `missingFields` lists such key fields. Opening hours are those
Google shows for the coming seven days (holidays included). Large ids are strings.

The key-value store also holds `MAP` – an HTML map of the places with the search coverage and a density layer –,
`DIGEST` (with a sync key) and `GEOJSON` (open both from the run's Output
tab). The run summary (`OUTPUT` in the key-value store) holds, per query: delivered and found places, cells, pages,
duplicates, places outside the area, `saturatedCells` and the list of `unsearched` cells with the reason.

### Integrations

**API – places as JSON in one call** (waits for the run, up to 5 minutes):

```bash
curl -X POST "https://api.apify.com/v2/acts/datarelay~google-maps-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"searchQueries": ["dentist"], "areaNames": ["Praha 2"], "maxPlaces": 200}'
```

For a spreadsheet-ready CSV add
`&format=csv&fields=name,categoriesText,address,phone,website,rating,reviewCount,openingHoursText,googleMapsUrl`
(hours and categories as one line each).

**Google Sheets** – in the Actor's (or a saved task's) *Integrations* tab add *Google Sheets*, choose the dataset
view *Flat (CSV / spreadsheet)*: every finished run appends or replaces the rows of your sheet.

**Weekly monitoring with a Slack message:**

1. Save a task with your search, `"syncKey": "my-area"` and `"syncMode": "changes"` (the first run only remembers the
   places).
2. Schedule the task (e.g. every Monday 7:00) in *Schedules*.
3. In the task's *Integrations* add *Slack* (or a webhook to Make / Zapier / n8n) on *Run succeeded*, with the run's
   key-value store record `DIGEST` – a ready-made Markdown list of new, changed and missing places:

```
**my-area**: 3 new, 2 changed, 1 missing

*New*
• Coffee Bird – Pštrossova 1924/8, 110 00 Nové Město
…
```

**CRM enrichment** – run `"mode": "match"` with your list of businesses (`matchPlaces`, with your `id`s) and import
the dataset back by `inputId`: Place ID, phone, website, rating, hours and the match confidence.

### Pricing – what a job costs

Pay per event; platform usage and proxies are included (no compute or proxy costs on top).

| Event           | Price (FREE / BRONZE)   | When                                                                       |
| --------------- | ----------------------- | -------------------------------------------------------------------------- |
| `run-start`     | $0.001                  | once per run, together with the first paid result                          |
| `place`         | $1.33 / $1.00 per 1,000 | delivered place: unique, inside the area, passing the filters              |
| `place-details` | $0.66 per 1,000         | details delivered (`includeDetails`, `details` mode, `match` with details) |

Higher plans pay less: `place` $0.66 (SILVER) and $0.50 (GOLD and above) per 1,000; `place-details` $0.50 and
$0.35. Filters are free.

| Typical job (BRONZE)                              | Cost   |
| ------------------------------------------------- | ------ |
| All 139 dentists in Prague 2                      | $0.14  |
| 582 coffee shops within 1.5 km of central Prague  | $0.58  |
| 1,000 places with details                         | $1.66  |
| 7,673 restaurants in all of Prague (495 km²)      | $7.67  |
| Weekly monitor of an area, 5 new places in a week | $0.006 |

**You don't pay** for places removed by filters or exclusions, duplicates, unchanged places on monitored searches,
inputs that were not found, invalid or failed (details and match modes), the `estimate` mode, or a run that delivers
nothing (no start fee either). Use `mode: estimate` for the expected number of places and the cost before a large run.

### Reliability (measured)

- **Scale:** all restaurants in Prague (495 km²): 7,673 places in 41 minutes, 2,488 Google requests, none blocked, on
  the default 256 MB.
- **Proxies:** pages go out through Apify's datacenter proxy first and switch to residential only for pages Google
  blocked twice – in our runs fewer than 1 % of datacenter requests were blocked.
- **No duplicates after a restart:** when the platform migrates a run, it resumes from its saved state and skips the
  places already in the dataset (tested: 855 of 855 unique).
- **Checked every hour:** scheduled control runs and alerts on blocked requests, parser misses and changes of Google's
  response format.
- If Google blocks every request of a run, the run finishes with `outcome: "no_results"` in `OUTPUT` and nothing is
  charged.

### Limits and good to know

- No reviews, photo galleries, popular times, price level or the full About tab – Google shows them only to signed-in
  users, and this Actor does not use Google accounts (details include the rating distribution, review topics and a few
  photos, not the reviews themselves).
- "Complete" means: every place Google lists for the query in the searched cells. Google does not list every business
  for every query; use several queries (`dentist`, `dental clinic`, `orthodontist`) or `expandQueries` for the best
  coverage. `OUTPUT` lists the cells where Google may still know more (`saturatedCells`).
- Google's lists vary a little between runs: two runs of the same search can differ by a few percent.
- A text search (`location`) returns at most the 100–200 places Google lists for it – use an area for more.
- Area names are resolved by OpenStreetMap (Nominatim); check `OUTPUT.area` that it found the area you meant.
- Circles, polygons and area names crossing the 180° meridian (Fiji, Chukotka, the Aleutians) are split at it
  automatically. Areas over 50,000 km² should be split into several runs.

### FAQ

**Is it legal to collect this data?** The Actor reads what Google Maps shows publicly to anyone without signing in:
business names, addresses, opening hours, phone numbers and websites of businesses. It does not collect reviews or
reviewers' names. If you process the data of sole traders or people, data protection rules such as GDPR apply to your
use of it.

**How is it different from other Google Maps scrapers?** It covers areas, not single lists: the adaptive grid and exact
boundaries find several times more places than one search, and the coverage report shows what was searched. It reads
Google's data API directly instead of a browser, so it is fast and inexpensive.

**Why are there no reviews?** Google shows reviews only to signed-in users; this Actor does not use Google accounts.

**How do I get only new places?** Use a `syncKey` with `syncMode: "newOnly"` (or `changes`) and schedule the task.

**Can I get the data into Google Sheets or my CRM?** Yes – see Integrations; the `flat` dataset view is ready for CSV
and spreadsheets.

### Not affiliated with Google

This is an independent Actor by DataRelay, not Google's official Places API. It reads the same data that the public
Google Maps website shows.

### Usage statistics

The Actor sends anonymous run statistics to the developer (mode and options, numbers of places and statuses, Google
request counts, duration, the kind and size of the area and its centre rounded to ~10 km). Search queries, locations
and your Apify user id are never sent – only keyed hashes, used to count repeated queries and distinct users. No
results, place lists, account details or personal data are collected.

### Changelog

- 0.1 – first version.

# Actor input Schema

## `mode` (type: `string`):

`search` – find places by search queries in an area or by text (default). `details` – full details of places you already know (Place IDs, CIDs or Google Maps URLs). `match` – find the Google Maps place of each business in your list (name + address, e.g. from a CRM), with a confidence. `estimate` – free estimate of how many places a search would deliver and what it would cost.

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

What to search for, as you would type it into Google Maps: `coffee shop`, `dentist`, `car repair`. Each place is delivered once per run, even when several queries find it. Search mode only.

## `allPlaces` (type: `boolean`):

Search ~50 broad categories (restaurants, shops, services, health, education, offices…) and merge them: "everything" in the area – Google Maps has no search for all places. Costs as many searches as categories; try `mode: estimate` first.

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

Details mode: Place IDs (`ChIJ…`), CIDs, feature ids (`0x…:0x…`) or Google Maps URLs (place links, `?cid=` links, maps.app.goo.gl short links). Unknown or invalid entries are reported with a status and not charged.

## `matchPlaces` (type: `array`):

Match mode: one business per entry – a line "Name, street, city" or an object { "name", "address", "city", "country", "phone", "website", "id" }. Phone and website confirm the match; `id` is returned as `inputId`. Matches are charged; inputs without a confident match are reported free with the closest candidates.

## `includeDetails` (type: `boolean`):

Search mode: fetch the details of every delivered place (rating distribution, the owner's description, review topics). One extra request per place, charged as a separate event only when the details were delivered.

## `expandQueries` (type: `boolean`):

After the search, also search the categories that many of the found places have (e.g. `dentist` → `Dental clinic`, `Orthodontist`) – more places without knowing Google's category names. Up to 3 extra queries per query.

## `includePlacesInside` (type: `boolean`):

For malls, airports, stations and similar places found, also deliver the places in their directory (shops, restaurants, services) as separate records with `insideOf` – each charged as a place.

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

Optional. Searches "<query> in <location>" and lets Google pick the area – quick, but Google returns at most 100–200 places per query. For complete coverage of an area use a circle or a polygon below instead.

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

More text locations, each searched with every query (e.g. `Prague`, `Brno`, `Ostrava`). Each place is delivered once.

## `centerLat` (type: `number`):

Latitude of the circle's centre, e.g. `50.0875`. The circle is ignored when area names or a text location are given.

## `centerLng` (type: `number`):

Longitude of the circle's centre, e.g. `14.4213`.

## `radiusKm` (type: `number`):

Radius in kilometres (up to 150; the area may be up to 50,000 km²).

## `polygon` (type: `object`):

Instead of a circle: a GeoJSON `Polygon` or `MultiPolygon` (also inside a `Feature`), coordinates as `[longitude, latitude]`. Holes are respected. Draw one at geojson.io and paste it here.

## `areas` (type: `object`):

Search several areas in one run, e.g. postal codes or districts: a GeoJSON FeatureCollection whose features are Polygons / MultiPolygons, or Points with `properties.radiusKm` (circles). `properties.name` names the area in the results (`areaName`) and in the summary. Each place is delivered once, for the first area that finds it. Use instead of the circle or the polygon.

## `areaNames` (type: `array`):

Names of districts, towns, postal codes or regions (e.g. `Praha 2`, `10115 Berlin`, `Kraków`): their boundaries come from OpenStreetMap (© OpenStreetMap contributors) and each is searched like a polygon. Names without a boundary (a street, a single building) are rejected.

## `estimateCompleteness` (type: `boolean`):

Search the area a second time on a shifted grid and estimate (capture–recapture) how many places Google lists for the queries – the summary tells how complete the result is. About twice the requests; places found only by the second pass are delivered too.

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

Language of names, categories, addresses and opening hours, e.g. `en`, `de`, `pl`.

## `languages` (type: `array`):

Search again in these languages (e.g. `cs`, `de`): Google ranks places differently per language, so more languages can find more places. Records are in the language of the search that found them first.

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

Optional two-letter country code (`US`, `DE`) for Google's regional ranking.

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

Stop after this many places in total (0 = no limit). You pay only for delivered places.

## `maxPlacesPerQuery` (type: `integer`):

Limit per search query (0 = no limit).

## `maxRunSecs` (type: `integer`):

Stop early with the places found so far (0 = until the run's timeout). The summary lists the cells that were not searched.

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

Leave out places Google marks as closed. Filtered places are not charged.

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

Only places rated at least this (0–5). Places without a rating are left out when set.

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

Only places with at least this many reviews.

## `categories` (type: `array`):

Only places with a category containing one of these words (any language Google shows), e.g. `cafe`, `bakery`. Empty = all.

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

Leave out places without a website.

## `onlyWithPhone` (type: `boolean`):

Leave out places without a phone number.

## `requireSignals` (type: `array`):

Lead filters: e.g. `popular_without_website` (100+ reviews, no website), `unclaimed_profile`, `no_website`, `few_reviews`, `low_rating`, `open_sundays`, `open_late`, `open_24_hours`, `no_phone`. All selected must apply.

## `excludePlaces` (type: `array`):

Place IDs, CIDs or Google Maps URLs you already have (e.g. from your CRM): skipped and not charged.

## `distanceFromLat` (type: `number`):

Adds `distanceMeters` from this point to each place (default: the circle's centre).

## `distanceFromLng` (type: `number`):

Longitude of the point for `distanceMeters`.

## `competitorRadiusMeters` (type: `integer`):

For each delivered place, count the delivered places of the same Google type within this radius, their average rating and the nearest one (record COMPETITION, summary in OUTPUT.analytics). 0 = off. The count covers what this run searched.

## `nameMatch` (type: `string`):

`all` – every place Google finds (default). `contains` – only places whose name contains the query (e.g. all "Starbucks" branches). `exact` – the name equals the query. Case and accents are ignored.

## `syncKey` (type: `string`):

A name for a search you repeat (e.g. `prague-cafes`). Places are remembered under this key in your account's storage, so later runs can deliver only new or changed places. Letters, digits and dashes.

## `syncMode` (type: `string`):

`newOnly` – deliver (and pay for) only places not delivered under this key before ("new" = first found under the key; Google's lists vary, so early runs still add places that existed before). `changes` – deliver new places and places whose name, address, phone, website, rating, status or opening hours changed (with the previous values), and report places not found in two complete runs in a row as `missing` (free). Unchanged places are not delivered or charged.

## `syncReset` (type: `boolean`):

Forget what was remembered under the key (e.g. after changing the queries or the area) and start again.

## `syncBaselineRuns` (type: `integer`):

Runs under a new sync key that only remember places, delivering and charging nothing – so the first report of changes is not a list of everything. Default: 1 for `changes`, 0 for `newOnly`.

## `fields` (type: `array`):

Only these fields in each record (empty = all), e.g. `phone`, `website`, `rating`. `fid`, `placeId` and `name` are always included.

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

With residential proxies (default), pages go out through the cheaper datacenter proxy first and switch to residential only after Google blocks a page twice. Other proxy settings are used as they are.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "coffee shop"
  ],
  "allPlaces": false,
  "matchPlaces": [
    "The Miners Coffee, Železná 14, Praha",
    {
      "name": "La Pecora Bianca",
      "address": "20 W 40th St",
      "city": "New York",
      "id": "crm-42"
    }
  ],
  "includeDetails": false,
  "expandQueries": false,
  "includePlacesInside": false,
  "centerLat": 50.0875,
  "centerLng": 14.4213,
  "radiusKm": 1,
  "estimateCompleteness": false,
  "language": "en",
  "maxPlaces": 50,
  "maxPlacesPerQuery": 0,
  "maxRunSecs": 0,
  "skipClosed": true,
  "minRating": 0,
  "minReviews": 0,
  "categories": [],
  "onlyWithWebsite": false,
  "onlyWithPhone": false,
  "requireSignals": [],
  "competitorRadiusMeters": 0,
  "nameMatch": "all",
  "syncMode": "off",
  "syncReset": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

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

No description

## `map` (type: `string`):

No description

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

No description

## `digest` (type: `string`):

No description

## `competition` (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 = {
    "searchQueries": [
        "coffee shop"
    ],
    "matchPlaces": [
        "The Miners Coffee, Železná 14, Praha",
        {
            "name": "La Pecora Bianca",
            "address": "20 W 40th St",
            "city": "New York",
            "id": "crm-42"
        }
    ],
    "centerLat": 50.0875,
    "centerLng": 14.4213,
    "radiusKm": 1,
    "maxPlaces": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("datarelay/google-maps-api").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": ["coffee shop"],
    "matchPlaces": [
        "The Miners Coffee, Železná 14, Praha",
        {
            "name": "La Pecora Bianca",
            "address": "20 W 40th St",
            "city": "New York",
            "id": "crm-42",
        },
    ],
    "centerLat": 50.0875,
    "centerLng": 14.4213,
    "radiusKm": 1,
    "maxPlaces": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("datarelay/google-maps-api").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": [
    "coffee shop"
  ],
  "matchPlaces": [
    "The Miners Coffee, Železná 14, Praha",
    {
      "name": "La Pecora Bianca",
      "address": "20 W 40th St",
      "city": "New York",
      "id": "crm-42"
    }
  ],
  "centerLat": 50.0875,
  "centerLng": 14.4213,
  "radiusKm": 1,
  "maxPlaces": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call datarelay/google-maps-api --silent --output-dataset

```

## MCP server setup

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

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/enAGQPe4qJgGtJnH6/builds/qJYtkXB7i7O97cDO7/openapi.json
