# Google Maps Scraper - Places, Phones, Websites, Emails, Reviews (`kantolabs/google-maps-scraper`) Actor

Google Maps scraper for leads: name, category, address, phone, website, rating, reviews count, hours, coordinates and IDs from searches, Maps URLs or place IDs. Add-ons: emails + socials from the business website, details, top reviews. Grid search past Google's cap, dedup, lead score.

- **URL**: https://apify.com/kantolabs/google-maps-scraper.md
- **Developed by:** [Kanto Labs](https://apify.com/kantolabs) (community)
- **Categories:** Lead generation, Travel
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.39 / 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

### What does this Google Maps scraper do?

This **Google Maps scraper** turns any Google Maps search into a clean list of businesses: **name, category, full address, phone, website, rating, review count, opening hours, coordinates, place ID and Maps link**, one row per place. Type what you would type into Google Maps ("dentist", "coffee shop", "plumber"), add a city or country, and get a **lead list** you can download as CSV/Excel or pull through the API.

Optional add-ons, charged **only when the data is actually delivered**:

- **Emails and social profiles** from the business's own website (Facebook, Instagram, LinkedIn, X, YouTube, TikTok, extra phone numbers)
- **Place details**: plus code, description, 1-5 star distribution, review keywords, booking and order links
- **Reviews**: the newest of the reviews Google shows to signed-out visitors (up to 5 per place)

### Why this one

- **Gets past Google's ~240-results-per-search limit.** When you ask for more, the area is split into a grid and searched cell by cell. A test run for "dentist" in Houston returned exactly 400 unique places.
- **Stays inside your location.** Places are checked against the real city boundary from OpenStreetMap, not a rough rectangle. Places outside the area are dropped for free.
- **Stops exactly at your limit.** Ask for 150, get 150.
- **No duplicates.** A place found by two search terms, or by two grid cells, is returned and charged once.
- **Filters are free.** Category, minimum rating, with/without website and skip-closed cost nothing extra.
- **Only new places mode.** Point it at the dataset of an earlier run and it skips every place you already have. Run it weekly to catch newly listed businesses.
- **Lead score (0-100)** on every row: website, phone, email, rating, review volume, open status.
- **CSV-friendly views**: "Places", "Leads: contacts" and "Details" tables with flat columns.
- **Failed lookups are free** and show up as clear error rows instead of a silent empty run.

### How much does it cost to scrape Google Maps?

Pay per result, no subscription:

| Event | Free plan | Starter (Bronze) | Scale (Silver) | Business (Gold) | When it is charged |
|---|---|---|---|---|---|
| Place | $0.0037 | $0.0028 | $0.00185 | $0.00139 | every place delivered |
| Contact details | $0.00185 | $0.00185 | $0.00139 | $0.00097 | only when an email, social profile or phone was found on the website |
| Place details | $0.00185 | $0.00185 | $0.00139 | $0.00097 | only when the place page loaded |
| Review | $0.00046 | $0.00046 | $0.00034 | $0.00024 | per review delivered |

Every tier is below the most-used Google Maps scraper's price for the same tier. Filters, duplicates, places outside the area and errors are free.

**Worked example.** 1,000 dentists with the minimum-rating filter and the contacts add-on, with contacts found for 700 of them: 1,000 x $0.0037 + 700 x $0.00185 = **$4.99**. The same job on the most-used Google Maps scraper at its Free-plan prices: 1,000 x $0.004 (place) + 1,000 x $0.001 (filter) + 1,000 x $0.002 (contacts) = **$7.00**. Duplicates, filtered-out places and errors are never charged here.

You can cap any run with **Max cost per run**; the scraper stops cleanly when the cap is reached.

### How to use it

1. Enter one or more **search terms** and a **location** ("Austin, TX, USA", "Manila, Philippines", "Praha").
2. Set **Number of places per search**.
3. Optional: filters, the add-ons, a language, or a custom circle (latitude, longitude, radius).
4. Run, then download the table as CSV, Excel or JSON.

You can also paste **Google Maps URLs** (search or place links) or **place IDs** (ChIJ..., CID numbers).

#### Input example

```json
{
  "searchStringsArray": ["dentist"],
  "locationQuery": "Austin, TX, USA",
  "maxCrawledPlacesPerSearch": 100,
  "scrapeContacts": true,
  "placeMinimumStars": "4"
}
```

#### Output example (one row, shortened)

```json
{
  "title": "Swish Dental Downtown",
  "categoryName": "Dentist",
  "address": "201 W 5th St Ste 175, Austin, TX 78701",
  "city": "Austin",
  "postalCode": "78701",
  "phone": "(512) 713-1099",
  "website": "https://www.swishsmiles.com/location/downtown",
  "rating": 4.5,
  "reviewsCount": 987,
  "latitude": 30.2674035,
  "longitude": -97.7451433,
  "openingHoursText": "Monday: 9 AM-6 PM; Tuesday: 8 AM-5:30 PM; ...",
  "placeId": "ChIJdWgENLS1RIYReos4IrRuTmQ",
  "cid": "7227836171923524474",
  "url": "https://www.google.com/maps/place/?q=place_id:ChIJdWgENLS1RIYReos4IrRuTmQ",
  "emails": ["downtown@swishsmiles.com"],
  "facebook": "https://www.facebook.com/SwishDentalTX",
  "instagram": "https://www.instagram.com/swishdental",
  "leadScore": 100
}
```

### Lead generation with n8n, Make and the API

- **Local lead lists**: "roofer" in 20 cities, "Only without a website" for web-design outreach, or "Only these categories" to keep the list tight.
- **n8n / Make / Zapier**: use the Apify node or module "Run an Actor", pick this scraper, then send the dataset rows to Google Sheets, a CRM or an email tool.
- **API**: `POST https://api.apify.com/v2/acts/kantolabs~google-maps-scraper/run-sync-get-dataset-items` with the input JSON returns the rows in one call.
- **Schedules**: run weekly with "Only new places" to receive just the businesses that appeared since last time.

### What it does not do

- **No sign-in, ever.** It reads what Google Maps shows any visitor who is not signed in. Because of that, reviews are limited to the few Google shows signed-out visitors (up to 5 per place), and **popular times** are not available (Google no longer shows them to signed-out visitors).
- **Price level** is filled only when Google shows it in search results.
- Short `maps.app.goo.gl` links are not followed - open them once and paste the full URL.

### FAQ

#### Is it legal to scrape Google Maps?

This scraper collects **publicly visible business information** - the same details anyone sees on Google Maps without logging in - and reads only the businesses' own public websites for contact details, respecting robots.txt. It does not log in, solve captchas or bypass any access control. Business listings can still contain **personal data** (for example a sole trader's name, or reviewer names if you switch them on). If you process personal data, you are responsible for having a lawful basis under GDPR, CCPA and similar laws, and for following the rules on direct marketing in your country. If unsure, ask a lawyer.

