# Google Maps API - Places, Reviews, Emails & Leads (`pnda/google-maps-scraper`) Actor

No Google API key needed. Google Maps API and Places API alternative: search places by keyword and location or by URL / place_id. Phone, website, rating, hours, GPS, reviews and website emails as JSON or CSV. Pay per place.

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

## Pricing

from $2.80 / 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.

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 API - Places, Reviews, Emails & Leads

**A Google Maps API and Google Places API alternative that needs no API key.** Search businesses on Google Maps by keyword and location, or look up specific places by URL or `place_id`, and get clean JSON, CSV or Excel: name, address, phone, website, rating, reviews count, categories, opening hours, GPS coordinates, `place_id`, CID and Google Maps link. Add the latest reviews and the public emails found on each business website to turn Google Maps into a local business leads list.

**No Google Cloud account, no API key, no proxy, no browser to set up.** Type a search, pick a location, get a spreadsheet of leads. Pay only for the places delivered.

### Why use this Google Maps API instead of the official Places API?

- **No API key and no Google Cloud billing account**: you call one Apify endpoint, Apify handles the rest.
- **All fields in one call**: the official Places API splits contact, atmosphere and reviews into separately billed SKUs. Here a place comes back with phone, website, hours, rating and coordinates at a flat price.
- **More than 5 reviews**: the official Places API returns at most 5 reviews per place. This Actor returns up to 1,000, sorted the way you want.
- **Emails**: the official API has no email field. This Actor visits the business website and returns the public emails it finds (local business leads, ready for outreach).
- **Bulk**: many search terms and many place IDs in a single run, deduplicated.

### What you get

- **Every useful field of a Google Maps listing**: name, full address, phone, website, rating, reviews count, main category and all categories, price level, open/closed state, opening hours per day, latitude/longitude, `place_id`, CID and a Google Maps link.
- **Search by keyword and location**: "dentist" in "Austin, TX", "coffee shop" in "Paris, France"... up to about 120 places per search, the maximum Google Maps shows.
- **Look up specific places**: paste Google Maps place URLs, `place_id`s (ChIJ...) or CIDs.
- **Reviews (optional)**: text, rating, date, author, local guide flag, likes, owner response and photos, sorted by newest, most relevant, highest or lowest rating.
- **Emails (optional)**: the Actor visits each business website (homepage and contact page) and returns the public email addresses it finds.
- **Deduplicated**: a place found by two searches is delivered and billed once.
- **Budget-safe**: the Actor never fetches more than your *Maximum cost per run* can pay for.

### Input

| Field | Description | Default |
|---|---|---|
| `searchQueries` | What you would type in Google Maps, one per line. | - |
| `location` | City, region or country added to every search term. | - |
| `maxResultsPerSearch` | Places per search term (5 to 120). | `20` |
| `placeUrls` | Google Maps place URLs, `place_id`s or CIDs to look up directly. | - |
| `maxReviewsPerPlace` | Reviews added to each place (0 = none). | `0` |
| `reviewsSort` | `newestFirst`, `qualityScore`, `ratingHigh` or `ratingLow`. | `newestFirst` |
| `scrapeEmails` | Find emails on each place's website. | `false` |
| `coordinates` | Optional map center: `"latitude,longitude[,zoom]"`. | - |
| `language` | Language of the results (`en`, `fr`, `de`...). | `en` |
| `country` | Google country code (`us`, `gb`, `fr`...). | `us` |

```json
{
  "searchQueries": ["dentist", "orthodontist"],
  "location": "Austin, TX",
  "maxResultsPerSearch": 50,
  "maxReviewsPerPlace": 10,
  "scrapeEmails": true
}
```

### Output

One row per unique place:

```json
{
  "type": "place",
  "searchQuery": "coffee shop in Paris, France",
  "name": "Café Example",
  "address": "12 Rue de Rivoli, 75004 Paris, France",
  "phone": "+33 1 23 45 67 89",
  "website": "https://cafe-example.fr/",
  "rating": 4.6,
  "reviewsCount": 1234,
  "category": "Coffee shop",
  "categories": ["Coffee shop", "Cafe"],
  "priceLevel": "€€",
  "openState": "Open ⋅ Closes 7 PM",
  "openingHours": { "monday": "8 AM–7 PM", "tuesday": "8 AM–7 PM" },
  "latitude": 48.8559,
  "longitude": 2.3589,
  "placeId": "ChIJ...",
  "dataId": "0x47e66e1f...:0x40b82c36...",
  "cid": "290832459437401184",
  "googleMapsUrl": "https://www.google.com/maps/search/?api=1&query=Caf%C3%A9%20Example&query_place_id=ChIJ...",
  "emails": ["contact@cafe-example.fr"],
  "reviews": [
    { "rating": 5, "text": "Best flat white in the Marais.", "publishedAt": "2026-09-01T10:12:00Z", "authorName": "Ann", "isLocalGuide": true, "ownerResponse": "Thank you Ann!" }
  ],
  "scrapedAt": "2026-09-29T12:00:00.000Z"
}
```

