# Google Maps Reviews Scraper with Owner Replies & AI Insights (`maxencebernerd/google-maps-reviews-ai-scraper`) Actor

Extract Google Maps reviews with owner replies, translations, photos and reviewer profiles from any place URL, Place ID or search. Optional AI summaries and per-review sentiment. Pay per result.

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

## Pricing

from $0.30 / 1,000 review scrapeds

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 Reviews Scraper extracts **every public review of any place on Google Maps** — star rating, full text, Google's translation, date, photos, reviewer profile and the **owner's reply** — from a place URL, a Place ID or a plain search such as `dentist in Lyon`. Optional **AI insights** add a per-place reputation summary (sentiment, top praises and complaints, 12-month trend) and a per-review classification, generated by Claude. Pay only for what you get: a few cents per thousand reviews, no subscription.

### What data does Google Maps Reviews Scraper extract?

One dataset row per review, with the place attached to every row so exports stay flat:

| Field | Description |
| --- | --- |
| `placeId`, `placeName`, `placeUrl` | Google Place ID, name and canonical Maps URL of the place |
| `placeRating`, `placeReviewCount` | Overall star rating and total number of reviews of the place |
| `reviewId`, `reviewUrl` | Stable review identifier and direct link to the review |
| `rating` | Stars given by the reviewer (1–5) |
| `text`, `textTranslated`, `originalLanguage` | Review text as written, Google's translation into your `language` (when the review is in another language), and the detected language |
| `publishedAt`, `publishedAtText` | Exact publication timestamp (ISO 8601) and Google's relative label ("3 months ago") |
| `likesCount` | Number of "helpful" likes |
| `reviewerName`, `reviewerUrl`, `reviewerReviewCount`, `isLocalGuide` | Public reviewer profile (URL, review count and Local Guide status only with `includeReviewerProfile`) |
| `ownerReplyText`, `ownerReplyTextTranslated`, `ownerReplyAt` | The business owner's public response, its translation and date |
| `imageUrls` | Photos attached to the review (full-size URLs) |
| `aiSentiment`, `aiMainTheme`, `aiMentions` | Optional AI classification (see below) |
| `scrapedAt` | When the row was collected |

When AI insights are enabled, one extra row per place with `rowType: "place-summary"` carries the reputation summary.

No e-mail addresses, phone numbers or any non-public data are collected.

### How to scrape Google Maps reviews

1. Open the Actor and paste one or more **place URLs** (any Google Maps link: full URL, `maps.app.goo.gl` short link, `?cid=` link or `place_id:` link). You can also give **Place IDs** or **search queries** (`italian restaurant` + location `Paris`).
2. Set **Max reviews per place** (default 100, `0` for all) and the **sort order**. To fetch only recent reviews, keep `newest` and set **Only reviews newer than** to a date or a period such as `30 days`.
3. Optionally enable **AI insights** (one summary per place) and/or **AI classification** (sentiment and themes on every review).
4. Click **Start**. Reviews appear in the Output tab while the run progresses; download them as JSON, CSV, Excel or XML, or read them through the API.

The Actor handles Google's cookie consent, pagination, retries and session rotation on its own. Residential proxies (Apify Proxy) are on by default because Google blocks datacenter IPs.

### Input examples

Scrape 200 most recent reviews of two places given by URL and Place ID:

```json
{
    "placeUrls": ["https://www.google.com/maps/place/Le+Comptoir+du+Relais/@48.8519,2.3386,17z"],
    "placeIds": ["ChIJh-zejGjr9EcRjofGropl2Tk"],
    "maxReviewsPerPlace": 200,
    "sortBy": "newest"
}
```

Monitor the reviews of the last 30 days for every dentist found for a search, with AI classification:

```json
{
    "searchQueries": ["dentist"],
    "location": "Lyon, France",
    "maxPlacesPerQuery": 20,
    "sortBy": "newest",
    "reviewsNewerThan": "30 days",
    "enableReviewClassification": true
}
```

Reputation audit of one business: all reviews, owner replies, reviewer profiles and an AI summary:

```json
{
    "placeIds": ["ChIJh-zejGjr9EcRjofGropl2Tk"],
    "maxReviewsPerPlace": 0,
    "includeReviewerProfile": true,
    "enableAiInsights": true
}
```

### Output example