#### Why did I get fewer places than I asked for?

Either the area simply has fewer matching places (a small town may have 4 hardware stores), your filters removed some, or Google returned places outside your location, which are dropped for free. The status message at the end of the run gives the exact counts.

#### What happens when Google blocks a request?

The request is retried on a different proxy IP. If it still fails, that search page or place becomes a free error row - you are never charged for it.

#### Can I search a whole country?

Yes, but a country is a very large area: set a sensible "Number of places per search" and consider searching city by city for full coverage.

#### Which languages are supported?

40 languages, including English, Spanish, German, French, Portuguese, Czech, Polish, Japanese, Korean, Chinese, Indonesian, Vietnamese, Thai and Filipino. Names, categories, addresses and hours come back as Google Maps shows them in that language.

### More from Kanto Labs

Browse our other data and media tools at [apify.com/kantolabs](https://apify.com/kantolabs).

### Support

Found a wrong field or a place that should have been found? Open an issue in the **Issues** tab with the run ID and the place link - we reply within a day.

# Actor input Schema

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

What you would type into Google Maps, one per line: "dentist", "coffee shop", "plumber". Each term is searched in the location below. Leave empty if you only use Maps URLs or place IDs.

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

City, region or country, e.g. "Austin, TX, USA", "Manila, Philippines", "Praha". Places outside this area are dropped (free). Big areas are split into a grid automatically to get past Google's ~240-results-per-search limit.

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

Maximum places to return for each search term (or Maps URL). The run stops exactly at this number. Above ~200 the area is searched cell by cell (grid).

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

Language of names, categories, addresses and opening hours, as Google Maps shows them in that language.

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

Two-letter country code Google should rank results for, e.g. "us", "ph", "cz". Usually not needed when a location is set.

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

Centre of a circle to search in. Use with Longitude and Radius instead of the Location field. Example: 30.2672

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

Centre of the circle. Example: -97.7431

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

Radius of the custom circle in kilometres.

## `startUrls` (type: `array`):

Search URLs (google.com/maps/search/...) or place URLs (google.com/maps/place/..., ?cid=...). Short maps.app.goo.gl links are not resolved - open them and copy the full URL.

## `placeIds` (type: `array`):

Google place IDs (ChIJ...), CID numbers or feature IDs (0x...:0x...). One place per line.

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

Keep a place only if one of its Google categories contains one of these words, e.g. "dentist", "orthodontist". Filtered-out places are free and do not count toward the limit.

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

Drop places rated below this (places without a rating are dropped too when set).

## `website` (type: `string`):

"Only without a website" is a classic lead list for web designers.

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

Drop places Google marks as permanently or temporarily closed.

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

Opens each place for: plus code, description, star distribution (1-5), review keywords, booking/order links, menu link. Charged as "Place details" per place where it loaded.

## `scrapeContacts` (type: `boolean`):

Reads the business's own website (home page + up to 2 contact/about pages, robots.txt respected) for emails, Facebook/Instagram/LinkedIn/X/YouTube/TikTok links and phone numbers. Charged as "Contact details" only when at least one was found.

## `maxReviews` (type: `integer`):

Up to 5 reviews per place, newest first, with stars, text, date and owner reply. Google shows signed-out visitors only these few reviews; this actor never signs in. Charged per review delivered.

## `reviewsIncludeAuthor` (type: `boolean`):

Reviewer names are personal data. Leave off unless you have a lawful reason to process them (GDPR).

## `splitLargeAreas` (type: `boolean`):

When a search hits Google's results limit and you asked for more, split the area into cells and search each (duplicates removed). Turn off to get only the first ~240.

## `skipPlacesFromDatasetId` (type: `string`):

Pick the dataset of an earlier run of this actor. Places already in it are skipped and not charged - run it weekly to get only newly listed businesses.

## Actor input object example

```json
{
  "searchStringsArray": [
    "dentist"
  ],
  "locationQuery": "Austin, TX, USA",
  "maxCrawledPlacesPerSearch": 20,
  "language": "en",
  "radiusKm": 5,
  "placeMinimumStars": "",
  "website": "allPlaces",
  "skipClosedPlaces": false,
  "scrapePlaceDetailPage": false,
  "scrapeContacts": false,
  "maxReviews": 0,
  "reviewsIncludeAuthor": false,
  "splitLargeAreas": true
}
```

# Actor output Schema

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

No description

## `csv` (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": [
        "dentist"
    ],
    "locationQuery": "Austin, TX, USA",
    "maxCrawledPlacesPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("kantolabs/google-maps-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 = {
    "searchStringsArray": ["dentist"],
    "locationQuery": "Austin, TX, USA",
    "maxCrawledPlacesPerSearch": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("kantolabs/google-maps-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 '{
  "searchStringsArray": [
    "dentist"
  ],
  "locationQuery": "Austin, TX, USA",
  "maxCrawledPlacesPerSearch": 20
}' |
apify call kantolabs/google-maps-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kantolabs/google-maps-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/nSF86DJpkiGOEZtag/builds/2ObcKxednB3PSlCLh/openapi.json