`emails` and `reviews` appear only when the options are on. A place given by URL that no longer exists returns `{"status": "not-found"}` and is not billed. A `SUMMARY` record in the key-value store gives the run totals.

### Pricing

Pay per event, only for what is delivered:

| Event | Price |
|---|---|
| Place from a search | $2.80 per 1,000 places |
| Place looked up by URL / place ID | $0.04 per place |
| Review | $2.50 per 1,000 reviews |
| Place with at least one email found (option) | $1.90 per 1,000 places |
| Run start | $0.01 per run |

Duplicates, places without email and places not found are free. Example: 1,000 dentists with emails found on 400 of them costs $0.01 + $2.80 + $0.76 = $3.57.

This Actor is available on **paid Apify plans only**. On a free plan, the run returns a single item that explains this, and nothing is billed.

### Use it through the API

```bash
curl -X POST "https://api.apify.com/v2/acts/pnda~google-maps-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["plumber"], "location": "Chicago, IL", "maxResultsPerSearch": 40}'
```

It also works from Make, Zapier, n8n and the Apify Python and JavaScript clients.

### FAQ

**Do I need a Google Maps API key or a Google Places API key?** No. You only need an Apify account on a paid plan. There is nothing to set up on Google Cloud.

**Can I get Google Maps reviews by place ID?** Yes. Put the `place_id` (or the Google Maps URL) in `placeUrls` and set `maxReviewsPerPlace`.

**Why about 120 places at most per search?** Google Maps itself stops there. To cover a whole city, split it: one search per neighborhood or per category.

**How do I stay under a budget?** Set *Maximum cost per run* in the run options. The Actor stops before it goes over.

**Are the emails verified?** They are the addresses published on the business website. To check that the mailbox exists, run them through [Email Verifier](https://apify.com/pnda/email-verifier).

**Is it legal?** The Actor only collects publicly available business information. Personal data in reviews (author names) may be protected by GDPR and similar laws: process it only with a legitimate reason.

# Actor input Schema

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

What you would type in Google Maps, one per line (e.g. "dentist", "coffee shop", "plumber"). Each term is combined with the location below. Duplicates are removed.

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

City, region or country to search in (e.g. "Paris, France", "Austin, TX"). Leave empty if your search terms already contain the place.

## `maxResultsPerSearch` (type: `integer`):

Google Maps shows up to about 120 places per search. You are billed per place delivered.

## `placeUrls` (type: `array`):

Get specific places directly: a Google Maps place URL, a place_id (ChIJ...) or a CID. Billed as "place details" (one lookup per place).

## `maxReviewsPerPlace` (type: `integer`):

0 = no reviews. Otherwise the latest reviews (text, rating, date, author, owner response) are added to each place. Billed per review.

## `reviewsSort` (type: `string`):

Which reviews come first.

## `scrapeEmails` (type: `boolean`):

Visit each place's website (homepage and contact page) and return the public email addresses found. Billed only when at least one email is found.

## `coordinates` (type: `string`):

Center the search on exact GPS coordinates: "latitude,longitude\[,zoom]", e.g. "48.8566,2.3522,14".

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

Language of the results (Google hl code: en, fr, de, es, it, pt-BR...).

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

Two-letter country code of the Google version to use (us, gb, fr, de...).

## Actor input object example

```json
{
  "searchQueries": [
    "coffee shop"
  ],
  "location": "Paris, France",
  "maxResultsPerSearch": 10,
  "maxReviewsPerPlace": 0,
  "reviewsSort": "newestFirst",
  "scrapeEmails": false,
  "language": "en",
  "country": "us"
}
```

# Actor output Schema

## `dataset` (type: `string`):

All places (JSON, CSV, Excel).

# 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"
    ],
    "location": "Paris, France",
    "maxResultsPerSearch": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("pnda/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 = {
    "searchQueries": ["coffee shop"],
    "location": "Paris, France",
    "maxResultsPerSearch": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("pnda/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 '{
  "searchQueries": [
    "coffee shop"
  ],
  "location": "Paris, France",
  "maxResultsPerSearch": 10
}' |
apify call pnda/google-maps-scraper --silent --output-dataset

```

## MCP server setup

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