```json
{
    "rowType": "review",
    "placeId": "ChIJh-zejGjr9EcRjofGropl2Tk",
    "placeName": "Centre Dentaire Lyon République",
    "placeUrl": "https://www.google.com/maps/place/?q=place_id:ChIJh-zejGjr9EcRjofGropl2Tk",
    "placeRating": 4.6,
    "placeReviewCount": 1157,
    "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2toNmMwSkdjV2xpZEVwQlNYZHpSV3RDVHpBeWVFRRAB",
    "reviewUrl": "https://www.google.com/maps/reviews/data=!4m8!14m7!1m6!2m5!1sCi9DQUlR...",
    "rating": 5,
    "text": "Tout le personnel est top. Nous sommes toujours bien reçus, à l'écoute.",
    "textTranslated": "All the staff are great. We are always well received and listened to.",
    "originalLanguage": "fr",
    "publishedAt": "2026-07-09T16:05:04.958Z",
    "publishedAtText": "2 months ago",
    "likesCount": 0,
    "reviewerName": "Soso",
    "reviewerUrl": "https://www.google.com/maps/contrib/113693588107109307242",
    "reviewerReviewCount": 7,
    "isLocalGuide": false,
    "ownerReplyText": "Bonjour, nous vous remercions pour votre superbe retour 5 étoiles...",
    "ownerReplyTextTranslated": "Hello, thank you for your wonderful 5-star review...",
    "ownerReplyAt": "2026-07-10T16:10:07.000Z",
    "imageUrls": [],
    "aiSentiment": "positive",
    "aiMainTheme": "staff professionalism",
    "aiMentions": { "price": false, "service": true, "cleanliness": false, "waitingTime": false, "staff": true },
    "scrapedAt": "2026-09-29T12:11:29.000Z"
}
```

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

Pricing is pay-per-event: you are charged only for results actually stored in your dataset. Platform usage (compute, proxies) is included.

| Event | Price | When it is charged |
| --- | --- | --- |
| Actor start | $0.00005 | once per run |
| `place-scraped` | $0.01 | per place whose reviews were stored |
| `review-scraped` | $0.0003 ($0.30 per 1,000) | per review row |
| `place-summary` | $0.03 | per AI summary produced (only with `enableAiInsights`) |
| `review-classified` | $0.001 | per review classified (only with `enableReviewClassification`) |

Examples (100 reviews per place):

| Reviews | Places | Scraping only | With AI classification | With AI summaries |
| --- | --- | --- | --- | --- |
| 1,000 | 10 | $0.40 | $1.40 | $0.70 |
| 10,000 | 100 | $4.00 | $14.00 | $7.00 |
| 100,000 | 1,000 | $40.00 | $140.00 | $70.00 |

Failed places, empty places and failed AI calls are never charged. Set **Maximum cost per run** in the run options to cap spending: the Actor stops cleanly when the cap is reached and says so in the log.

### AI insights

Enable **Generate AI summary per place** to get one `place-summary` row per place:

```json
{
    "rowType": "place-summary",
    "placeName": "Le comptoir du Relais",
    "reviewsAnalyzed": 80,
    "overallSentiment": "mixed",
    "sentimentScore": 0.32,
    "positiveThemes": [
        { "theme": "Classic French dishes (tartare, onion soup)", "mentions": 21, "quote": "Best steak tartare I've had in Paris" }
    ],
    "negativeThemes": [
        { "theme": "Inconsistent, sometimes dismissive service", "mentions": 14, "quote": "The waiter ignored us for 20 minutes" }
    ],
    "representativeQuotes": ["..."],
    "trend12Months": [{ "month": "2026-09", "averageRating": 4.3, "reviewCount": 12 }],
    "trendSummary": "Ratings dipped to 3.4★ in January 2026 amid complaints about the new ownership, then recovered to 4.2–4.3★ from March onwards.",
    "summary": "Food quality remains the main strength... Management should prioritize consistent service...",
    "model": "claude-haiku-4-5"
}
```

Enable **Classify each review with AI** to add three fields to every review: `aiSentiment` (`positive`, `neutral`, `negative`), `aiMainTheme` (a short label such as `waiting time` or `food quality`) and `aiMentions` (whether the review talks about price, service, cleanliness, waiting time or staff). Reviews are processed in batches, in their original language.

Summaries are computed from up to 80 reviews per place (most recent first, negatives always represented). The 12-month trend is computed from the scraped dates and ratings, not by the model.

