# Google Maps Places Scraper: Businesses, Phones & Websites (`tidyharvest/google-maps-places-scraper`) Actor

Built to beat Google Maps Scraper: 98% of results in the city you asked for (theirs 77%), more places in whole-city runs (339 vs 138 Leeds plumbers), structured addresses, closed and unclaimed flags. $1.00 per 1,000 places vs ~$2.70 there.

- **URL**: https://apify.com/tidyharvest/google-maps-places-scraper.md
- **Developed by:** [TidyHarvest](https://apify.com/tidyharvest) (community)
- **Categories:** Lead generation, Travel
- **Stats:** 3 total users, 2 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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: Businesses, Phones & Websites

Search Google Maps the way you would in the app and get back structured **business listings**: name, category,
**street, city, postal code, state and country**, **phone** (local and E.164), **website**, rating, review count,
**opening hours**, **booking links**, **permanently closed** and **claimed/unclaimed** flags, plus code, coordinates and
photo. Give it a **whole city** and it finds every place of that type inside the city's boundary, past Google's
\~120-results-per-search limit, and **nothing from outside it**. Give it a **list of known businesses** and each line comes
back as its best match, labelled by how well name and street agree. No login or cookies needed:
**from $1.00 per 1,000 places**, less on higher Apify plans.

### Compared with Google Maps Scraper

We built this to beat [Google Maps Scraper](https://apify.com/compass/crawler-google-places) (`compass/crawler-google-places`),
the most-used Maps actor on Apify Store. In October 2026 we ran both on the same inputs, theirs on its free tier with the
detail page off (its default), and checked 25 random places against their Google Maps pages by hand.

| | This actor | Google Maps Scraper |
|---|---|---|
| Results inside the city searched (34 "category in city" searches, 25 countries, first 30 each) | **97.7%** | 77.3% |
| Results more than 50 km outside it | **0%** | 21% (New York places for "pizzeria a Napoli") |
| Known businesses (101 from OpenStreetMap, 15 cities) matched to the right place | **79%** | 73% |
| ...matched to a wrong place | **8%** (no-match lines come back free, labelled) | 22% |
| Whole city, dentists in Boulder, CO: relevant places found inside the city | **162** | 145 |
| Whole city, plumbers in Leeds, UK | **339** | 138 |
| Phone / website / street / hours on search results | 96.6% / 89.4% / 95.9% / 90.8% | 96.5% / 90.6% / 94.9% / 87.9% |
| Price per 1,000 places (Free plan / Gold plan) | **$1.00 / $0.45** | $4.00 / $1.50 (our test runs cost $2.63-$3.00) |

In four more whole-city tests their run stopped at a 500-place cap, so compare with care: restaurants in Bruges, we found
488 (their 500 held 444 restaurants); cafés in Porto 453 (325); hair salons in Utrecht 635 (484); auto repair in Calgary
758 (494). On every Apify plan this one costs about a quarter to a third of theirs.

**Where Google Maps Scraper still does more:** reviews, photos, popular times and the place-page extras (owner updates,
Q\&A, hotel prices), polygon and place-ID inputs, and speed on big areas: it covered each city in 40-140 seconds, this one
takes 6-8 minutes. **[Google Maps Scraper Pro](https://apify.com/tidyharvest/google-maps-scraper-pro)** (same engine) adds
all of these except Q\&A: reviews with owner replies, the star breakdown, popular times, photos, website emails, drawn
areas, place IDs, postal codes, and whole cities in about 2 minutes, at 70% of Google Maps Scraper's prices.

### How well it works

Measured in October 2026 on Apify cloud at the default 256 MB.

- **25 random places checked against their Google Maps pages:** name, open/closed status, phone, website and hours matched
  on all 25, the address on 22 (the 3 misses were Lagos postal codes, fixed since).
- **3,959 places from 34 searches in 25 countries in under 5 minutes:** phone 95%, website 82%, street address 93%, postal
  code 86%, hours 87%, and 98.6% match the category searched. Every phone also comes in E.164 (+44...).
- **Strict location:** places Google mixes in from another town with the same name (Glasgow, Kentucky for
  `electricians in Glasgow`) are dropped and not charged: 54 such rows in 3,987 before, 1 now.
- **Lookups:** on 145 businesses drawn from OpenStreetMap, wrong-place answers fell from 17% to 8%. 14 of the matched
  places turned out to be permanently closed, and they are flagged.
- **Whole-city mode** keeps only places inside the city's boundary, so you never pay for the next town over.

### What you can do with it

- **Every business of a type in a city:** `dentists` + location `Boulder, CO` returns all ~160, not the first 120, and none from Denver.
- **Local lead lists:** phones, websites, structured addresses and a `claimed` flag (unclaimed listings are owners who
  haven't set up their Google profile) for `plumbers` in your area. Add emails and socials with the contacts add-on.
- **Check or refresh a list of known businesses:** `Name, street, city` per line, or Google Maps links. Each comes back
  with `lookupMatch` (`name+address`, `name`, `address`) or a free `none` row, plus `permanentlyClosed`.
- **Only new places on a schedule:** pass last run's dataset id in `skipDatasetIds`. Places you already have are skipped and not charged.

### Input

| Field | What it does |
|---|---|
| `searches` | What you would type into Maps, one per line. With `location`, just the category (`coffee shops`). For lookups: `Name, street, city` or a Google Maps link. |
| `location` | City, district or region. Turns on whole-area mode: every matching place inside its boundary, past the ~120 limit. |
| `splitArea`, `maxCellsPerSearch` | Whole-area mode on/off, and its search budget per category (default 60; higher = more complete for big cities, slower). |
| `maxPlacesPerSearch` | Cap per search: ~120 for a normal search, thousands in whole-area mode. `1` = lookup mode for known businesses. |
| `latitude`, `longitude`, `radiusMeters` | Center a normal search on a point instead. |
| `strictLocation` | On by default: drop (and don't charge) places outside the area you asked for. |
| `includeContacts` | Read each place's website for public emails and social links (add-on, see pricing). |
| `keywords`, `minRating`, `minReviews`, `requireWebsite`, `skipClosedPlaces` | Filters applied before you are charged. |
| `skipPlaceIds`, `skipDatasetIds` | Places you already have (ids, or earlier runs' datasets): skipped and not charged. |
| `dedupeAcrossSearches` | On by default. Turn off for lookup lists so every input line gets its row (match rows back with `searchIndex`). |
| `language`, `country` | Result language and country bias. |
| `maxItems` | Hard cap on results for the whole run (also caps cost). |

Examples:

```json
{ "searches": ["dentists", "orthodontists"], "location": "Boulder, CO", "minRating": 4.3 }
```

```json
{ "searches": ["Hawksworth Village Bakery, 12 Broadway, Leeds", "The Fillmore, 1805 Geary Blvd, San Francisco"], "maxPlacesPerSearch": 1, "dedupeAcrossSearches": false }
```

### Output

One item per place:

```json
{ "title": "Press Club", "primaryTypeDisplayName": "Wine bar", "categories": ["Wine bar", "Bar", "Cocktail bar"],
  "street": "20 Yerba Buena Ln", "city": "San Francisco", "postalCode": "94103", "state": "California", "countryCode": "US",
  "fullAddress": "Press Club, 20 Yerba Buena Ln, San Francisco, CA 94103", "neighborhood": "SoMa",
  "phone": "(415) 744-5000", "phoneInternational": "+14157445000", "website": "http://www.pressclubsf.com/",
  "rating": 4.5, "reviewsCount": 757, "latitude": 37.7859, "longitude": -122.4043, "plusCode": "849VQHPW+97",
  "businessStatus": "OPERATIONAL", "permanentlyClosed": false, "claimed": true, "serviceArea": false,
  "openingHours": { "Monday": "Closed", "Tuesday": "5–9 PM", "Friday": "5–10 PM" },
  "bookingLinks": [{ "provider": "opentable.com", "url": "https://www.opentable.com/..." }],
  "attributes": { "Accessibility": ["Wheelchair accessible entrance"] },
  "placeId": "ChIJ5VjkK4aAhYAR4JdQs3IUjQA", "cid": "39710454583433184", "rank": 1,
  "googleMapsUri": "https://www.google.com/maps/place/?q=place_id:ChIJ5VjkK4aAhYAR4JdQs3IUjQA",
  "googleMapsCidUrl": "https://maps.google.com/?cid=39710454583433184",
  "searchQuery": "wine bars in San Francisco", "searchIndex": 0 }
```

Lookups add `lookupMatch`. `serviceArea: true` marks businesses that show no address on Google (mobile trades). Their
pin is Google's approximation. With `includeContacts`: `emails` and `socialProfiles`.

### Pricing

- **Places** (`place-scraped`), per 1,000: **$1.00** on the Free plan, $0.80 Bronze, $0.60 Silver, $0.45 on Gold and above.
- **Contacts add-on: $0.20 per 1,000 places** (`contact-details-scraped`), only when `includeContacts` is on and only for places that list a website.
- $0.00005 per run start.
- Not charged: places outside the area (`strictLocation`), filtered or skipped places, and lookup lines with no confident match.

A run fails, instead of returning an empty dataset, when nothing is found or Google stops answering.

### Limits and notes

- A normal search returns at most ~120 places. Use `location` for more.
- Lookups work best as `Name, street, city`. Without a country, an ambiguous town can resolve to the US one
  (Manchester, CT), so add the country or set `country`.
- No reviews, photo galleries or popular times here: those are in [Google Maps Scraper Pro](https://apify.com/tidyharvest/google-maps-scraper-pro).
- If Google answers with a block, consent or CAPTCHA page, the run stops instead of retrying. There is no CAPTCHA solving.

### Is it legal?

It reads publicly visible business listings that the businesses publish themselves.
Google's terms restrict automated access to Maps, and you are responsible for how you use the data (for example, rules
on marketing calls and emails in your country). Not affiliated with Google.

# Actor input Schema

## `searches` (type: `array`):

What you would type into Google Maps, one per line: `coffee shops`, `plumbers`, `wine bars in San Francisco`. For a list of known businesses, one per line as `Name, street, city` (or paste Google Maps place links) and set Max places per search to 1: each line gets its best match, labelled by how well name and street agree, and a line with no confident match comes back as a free `lookupMatch: none` row. With a Location below, write just the category (`coffee shops`).

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

A city, district or region (`Austin, TX`, `Brooklyn`, `Bruges, Belgium`). Each search then covers the whole area, past Google Maps' ~120-results-per-search limit. Places outside the area's boundary are dropped and not charged.

## `splitArea` (type: `boolean`):

With a Location: keep searching until the whole area is covered or the search budget is used. Off = one search of up to ~120 places centred on the location.

## `maxCellsPerSearch` (type: `integer`):

Search budget per category in whole-area mode. The run stops earlier when the area is covered. 60 covers one category in a city of ~100k; raise it for large cities or broad categories like restaurants.

## `maxPlacesPerSearch` (type: `integer`):

Cap per search. Default 60 for a normal search (Google stops at ~120); no cap in whole-area mode unless you set one. 1 = lookup mode for lists of known businesses (see Searches).

## `latitude` (type: `number`):

Center the search here. Use with Longitude and Radius.

## `longitude` (type: `number`):

Center the search here.

## `radiusMeters` (type: `integer`):

Search area around the coordinates. Default 10,000.

## `strictLocation` (type: `boolean`):

Google sometimes mixes in places from another city with the same name (Glasgow, Delaware for `electricians in Glasgow`) or another country. On: those are dropped and not charged (counted in the run's SUMMARY). Service-area businesses with no public address are kept.

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

Also read each place's own website for public emails and Instagram, Facebook, TikTok, X, YouTube and LinkedIn links. Add-on: $0.20 per 1,000 places that have a website.

## `keywords` (type: `array`):

Keep a place only if its name, categories or description contains one of these.

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

Keep places rated at least this (0-5).

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

Keep places with at least this many reviews.

## `requireWebsite` (type: `boolean`):

Drop places that list no website.

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

Leave out places Google marks as permanently closed (not charged). Off: they are returned with `permanentlyClosed: true`.

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

Result language code, for example `en`, `es`, `de`.

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

Two-letter country code that biases results, for example `us`, `gb`, `mx`.

## `useApifyProxy` (type: `boolean`):

Route requests through Apify datacenter proxy. Leave off unless Google refuses the run.

## `maxItems` (type: `integer`):

Hard cap on results for the whole run (also caps your cost). 0 or empty = no limit.

## `dedupeAcrossSearches` (type: `boolean`):

Return each place once per run. Turn off when you look up a list of businesses and want one row per input line, even for duplicates.

## `skipPlaceIds` (type: `array`):

placeId, fid or cid values to leave out (not charged). For scheduled runs that should return only new places.

## `skipDatasetIds` (type: `array`):

Dataset ids (or dataset URLs) of earlier runs of this actor: every place in them is skipped and not charged.

## Actor input object example

```json
{
  "searches": [
    "wine bars in San Francisco"
  ],
  "splitArea": true,
  "maxCellsPerSearch": 60,
  "strictLocation": true,
  "includeContacts": false,
  "requireWebsite": false,
  "skipClosedPlaces": false,
  "language": "en",
  "country": "us",
  "useApifyProxy": false,
  "dedupeAcrossSearches": true
}
```

# Actor output Schema

## `places` (type: `string`):

One item per place.

# 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 = {
    "searches": [
        "wine bars in San Francisco"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidyharvest/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 = { "searches": ["wine bars in San Francisco"] }

# Run the Actor and wait for it to finish
run = client.actor("tidyharvest/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 '{
  "searches": [
    "wine bars in San Francisco"
  ]
}' |
apify call tidyharvest/google-maps-places-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidyharvest/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/9zu68r9YhB1mGYPJ5/builds/n59zhIeXYugKlHD1H/openapi.json