### Use cases

- **Reputation audit** — pull every review and owner reply of a business, spot unanswered negative reviews, measure response time.
- **Competitive benchmark** — scrape the reviews of every competitor in a search area and compare themes, ratings and trends with the AI summary.
- **Multi-location monitoring** — schedule a weekly run with `reviewsNewerThan: "7 days"` over all your locations and push new reviews to Slack, Google Sheets or your CRM.
- **Data for AI agents and LLM apps** — clean JSON with stable IDs, translations and sentiment, ready for RAG pipelines or agents through the Apify MCP server.
- **Market research** — analyze what customers say about a category of businesses in a city.

### Using the Actor from code

Run it and read the results with the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/maxencebernerd~google-maps-reviews-ai-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"placeIds": ["ChIJh-zejGjr9EcRjofGropl2Tk"], "maxReviewsPerPlace": 50}'
```

JavaScript (`npm install apify-client`):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('maxencebernerd/google-maps-reviews-ai-scraper').call({
    searchQueries: ['bakery'],
    location: 'Bordeaux',
    maxPlacesPerQuery: 5,
    maxReviewsPerPlace: 100,
    enableAiInsights: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((row) => row.rowType === 'place-summary'));
```

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("maxencebernerd/google-maps-reviews-ai-scraper").call(run_input={
    "placeUrls": ["https://maps.app.goo.gl/example"],
    "maxReviewsPerPlace": 200,
    "sortBy": "lowest",
    "enableReviewClassification": True,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["rating"], row["aiSentiment"], row["text"][:80])
```

#### MCP: use it from Claude, Cursor or any AI agent

The Actor is exposed through the [Apify MCP server](https://mcp.apify.com). Add it to your MCP client (example for Claude Desktop):

```json
{
    "mcpServers": {
        "apify": {
            "url": "https://mcp.apify.com?tools=maxencebernerd/google-maps-reviews-ai-scraper",
            "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
        }
    }
}
```

Then ask your agent: *"Get the 50 latest reviews of Le Comptoir du Relais in Paris and summarize the complaints."* The agent calls the Actor, waits for the dataset and reads the rows.

### Integrations

Every run can trigger Zapier, Make, n8n, Slack, Google Sheets, Google Drive, Airbyte, webhooks or any other [Apify integration](https://apify.com/integrations). The dataset is also readable through the [LangChain](https://python.langchain.com/docs/integrations/document_loaders/apify_dataset) and LlamaIndex Apify loaders for RAG use cases. Schedule the Actor from the **Schedules** tab for continuous review monitoring.

### FAQ

**Is it legal to scrape Google Maps reviews?**
The Actor only collects data that is publicly visible to anyone without logging in. Reviews are public statements and reviewer names are public profiles; no contact details are collected. You remain responsible for how you use the data, notably under GDPR when storing personal data such as reviewer names — the `includeReviewerProfile` option is off by default for that reason. Read Apify's [legal guide to web scraping](https://blog.apify.com/is-web-scraping-legal/).

**How fresh is the data?**
Every run fetches live data from Google Maps at the time it runs. Use `sortBy: "newest"` with `reviewsNewerThan` and a schedule to keep a dataset up to date at minimal cost.

**Can I get all reviews of a place with tens of thousands of reviews?**
Yes, set `maxReviewsPerPlace` to `0`. Google serves reviews in pages of 10, so plan roughly one minute per 500 reviews. For very large places, scraping in several runs by rating (`sortBy: "lowest"` / `"highest"`) is often more useful than one huge run.

**Why are some reviews missing?**
Google hides reviews it flags as spam and does not serve reviews without text on some sort orders. The count reported in `placeReviewCount` is Google's own figure, which includes reviews that are not displayed.

**Does it work in my language?**
Yes. `language` controls the interface and translation language (`textTranslated` is filled whenever the review language differs from it); reviews are collected in every language.

**What if Google blocks the run?**
The Actor detects Google's degraded pages, rotates proxy sessions and retries each place up to three times. Residential proxies in a European country give the most stable sessions; a failed place is logged, listed in the `FAILED-SOURCES` record of the key-value store and never charged.

**Can I request a feature or report a problem?**
Use the **Issues** tab of the Actor. Custom extractions and integrations are available on request.

# Actor input Schema

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

Links to Google Maps places. Accepts full place URLs (`https://www.google.com/maps/place/...`), short links (`https://maps.app.goo.gl/...`, `https://goo.gl/maps/...`), `?cid=` links and `place_id:` links. Search-result URLs (`/maps/search/...`) are also accepted and expanded into places.

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

Google Place IDs (e.g. `ChIJLU7jZClu5kcR4PcOOm6E87A`). You can get them from the Google Places API, the Places ID Finder, or the output of other Google Maps scrapers.

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

Free-text searches such as `dentist in Lyon` or `italian restaurant near Times Square`. Each query is run on Google Maps and the first `maxPlacesPerQuery` places are scraped. Combine with `location` to anchor the search.

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

Optional city, region or address appended to every search query (e.g. `Paris, France`). Ignored when no search queries are provided.

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

How many places to scrape for each search query.

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

Upper limit of reviews scraped for each place. Set `0` for all reviews. Cost scales with the number of reviews returned.

## `sortBy` (type: `string`):

Order in which Google returns reviews. Use `newest` together with `reviewsNewerThan` to fetch only recent reviews.

## `reviewsNewerThan` (type: `string`):

Skip reviews published before this date. Accepts an absolute date (`2025-01-31`) or a relative period (`30 days`, `6 months`, `1 year`). Works best with `sortBy: newest`, where scraping stops as soon as older reviews are reached.

## `minRating` (type: `integer`):

Only keep reviews with at least this many stars (1-5). Leave empty to keep all ratings.

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

Language code for the Google Maps interface (`hl` parameter). Reviews written in other languages also include Google's translation into this language in `textTranslated`.

## `includeOwnerReplies` (type: `boolean`):

Add the business owner's public response (`ownerReplyText`, `ownerReplyAt`) to each review.

## `includeReviewerProfile` (type: `boolean`):

Add public reviewer profile data: profile URL, total review count and Local Guide status. Reviewer names are always included. No e-mails or phone numbers are ever collected.

## `enableAiInsights` (type: `boolean`):

For each place, produce an AI summary: overall sentiment, top 5 positive and negative themes, representative quotes and a 12-month trend. Output as one extra dataset row with `rowType: "place-summary"`. Charged once per place (event `place-summary`).

## `enableReviewClassification` (type: `boolean`):

Adds `aiSentiment`, `aiMainTheme` and `aiMentions` (price, service, cleanliness, waiting time, staff) to every review. Charged per review (event `review-classified`).

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

Google Maps blocks datacenter IPs quickly: residential Apify Proxy is the default. Pinning the proxy to a European country (default: France) gives the most stable sessions, because Google serves EU visitors a consent page that establishes a regular browsing session. You can pick any country.

## `debugDumpRaw` (type: `boolean`):

Support option. Saves the raw review payloads into the key-value store (RAW-\* records).

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

Number of places processed in parallel (one browser tab each). Higher values are faster but use more memory and proxy sessions.

## Actor input object example

```json
{
  "placeUrls": [
    "https://www.google.com/maps/place/?q=place_id:ChIJlz74sd5x5kcRJKnMHqMw2x8"
  ],
  "maxPlacesPerQuery": 10,
  "maxReviewsPerPlace": 100,
  "sortBy": "newest",
  "language": "en",
  "includeOwnerReplies": true,
  "includeReviewerProfile": false,
  "enableAiInsights": false,
  "enableReviewClassification": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  },
  "debugDumpRaw": false,
  "maxConcurrency": 3
}
```

# Actor output Schema

## `results` (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 = {
    "placeUrls": [
        "https://www.google.com/maps/place/?q=place_id:ChIJlz74sd5x5kcRJKnMHqMw2x8"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "FR"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("maxencebernerd/google-maps-reviews-ai-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 = {
    "placeUrls": ["https://www.google.com/maps/place/?q=place_id:ChIJlz74sd5x5kcRJKnMHqMw2x8"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "FR",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("maxencebernerd/google-maps-reviews-ai-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 '{
  "placeUrls": [
    "https://www.google.com/maps/place/?q=place_id:ChIJlz74sd5x5kcRJKnMHqMw2x8"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}' |
apify call maxencebernerd/google-maps-reviews-ai-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maxencebernerd/google-maps-reviews-ai-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/1msgluL5wo2DvhIwh/builds/l5IZNHbuzJ4eelvKU/openapi.json
