# Google Maps Reviews Scraper + Reputation Intelligence (`ryanclinton/google-maps-reviews-scraper`) Actor

Scrapes Google Maps reviews from a business name, postcode, or place URL. Returns the complete review set with a coverage audit (claimed vs collected), sentiment, complaint themes, rating trajectory, response health, and a ranked reputation attention queue.

- **URL**: https://apify.com/ryanclinton/google-maps-reviews-scraper.md
- **Developed by:** [Ryan Clinton](https://apify.com/ryanclinton) (community)
- **Categories:** E-commerce, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

$0.60 / 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Google Maps Reviews Scraper - Coverage Audit + Reputation Intelligence

![Google Maps Reviews Scraper — every Google review with a receipt](https://apifyforge.com/readme-assets/ryanclinton-google-maps-reviews-scraper/hero.png)

### In one sentence

Google Maps Reviews Scraper is a Google reviews intelligence actor that resolves a business name, postcode, or place URL to a Google place, collects the complete review set with a coverage audit, and returns sentiment, complaint themes, rating trajectory, response health, and a ranked attention queue with the exact reviews behind every signal.

> **The promise:** we tell you exactly what Google said existed, exactly what we collected, what changed, why it matters, and which reviews prove it. That is the difference between a review extractor and a reputation operating system.

**Category:** Google Maps reviews scraper. Reputation monitoring tool. Review analysis API.
**Primary use case:** Pull a complete, audited review set from a business name and get a decision-ready reputation read in one run. Can also be used as a drop-in raw-review extractor for ingestion pipelines.

**Also known as:** Google reviews scraper, Google Maps review extractor, reputation monitoring actor, review sentiment analyzer, business review intelligence, Google place review API

### What this actor does

- **What it is:** A Google Maps reviews scraper that doubles as a reputation operations layer.
- **What it checks/extracts/processes:** Every Google review for a place, plus a coverage audit (claimed vs collected), sentiment, complaint themes, rating trajectory, response health, and a 5-state reputation read.
- **What it returns:** Per-place intelligence records and raw review rows, deduplicated by `reviewId`, in JSON/CSV/Excel.
- **What it does NOT do:** It does not post, reply to, flag, or delete reviews on Google. It does not predict revenue or future ratings, score controversy, or judge whether a review is true.
- **Who it's for:** Multi-location and franchise ops, reputation and review-management agencies, local-SEO consultants, market researchers, CX teams, and acquirers screening a target's locations.

Google Maps Reviews Scraper is an Apify actor that turns a typed business name into a complete, audited Google review set with a reputation read attached. You do not need a place URL to start: paste a business name, a postcode, a `share.google` link, a place ID, a feature ID, or a CID, and the actor classifies the input and resolves it to a Google place before scraping. Every place comes back with an explicit coverage block: how many reviews Google claimed it had, how many were collected, and why any gap exists.

Google Maps Reviews Scraper functions as a Google reviews API that returns finished reputation signals rather than only raw rows. It deduplicates reviews by Google's stable `reviewId`, emits them in a deterministic order, and stacks an interpretation layer on top: sentiment, an Issue Registry that groups complaints into root issues, rating trajectory, a reputation state machine, location ranking, and a ranked attention queue. Same input shape as `compass/Google-Maps-Reviews-Scraper`, plus you can just type a business name.

To scrape Google Maps reviews, provide a business name or URL to Google Maps Reviews Scraper and run it. You get back the complete review set, a coverage receipt showing claimed versus collected, and a per-place read of what changed, whether it is getting worse, and the exact reviews driving each signal.

**In short:** Paste a business name and get a complete, audited Google review set plus a reputation read, with the receipts behind every claim.

> **What it is:** A Google Maps reviews scraper with a coverage audit and a deterministic reputation-intelligence layer.
> **Who it's for:** Franchise ops, reputation agencies, local-SEO consultants, researchers, CX teams, and acquirers.
> **When to use it:** When you need complete, trustable Google reviews from a business name and a decision about them, not just rows.

**What it does** — Resolves a business name or URL to a Google place, scrapes the complete review set, and returns a reputation read.
**Best for** — Multi-location reputation monitoring, complete review extraction, complaint-theme mining, competitor benchmarking.
**Output** — `place_intelligence`, `review`, `run_summary`, and `error` records in JSON, CSV, or Excel.
**Input** — A business name, postcode, place URL, place ID, feature ID, CID, or a bulk list.

**Key limitation:** The actor reports exactly what Google's own place header claims and exactly what it collected. It never claims it got "every review that ever existed."
**What it is not:** Not a review-posting tool, not a revenue forecaster, and not a replacement for legal or moderation review of content.
**Does not include:** Owner-side actions on Google, controversy or brand-safety scoring, star-rating forecasting, or truth-judgement of reviews.
**Results may be incomplete when:** Google stops serving pages mid-corpus (reported as `partial`), or your `maxReviewsPerPlace` cap is hit before exhaustion (reported as `capped`).

### What you get from one call

**Input:** `"Domino's Pizza Belfast BT9 6AA"`
**Returns:**
- An **Executive Brief** per place: state, rank movement, top issue + its share of negatives, unanswered negatives, and coverage in one object — the one-glance read for an agency owner, franchise director, or acquirer.
- A coverage audit: claimed vs collected, coverage ratio, and a stop condition (for example, "312 claimed, 312 collected, 0 duplicates, complete").
- A reputation read: rating trajectory, reputation state, complaint themes, and a ranked attention queue.
- The Issue Registry: root complaint issues with concentration and recency-cohort emergence (new problems vs background constants).
- Every raw review, deduplicated by `reviewId`, with a per-review signal block.
- The exact `reviewId`s behind each signal (the evidence trail).

A typical first run reads back like this: 312 claimed, 312 collected, 0 duplicates, coverage complete. Rating down 0.7 in the recent window. Nine unanswered recent negatives. Main rising complaint: slow service. Here are the reviews driving that signal.

![The intelligence stack: resolve, paginate, coverage audit, synthesize, reputation state, attention queue, executive brief](https://apifyforge.com/readme-assets/ryanclinton-google-maps-reviews-scraper/intelligence-layers.png)

### What makes this different

The whole product is built around one promise: **we tell you exactly what Google said existed, exactly what we collected, what changed, why it matters, and which reviews prove it.** That is a different proposition from "we scrape Google reviews."

- **A count with a receipt** — Every place shows `reviewsClaimed` vs `reviewsScraped`, the stop condition, and duplicates dropped. Reviews are keyed by Google's stable `reviewId` and emitted in a deterministic order, so running the same place twice produces an identical set.
- **No place URL required** — Type a business name, a postcode, or drop a lat-lng, and `mode: auto` classifies the input and resolves it. One actor, not two.
- **Reputation operations, not row dumps** — A reputation state machine per location, an Issue Registry that detects new problems, location ranking, in-run local percentiles, derived acquisition-risk and manager-neglect grades, and a per-place timeline of how it got here.

If you were building this yourself, you would need a place resolver across six input shapes, a paginator with a coverage ledger, deterministic dedup, a TF-IDF theme engine, an issue-grouping dictionary, a cross-run state store, and a ranking model.

### Quick answers

**What is it?** Google Maps Reviews Scraper is a Google reviews intelligence actor that collects a complete, audited review set from a business name or URL and returns a reputation read with the evidence behind it.

**What makes it different?** It ships a coverage audit (claimed vs collected, with the stop condition) and a deterministic reputation layer, not only raw review rows. To scrape Google Maps reviews from a business name, you do not need to find a place URL first.

**What data sources does it use?** Public Google Maps place pages and Google's own reviews endpoint. `reviewsClaimed` comes from the place header; all review data comes from Google.

**What does it return?** Per-place intelligence records and raw review rows in JSON, CSV, or Excel, with version constants on every record so automations can pin to a stable schema.

**Does it monitor over time?** Yes. Set a `watchlistName` for managed cross-run deltas, a reputation timeline, and rank movement, or diff against a prior dataset with `comparisonDatasetId`.

**Is it deterministic?** Yes. Reviews are deduplicated by `reviewId` and emitted in a fixed order. The signal layer, state machine, and scores are computed deterministically; an optional LLM is used only for naming themes, never in the decision path.

### At a glance

**Quick facts:**
- **Input:** A business name, postcode, place URL, `share.google` link, place ID, feature ID, CID, or a bulk list (`places` / `placeUrls`).
- **Output:** `place_intelligence`, `review`, `run_summary`, and `error` records (JSON, CSV, Excel).
- **Output profiles:** `signals` (default, full intelligence), `compat` (compass field set, raw), `minimal` (id, text, stars, date).
- **Default max reviews per place:** 200 (configurable, capped honestly when hit).
- **Dedup key:** Google's stable `reviewId`; deterministic ordering across runs.
- **Monitoring:** `watchlistName` for managed deltas, or `comparisonDatasetId` for one-off diffs.
- **Memory:** 256 MB default (up to 2048 MB). **Timeout:** 7200s.

**Input -> Output:**
- Input: A business name, URL, or bulk list.
- Process: Resolve to a place, paginate reviews with a coverage ledger, synthesize signals, compute cross-run state.
- Output: Complete reviews + a per-place reputation read with an evidence trail.

**Problems this solves:**
- How to get Google reviews from a business name without hunting for a place URL.
- How to know whether a review scrape is complete and why any gap exists.
- How to find the recurring complaint driving a location's rating down.
- How to rank locations by reputation risk and see which ones fell behind.

**Best fit:** Complete review extraction from a business name, multi-location reputation monitoring, complaint-theme mining, competitor benchmarking within a local cohort, drop-in migration from a place-URL workflow.
**Not ideal for:** Forecasting future ratings, cross-platform review joins (TripAdvisor/Yelp), or auto-discovering competitors the run did not include.
**Does not include:** Owner-side actions on Google, controversy scoring, revenue prediction, or truth-judgement of reviews.

**Common questions this actor answers:**
- **Did I get every review?** The coverage block reports claimed vs collected and the stop condition.
- **Is this location getting worse?** `ratingTrajectory` and `reputationState` answer it.
- **What is driving the complaints?** The Issue Registry groups them into root issues with concentration and emergence.
- **Which of my locations need attention first?** The Attention Queue and location ranking order them by reputation risk.
- **Is this an alternative to a place-URL-only reviews scraper?** Yes; it accepts the same place URLs and adds business-name input plus a coverage audit.

### What is a Google Maps reviews scraper?

A Google Maps reviews scraper extracts the review text, ratings, dates, and reviewer details for a place on Google Maps. Most tools key off a place URL and hand back rows. Google Maps Reviews Scraper resolves a business name or URL to a place itself, audits the collection against Google's own claimed count, and returns a reputation read on top, so the output is decision-ready rather than a spreadsheet you still have to interpret.

### What data can you extract?

| Data point | Source | Availability | Example |
|------------|--------|--------------|---------|
| **Review text** | Google review | Per review | "Waited 50 minutes for delivery, food was cold." |
| **Star rating** | Google review | Per review | 2 |
| **Published date** | Google review | Per review | `2026-05-13` (with `datePrecision` and `extractionMethod`) |
| **Owner response** | Google review | When present | `responseFromOwnerText`, `responseFromOwnerDate` |
| **Reviewer name** | Google review | When metadata on | "Sarah Chen" |
| **Local Guide flag** | Google review | When metadata on | `true` |
| **Coverage audit** | Place header + scrape ledger | Per place | claimed 312, scraped 312, status `complete` |
| **Sentiment split** | Derived | Per place | positive 0.58, negative 0.28, neutral 0.14 |
| **Root issues** | Derived | Per place | `delivery_delay`, 42% of negatives, emerging |
| **Rating trajectory** | Derived | Per place | recent 3.6 vs baseline 4.3, delta -0.7 |
| **Reputation state** | Derived | Per place | `critical` (was `attention`) |
| **Response health** | Derived | Per place | 9 unanswered recent negatives |

### Why use Google Maps Reviews Scraper?

**Before:** Find each location's place URL, run a scraper, paste rows into a sheet, pivot by rating, read the negatives by hand, then paste the text into a separate NLP tool. Hours per batch, and no way to know whether the scrape was complete.
**After:** Type a business name or drop a list, and get a complete, audited review set with the reputation read already attached. One run.

Two pains drove the design, both visible in the real-world complaints about place-URL-only scrapers: "I can't trust the count" (silent undercounts with no explanation) and "I have a business, not a place URL." Google Maps Reviews Scraper answers the first with an explicit coverage block and a `coverageProof` ledger, and the second with universal input resolution.

**Key difference:** This actor reports exactly what Google's header claimed and exactly what it collected, then tells you what to do about it, where a row vendor hands you rows and stops.

| Feature | Google Maps Reviews Scraper | Place-URL-only reviews scrapers |
|---------|------------------------------|---------------------------------|
| Input | Business name, postcode, lat-lng, URL, place ID, CID | Place URL / place ID |
| Coverage audit (claimed vs collected) | Yes, on every place | Not a core feature |
| Deterministic cross-run dedup | Yes (`reviewId`-keyed, fixed order) | Varies by tool |
| Sentiment + complaint themes | Built in | Typically a separate tool |
| Root-issue grouping + emergence | Yes (Issue Registry) | Not a core feature |
| Rating trajectory + reputation state | Yes | Not a core feature |
| Cross-run monitoring + deltas | Yes (`watchlistName`) | Not a core feature |
| Drop-in compat field set | Yes (`outputProfile: compat`) | Native |
| Best for | Complete, audited reviews + a decision | Raw row extraction at scale |

*Features based on publicly available information as of May 2026 and may change.*

Unlike a place-URL-only reviews scraper, which is built to hand back rows, Google Maps Reviews Scraper returns a coverage receipt and a reputation read with every place.

### Platform capabilities

- **Scheduling** — Run on a schedule with a `watchlistName` for a daily reputation feed.
- **API access** — Trigger from Python, JavaScript, or any HTTP client.
- **Proxy rotation** — Defaults to Apify residential proxies for Google Maps.
- **Monitoring** — A failure webhook is registered on every cloud run for FAILED, TIMED_OUT, and ABORTED.
- **Integrations** — Zapier, Make, Google Sheets, webhooks, and the Apify API.

### Where Google Maps Reviews Scraper fits in a pipeline

Upstream, feed it a business list (from a Google Maps place search) or place URLs you already have. Downstream, branch automations on `reputationState.current` or `maxEscalationLevel`, push the `compat` profile into an ingestion pipeline, or drop the `clientSummary` block into a monthly report. With `comparisonDatasetId`, it diffs against any prior Apify dataset, so the first run already shows what changed.

![Coverage audit, no place URL needed, reputation operations, and an evidence trail](https://apifyforge.com/readme-assets/ryanclinton-google-maps-reviews-scraper/feature-callouts.png)

### Features

Google Maps Reviews Scraper stacks two layers: a completeness layer that guarantees a trustable, deterministic review set, and an interpretation layer that turns those reviews into a reputation read. Every record carries ten pinned version constants so automations can lock to a stable schema.

**Collection and trust**
- **Universal input resolution** — Resolves business names, postcodes, lat-lng, place URLs, `share.google` links, place IDs (`ChIJ...`), feature IDs (`0x..:0x..`), and CIDs to a place. `mode: auto` classifies the input by shape.
- **Coverage audit** — Every place carries `reviewsClaimed`, `reviewsScraped`, `coverageRatio`, and a `coverageStatus` of `complete`, `partial`, `capped`, or `degraded`, with a `gapReason` when not complete.
- **Coverage proof ledger** — `coverageProof` records pages fetched, empty-page retries, duplicates dropped, last review date, and the `stopCondition`. Emitted on any non-complete status, or always with `debugCoverage`.
- **Deterministic dedup** — Reviews are deduplicated by `reviewId` and emitted in a fixed order (date desc, `reviewId` tiebreak), so re-runs are identical by construction.
- **Honest match confidence** — `placeResolution` carries `matchConfidence` and `alternativesConsidered`; ambiguous resolutions set `ambiguousMatch` so you can verify the right place was picked.

**Reputation intelligence**
- **Review synthesis** — Lexicon sentiment plus TF-IDF complaint and praise themes (`method: tfidf-themes-v1`). Themes are suppressed below a 5-review sample (`synthesisStatus: disabled`).
- **Issue Registry** — Groups synonymous complaint themes into canonical root issues, with `shareOfNegativeReviews` (concentration) and `cohortEmergence` (new problems vs background constants).
- **Rating trajectory + archetype** — `currentRating`, `ratingSlope90d`, recent vs baseline window, and a descriptive place archetype with evidence.
- **Review velocity + response health** — 7-day, 30-day, and prior-30-day volume; owner response rate, median response lag, and unanswered recent negatives.
- **Reputation state machine** — A 5-state value (`healthy`, `monitor`, `attention`, `critical`, `recovery`) derived deterministically, with cross-run transitions and a `reputationTimeline`.
- **Attention routing** — `attentionPriority`, a plain-English `whyNow`, a prioritisation-only `recommendedAction`, and `evidenceReviewIds` pointing at the exact reviews behind the call.
- **Signal events** — Eight typed, decaying events (rating decline, complaint surge, velocity spike, unanswered-negative cluster, and more), each with a `signalStrength` and an evidence block.
- **Location ranking + local benchmark** — Within a multi-place run, rank locations, track `rankMovement`, compute `outlierScore`, and report in-run percentiles.
- **Derived persona projections** — `acquisitionRisk` (for acquirers) and `managerNeglectScore` (for franchise auditors), both re-projections of existing signals with `reusesSignals: true`.
- **Cross-run deltas** — `watchlistName` persists per-place state in a named key-value store for managed monitoring; `comparisonDatasetId` diffs against any prior dataset on the first run.
- **Escalations** — Levelled, timestamped triggers (`maxEscalationLevel` 0-3) for ops automation rules.
- **Client report mode** — `includeClientSummary` assembles deterministic wins / risks / changes per place for monthly client decks.

Multi-location ops teams use Google Maps Reviews Scraper to rank locations by reputation risk and surface which stores fell behind since the last run.

### Use cases for Google Maps reviews scraping

#### Best for multi-location reputation monitoring
Use when you manage many locations and need to spot the ones becoming a problem. Drop a list of business names, schedule with a `watchlistName`, and read the Attention Queue. Key outputs: `reputationState`, `whyNow`, `rankMovement`, `changeFlags`.

#### Best for reputation and review-management agencies
Use when you report monthly on client locations and need "what changed." Run a watchlist or diff a prior export with `comparisonDatasetId`, then enable `includeClientSummary`. Key outputs: `clientSummary`, `newSignalsSinceLastRun`, `ratingChange`, `responseHealth`.

#### Best for local-SEO consultants
Use when you benchmark a client against comparable local places. Run a category-and-area batch so the in-run cohort populates the benchmark. Key outputs: `localBenchmark.ratingPercentile`, `velocityPercentile`, `marketAverageRating`.

#### Best for complaint-theme mining
Use when you need the recurring complaint, not a wall of text. Read the Issue Registry. Key outputs: `activeIssues[].rootIssue`, `shareOfNegativeReviews`, `cohortEmergence.emergence`, `evidenceReviewIds`.

#### Best for acquirers screening a target's locations
Use when you screen review health pre-acquisition. Key outputs: `acquisitionRisk.grade`, `score`, `drivers`, `reputationTimeline`.

#### Best for raw ingestion pipelines
Use when your own pipeline does the analysis and you need complete, stable rows. Set `outputProfile: compat`. Key outputs: the exact compass field set plus a stable `reviewId` for idempotent re-ingestion.

### When to use Google Maps Reviews Scraper

**Best for:**
- Pulling a complete, audited review set from a business name or a list of names.
- Monitoring dozens of locations on a schedule and getting back only what changed.
- Finding the root complaint driving a rating down, with the reviews that prove it.
- Migrating from a place-URL-only workflow without changing downstream code.

**Not ideal for:**
- Forecasting future ratings or revenue impact (out of scope by design).
- Joining Google reviews with TripAdvisor or Yelp (not built; those sources are blocked on this stack).
- Auto-discovering competitors you did not list (the benchmark uses the in-run cohort only).

### How to scrape Google Maps reviews

1. **Enter a business** — Type a business name and location, for example `Domino's Pizza Belfast BT9 6AA`. A place URL, `share.google` link, or place ID also works.
2. **Configure options** — Set `maxReviewsPerPlace` (default 200) and leave `outputProfile` on `signals` for the full read. The advanced section is optional.
3. **Run the actor** — Click Start. The status message updates as each place resolves and paginates.
4. **Download results** — Open the Dataset tab and switch views (Executive Brief, Attention Queue, Coverage Audit, Raw Reviews) or export JSON, CSV, or Excel.

### First run tips

- **Start with one place** — Test with a single business name before scaling to a bulk list, so you can read the Attention Queue and Coverage Audit views once and learn the output shape.
- **Leave `mode` on auto** — The actor classifies the input by shape. You rarely need the explicit `search` / `places` / `placeUrls` modes.
- **A `capped` status is expected if you set a low cap** — Hitting `maxReviewsPerPlace` reports `capped` with a `gapReason`, never a silent truncation. Raise the cap to collect more.
- **Set a `watchlistName` only when you plan to re-run** — On the first run, deltas are empty by design (no fabricated history). The value appears from the second run onward.
- **Use a lat-lng for ambiguous names** — For a name like "Domino's Pizza" with many locations, add `lat`/`lng` in the `places` list to pin the right listing.

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `input` | string | No | — | A business name, place URL, `share.google` link, place ID, feature ID, or CID. Auto-detected by shape. |
| `maxReviewsPerPlace` | integer | No | `200` | Max reviews per place. Hitting it reports `capped`, never a silent truncation. |
| `outputProfile` | enum | No | `signals` | `signals` (full intelligence), `compat` (compass field set, raw), `minimal` (id, text, stars, date). |
| `mode` | enum | No | `auto` | `auto`, `search`, `places`, or `placeUrls`. Auto classifies the `input` string. |
| `places` | array | No | `[]` | Bulk list of names/queries, optionally `{query, lat, lng}` to disambiguate. |
| `placeUrls` | string[] | No | `[]` | Bulk list of URLs, place IDs, feature IDs, CIDs, or `share.google` links. |
| `reviewsOrigin` | enum | No | `google` | `google` (native, recommended) or `all` (incl. third-party; may be `degraded` for hotels). |
| `sort` | enum | No | `newest` | Order of emitted rows. Pagination always uses newest internally for full coverage. |
| `language` | string | No | `en` | Two-letter UI language for the Google Maps requests. |
| `watchlistName` | string | No | — | Enables managed cross-run monitoring, deltas, timeline, and rank movement. |
| `comparisonDatasetId` | string | No | — | Diff this run against a prior Apify dataset. `watchlistName` wins if both are set. |
| `includeReviewImages` | boolean | No | `true` | Include `reviewImageUrls` on review records. |
| `includeReviewerMetadata` | boolean | No | `true` | Include reviewer fields (name, id, review count, Local Guide). |
| `failOnPartialCoverage` | boolean | No | `false` | Fail the run if any place is not `complete`. For trust-critical pipelines. |
| `debugCoverage` | boolean | No | `false` | Always emit the `coverageProof` ledger, even when coverage is complete. |
| `includeClientSummary` | boolean | No | `false` | Assemble a wins / risks / changes summary per place for client decks. |
| `proxyConfiguration` | object | No | Apify residential | Proxy settings. Residential is recommended for Google Maps. |

#### Input examples

- **Single business name (most common):**
```json
{ "input": "Domino's Pizza Belfast BT9 6AA", "maxReviewsPerPlace": 200, "outputProfile": "signals" }
````

- **Multi-location watchlist (monitoring):**

```json
{
  "places": ["Northstar Coffee Glasgow", "Northstar Coffee Edinburgh", "Northstar Coffee Aberdeen"],
  "watchlistName": "northstar-uk-locations",
  "includeClientSummary": true
}
```

- **Drop-in raw extraction (migration):**

```json
{
  "placeUrls": ["https://www.google.com/maps/place/?q=place_id:ChIJexample"],
  "outputProfile": "compat",
  "maxReviewsPerPlace": 100000
}
```

#### Input tips

- **Start with defaults** — `mode: auto` and `outputProfile: signals` cover most first runs.
- **Use `places` for bulk names** — A single run over a list is how you get location ranking and the in-run benchmark.
- **Use `compat` for ingestion** — When a downstream pipeline does its own analysis, `compat` returns the raw field set with a stable `reviewId`.
- **Raise the cap for full coverage** — Set `maxReviewsPerPlace` high enough to reach `claimed_count_reached` if you want `complete` status.

### Output example

A `place_intelligence` record from the `signals` profile (abbreviated for readability):

```json
{
  "recordType": "place_intelligence",
  "coverageVersion": "1.0",
  "reputationStateVersion": "1.0",
  "title": "Domino's Pizza - Belfast",
  "placeId": "ChIJexamplebt96aa",
  "fid": "0x4861609fe9038cf3:0xexample",
  "url": "https://www.google.com/maps/place/?q=place_id:ChIJexamplebt96aa",
  "address": "12 Lisburn Road, Belfast BT9 6AA",
  "categoryName": "Pizza delivery",
  "placeResolution": {
    "inputType": "query",
    "resolvedPlaceId": "ChIJexamplebt96aa",
    "resolvedTitle": "Domino's Pizza - Belfast",
    "matchConfidence": 0.85,
    "alternativesConsidered": []
  },
  "ambiguousMatch": false,
  "coverage": {
    "reviewsClaimed": 312,
    "reviewsScraped": 312,
    "coverageRatio": 1.0,
    "coverageStatus": "complete",
    "gapReason": null,
    "reviewsOrigin": "google",
    "deterministicRun": true
  },
  "reviewSynthesis": {
    "synthesisStatus": "full",
    "sampleSize": 312,
    "sentiment": { "positive": 0.58, "negative": 0.28, "neutral": 0.14 },
    "themes": [
      { "themeCode": "service_speed", "label": "slow service / wait times", "polarity": "negative", "weight": 0.31, "trend": "rising", "exampleReviewIds": ["Ci9DQUlRexample1"] }
    ],
    "method": "tfidf-themes-v1"
  },
  "activeIssues": [
    {
      "rootIssue": "delivery_delay",
      "label": "slow / late service",
      "polarity": "negative",
      "mentions": 36,
      "shareOfNegativeReviews": 0.42,
      "trend": "rising",
      "cohortEmergence": { "olderShare": 0.02, "recent90dShare": 0.14, "recent30dShare": 0.21, "emergence": "emerging" },
      "firstSeenReviewAt": "2026-03-02",
      "lastSeenReviewAt": "2026-05-27",
      "evidenceReviewIds": ["Ci9DQUlRexample1", "Ci9DQUlRexample2"]
    }
  ],
  "ratingTrajectory": {
    "currentRating": 4.1, "ratingSlope90d": -0.18, "trend": "declining",
    "recentWindowRating": 3.6, "baselineRating": 4.3, "delta": -0.7
  },
  "placeArchetype": "at-risk",
  "placeArchetypeStrength": 0.82,
  "reviewVelocity": { "last7d": 4, "last30d": 19, "prior30d": 8, "velocityTrend": "rising" },
  "responseHealth": { "ownerResponseRate": 0.42, "negativeResponseRate30d": 0.18, "medianResponseLagDays": 6, "unansweredNegatives30d": 9 },
  "attentionPriority": "high",
  "whyNow": [
    "Rating dropped 0.7 in the recent window (4.3 to 3.6).",
    "Complaint 'slow / late service' rising - now 42% of negative reviews.",
    "9 recent negative reviews with no owner response."
  ],
  "recommendedAction": "Investigate the slow-service complaints this week.",
  "respondWithinDays": 7,
  "evidenceReviewIds": ["Ci9DQUlRexample1", "Ci9DQUlRexample2"],
  "reputationState": {
    "current": "critical", "previous": "attention",
    "enteredCurrentAt": "2026-05-15", "daysInCurrentState": 14,
    "daysInPreviousState": 23, "stateVersion": "1.0"
  },
  "reputationTimeline": [
    { "date": "2026-05-15", "event": "entered_state", "detail": "attention to critical" }
  ],
  "locationRank": 17, "previousRank": 8, "rankMovement": -9, "outlierScore": 0.82,
  "localBenchmark": {
    "cohortBasis": "in-run", "sampleSize": 18, "marketAverageRating": 4.3,
    "ratingPercentile": 41, "marketAverageReviewVelocity": 14, "velocityPercentile": 62
  },
  "acquisitionRisk": { "score": 82, "grade": "high", "drivers": ["rating_decline", "response_neglect"], "reusesSignals": true },
  "managerNeglectScore": { "score": 78, "grade": "high", "drivers": ["unanswered_negatives", "slow_response_lag"], "reusesSignals": true },
  "escalations": [
    { "escalationLevel": 3, "trigger": "entered_critical", "reason": "attention to critical (delivery_delay at 42% of negatives)", "firedAt": "2026-05-15" }
  ],
  "maxEscalationLevel": 3,
  "changeFlags": ["RATING_DROP", "NEW_COMPLAINT_THEME"]
}
```

![Sample output: places ranked worst-first with state, top issue, unanswered negatives, and coverage](https://apifyforge.com/readme-assets/ryanclinton-google-maps-reviews-scraper/output-table.png)

### Output fields

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | `place_intelligence`, `review`, `run_summary`, or `error`. Omitted in `compat`. |
| `executiveBrief` | object | One-glance per-place read: `state`, `rankMovement`, `topIssue`, `issueShare`, `unansweredNegatives`, `coverage`. |
| `coverage.reviewsClaimed` | number | The place's own stated total from the Google header. |
| `coverage.reviewsScraped` | number | Reviews actually collected. |
| `coverage.coverageStatus` | string | `complete`, `partial`, `capped`, or `degraded`. |
| `coverage.deterministicRun` | boolean | True when the review set is reproducible by construction. |
| `coverageProof.stopCondition` | string | Why pagination ended (`claimed_count_reached`, `max_reviews_reached`, etc.). |
| `placeResolution.matchConfidence` | number | 0-1 heuristic confidence the right place was resolved. |
| `reviewSynthesis.sentiment` | object | Positive / negative / neutral shares. |
| `activeIssues[].rootIssue` | string | Canonical complaint issue (additive-only enum). |
| `activeIssues[].shareOfNegativeReviews` | number | Complaint concentration. |
| `activeIssues[].cohortEmergence.emergence` | string | `emerging`, `persistent`, `fading`, or `stable`. |
| `ratingTrajectory.delta` | number | Recent-window rating minus baseline. |
| `reputationState.current` | string | `healthy`, `monitor`, `attention`, `critical`, or `recovery`. |
| `responseHealth.unansweredNegatives30d` | number | Recent negatives with no owner response. |
| `attentionPriority` | string | `high`, `medium`, `low`, or `none`. |
| `whyNow` | string\[] | Plain-English reasons the place needs attention. |
| `evidenceReviewIds` | string\[] | The exact reviews behind the attention call. |
| `locationRank` / `rankMovement` | number | Rank within the run and movement vs the prior run. |
| `localBenchmark.ratingPercentile` | number | Percentile against the in-run cohort. |
| `acquisitionRisk.grade` | string | `low`, `moderate`, `high`, or `critical`. |
| `maxEscalationLevel` | number | 0-3 escalation level for automation rules. |
| `review.reviewId` | string | Google's stable review ID (dedup key). |
| `review.stableReviewKey` | string | Namespaced idempotent key (`google:<placeId>:<reviewId>`). |
| `review.publishedAt` | object | `raw`, `date`, `datePrecision`, `extractionMethod` (signals profile). |
| `review.reviewSignals` | object | Per-review sentiment, theme codes, and flags (signals profile). |

### Scrape Google Maps reviews using the API

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("ryanclinton/google-maps-reviews-scraper").call(run_input={
    "input": "Domino's Pizza Belfast BT9 6AA",
    "maxReviewsPerPlace": 200,
    "outputProfile": "signals",
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("recordType") == "place_intelligence":
        cov = item["coverage"]
        print(f"{item['title']}: {cov['reviewsScraped']}/{cov['reviewsClaimed']} ({cov['coverageStatus']}), state={item['reputationState']['current']}")
```

#### JavaScript

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

const client = new ApifyClient({ token: "YOUR_API_TOKEN" });

const run = await client.actor("ryanclinton/google-maps-reviews-scraper").call({
    input: "Domino's Pizza Belfast BT9 6AA",
    maxReviewsPerPlace: 200,
    outputProfile: "signals",
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) {
    if (item.recordType === "place_intelligence") {
        const c = item.coverage;
        console.log(`${item.title}: ${c.reviewsScraped}/${c.reviewsClaimed} (${c.coverageStatus}), state=${item.reputationState.current}`);
    }
}
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/ryanclinton~google-maps-reviews-scraper/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": "Northstar Coffee Glasgow", "maxReviewsPerPlace": 200, "outputProfile": "signals" }'

curl "https://api.apify.com/v2/datasets/DATASET_ID/items?token=YOUR_API_TOKEN&format=json"
```

### How Google Maps Reviews Scraper works

**Mental model:** Input -> place resolution -> paginated reviews with a coverage ledger -> deterministic synthesis -> per-place and cross-run intelligence -> profiled output.

| Step | What happens |
|------|--------------|
| 1. Resolve | Classify the input by shape and resolve it to a place (feature ID, place ID, CID, title, claimed count). |
| 2. Paginate | Fetch reviews until `reviewsScraped == reviewsClaimed` or the cap is hit, recording the coverage ledger. |
| 3. Synthesize | Lexicon sentiment, TF-IDF themes, Issue Registry, and per-review signals in one deterministic pass. |
| 4. Per-place intelligence | Rating trajectory, velocity, response health, signal events, reputation state value, attention routing. |
| 5. In-run cohort | Location ranking, outlier score, and the local benchmark. |
| 6. Cross-run state | Reputation state machine, timeline, watchlist or comparison delta, escalations, client summary. |

#### Place resolution

The actor classifies the input string with regular expressions: `0x..:0x..` is a feature ID, `ChIJ...` is a place ID, bare digits are a CID, `share.google` or `maps.google` links resolve through the Maps page, and anything else is treated as free-text search. Resolution emits `placeResolution` with `matchConfidence` and any `alternativesConsidered`, and flags `ambiguousMatch` when confidence is low and there are multiple candidates.

#### Coverage and determinism

Pagination always runs in newest order for full coverage. The paginator records pages fetched, empty-page retries, duplicates dropped, and the stop condition into `coverageProof`. Reviews are deduplicated by `reviewId` and emitted in a fixed order, so the same place produces an identical set on every run.

#### Deterministic synthesis

Themes are extracted by TF-IDF against a fixed theme dictionary, then mapped up into canonical root issues. Sentiment uses a lexicon backstop. Every signal, state, escalation, and score is computed deterministically. An optional LLM is used only for naming themes when explicitly enabled, never in the decision path.

### Tips for best results

1. **Run locations together** — Location ranking and the local benchmark are computed across the places in a single run, so batch comparable locations into one run rather than many single-place runs.
2. **Schedule a watchlist** — A daily or weekly `watchlistName` run turns one-shot scraping into a reputation feed with deltas, a timeline, and rank movement.
3. **Diff a prior export on day one** — If you already have a past dataset, set `comparisonDatasetId` to get "what changed" on your first run instead of waiting for a second.
4. **Turn on `debugCoverage` when auditing** — It emits the full proof ledger even on complete runs, useful for verifying the coverage claim.
5. **Use `failOnPartialCoverage` for trust-critical pipelines** — A non-complete place becomes a run failure rather than a silent gap.
6. **Branch on the state machine** — Route only the locations that need action by checking `reputationState.current` or `maxEscalationLevel`.

### Combine with other Apify actors

| Actor | How to combine |
|-------|----------------|
| [Trustpilot Review Analyzer](https://apify.com/ryanclinton/trustpilot-review-analyzer) | Add Trustpilot sentiment alongside Google reviews for a fuller reputation picture. |
| [Multi-Review Analyzer](https://apify.com/ryanclinton/multi-review-analyzer) | Analyze reviews from multiple platforms in one pass. |
| [Google Maps Email Extractor](https://apify.com/ryanclinton/google-maps-email-extractor) | Turn flagged locations into contactable leads with email and phone. |
| [Company Deep Research](https://apify.com/ryanclinton/company-deep-research) | Pair reputation signals with a full company intelligence report. |
| [Website Change Monitor](https://apify.com/ryanclinton/website-change-monitor) | Watch a location's website alongside its review trajectory. |
| [HubSpot Lead Pusher](https://apify.com/ryanclinton/hubspot-lead-pusher) | Push at-risk locations or flagged leads into HubSpot. |

### Limitations

- **Coverage reflects Google's own header.** `reviewsClaimed` comes from the place header; if Google's header lags or jitters, a complete scrape can read slightly under 1.0. A `gapReason` always explains a sub-1.0 ratio, and complete allows a one-review header jitter.
- **Partial scrapes happen.** When Google stops serving pages mid-corpus, the status is `partial` with a `gapReason`, not a silent loss.
- **Ambiguous names can resolve to the wrong place.** Add a lat-lng to disambiguate; `ambiguousMatch` and `alternativesConsidered` flag the risk.
- **Themes need a sample.** Below five reviews, synthesis is `disabled` and only sentiment is reported.
- **No cross-platform joins.** Google reviews only; TripAdvisor and Yelp are not included.
- **The local benchmark is in-run only.** It compares against the places in the run; it does not auto-discover competitors.
- **The reputation timeline accrues over time.** The first run seeds one entry; the full history builds as the watchlist runs.
- **Third-party origin can be degraded.** With `reviewsOrigin: all`, Google's reduced hotel coverage is flagged as `degraded`.

### What this does NOT do

Google Maps Reviews Scraper is read-only and deterministic. It does not:

- **Post, reply to, flag, or delete reviews on Google.** It surfaces that negatives are unanswered; it never acts on your behalf on the platform. `recommendedAction` is prioritisation-only ("Review", "Investigate", "Monitor", "Compare").
- **Predict revenue or future ratings.** No forecasting of any kind.
- **Score controversy or brand safety.** That is a liability profile this actor does not take on.
- **Judge whether a review is true.** It extracts and interprets public reviews; it does not adjudicate their accuracy.
- **Use an LLM in the decision path.** Every signal, state, escalation, and score is deterministic. An optional LLM names themes only, when explicitly enabled.

### Integrations

- [Zapier](https://apify.com/integrations/zapier) — Trigger a downstream action when a location enters a critical state.
- [Make](https://apify.com/integrations/make) — Route escalations by `maxEscalationLevel` into Slack or a ticketing tool.
- [Google Sheets](https://apify.com/integrations/google-sheets) — Export the Attention Queue or Location Ranking for a shared dashboard.
- [Apify API](https://docs.apify.com/api/v2) — Run on a schedule and pull the dataset programmatically.
- [Webhooks](https://docs.apify.com/platform/integrations/webhooks) — Fire on run completion to push fresh deltas into your pipeline.
- [LangChain / LlamaIndex](https://docs.apify.com/platform/integrations) — Feed structured reputation signals into an AI workflow.

### How do I get Google reviews without a place URL?

Type the business name and location into the `input` field, for example `Northstar Coffee Glasgow`, and run the actor. `mode: auto` classifies the text as a free-text query, resolves it to a Google place, and scrapes the reviews. No place URL needed.

### How do I monitor a location's reviews over time?

Set a `watchlistName` and run the actor on a schedule. It persists per-place state in a named key-value store and returns deltas, a reputation timeline, and rank movement on every run after the first. To compare against a single past export instead, set `comparisonDatasetId`.

### How do I migrate from a place-URL reviews scraper?

Put your existing place URLs or IDs into `placeUrls` and set `outputProfile: compat`. The actor returns the same field set, so downstream code reading `text`, `reviewId`, or `stars` works unchanged, and you can additionally type business names that a place-URL-only tool cannot accept.

### Troubleshooting

**The run finished with only error records.** The input could not be resolved to a place. Check the Errors view for the `failureType` (for example `resolution_failed`) and try a more specific business name plus location, or a place URL.

**Coverage shows `capped`.** Your `maxReviewsPerPlace` was reached before the place was exhausted. The `gapReason` states the claimed total. Raise the cap to collect more, or accept the cap as an explicit, honest limit.

**Coverage shows `partial`.** Google stopped serving pages mid-corpus. The actor retried empty pages up to its limit before stopping. Re-run with residential proxies; partial results are still emitted.

**The wrong place was scraped.** `ambiguousMatch` is true and `alternativesConsidered` lists other candidates. Add a lat-lng in the `places` list to pin the correct listing.

**The run was halted with a bot-block error.** The circuit breaker stopped the run after consecutive blocks. Use residential proxies and retry; any results collected before the halt were emitted.

### Key takeaways

- **Complete, audited reviews from a business name** — `reviewsClaimed` vs `reviewsScraped` with a stop condition, resolved from six input shapes, no place URL required.
- **Deterministic by construction** — `reviewId`-keyed dedup and fixed ordering mean re-runs are identical; the signal layer runs without an LLM in the decision path.
- **A reputation read, not just rows** — A 5-state machine, an Issue Registry with cohort emergence, location ranking, and a ranked attention queue, all with an evidence trail.
- **Cross-run monitoring built in** — `watchlistName` for managed deltas and `comparisonDatasetId` for a first-run diff, with a reputation timeline that accrues over time.
- **Schema you can pin** — Ten version constants on every record so automations stay stable across releases.

### Recent updates

- **Universal input resolution** — Resolve a business name, postcode, lat-lng, URL, `share.google` link, place ID, feature ID, or CID in one run.
- **Coverage audit and proof ledger** — Claimed vs collected with a coverage status and a `coverageProof` receipt.
- **Issue Registry** — Root-issue grouping with complaint concentration and recency-cohort emergence.
- **Reputation state machine and timeline** — Five states with cross-run transitions and a per-place history.
- **Escalations, ranking, and derived scores** — Levelled automation triggers, location ranking, acquisition-risk and manager-neglect projections.
- **Watchlist and comparison-diff modes** — Managed cross-run deltas, plus a one-off diff against any prior dataset.

### Responsible use

- Google Maps Reviews Scraper extracts publicly available reviews from Google Maps. It does not bypass authentication, CAPTCHAs, or access restricted content.
- Users are responsible for ensuring their use complies with applicable laws and platform terms, including data protection regulations in their jurisdiction.
- Do not use extracted data for spam, harassment, or unauthorized purposes.
- For guidance on web scraping legality, see [Apify's guide](https://blog.apify.com/is-web-scraping-legal/).

### FAQ

**What is the difference between a Google Maps reviews scraper and this actor?** A typical Google Maps reviews scraper keys off a place URL and returns rows. Google Maps Reviews Scraper resolves a business name itself, audits the collection against Google's claimed count, deduplicates deterministically, and returns a reputation read on top of the rows.

**Can I scrape reviews from just a business name?** Yes. Type the name and location into `input`; `mode: auto` resolves it to a place before scraping. This is a practical alternative to place-URL-only reviews scrapers when you have a business list rather than URLs.

**Can I run this on many locations at once?** Yes. Use the `places` or `placeUrls` lists. A multi-place run also produces location ranking and an in-run local benchmark.

**Can I verify the scrape was complete?** Yes. Every place carries `reviewsClaimed`, `reviewsScraped`, and a `coverageStatus`, and `debugCoverage` emits the full proof ledger. The actor reports exactly what Google claimed and exactly what it collected.

**How does the actor detect a new complaint rather than a constant one?** The Issue Registry computes `cohortEmergence` across recency buckets. An issue at a low share in older reviews but a high share in the last 30 days is marked `emerging`.

**How does monitoring work?** Set a `watchlistName` and run on a schedule. State persists in a named key-value store, and each later run returns deltas, rank movement, and a reputation timeline. A `comparisonDatasetId` gives the same delta against a prior dataset on the first run.

**What does `recommendedAction` tell me?** It is a prioritisation instruction only ("Review", "Investigate", "Monitor", "Compare", "Re-check in N days"). It never tells you to respond on Google or predicts business outcomes.

**Is the output deterministic?** Yes. Reviews are deduplicated by `reviewId` and emitted in a fixed order, and the signal layer is computed without an LLM in the decision path, so the same input reproduces the same output.

**What output formats are supported?** JSON, CSV, and Excel from the Dataset tab, plus distinct dataset views (Attention Queue, Coverage Audit, Raw Reviews, and more).

**How is this different from a place-URL-only scraper for migration?** Set `outputProfile: compat` to get the same field set, so downstream code works unchanged, while gaining business-name input and the coverage audit.

**Does it handle hotels and third-party reviews?** With `reviewsOrigin: all` it includes third-party aggregated reviews, and flags Google's reduced hotel coverage as `degraded`. The default `google` origin is recommended for full native coverage.

**Is it legal to scrape Google reviews?** The actor extracts publicly available reviews and does not bypass access controls. Legality depends on your jurisdiction and intended use; consult legal counsel and follow applicable data-protection rules. See [Apify's guide on scraping legality](https://blog.apify.com/is-web-scraping-legal/).

### Help us improve

If you encounter issues, you can help us debug faster by enabling run sharing in your Apify account:

1. Go to [Account Settings > Privacy](https://console.apify.com/account/privacy)
2. Enable **Share runs with public Actor creators**

This lets us see your run details when something goes wrong, so we can fix issues faster. Your data is only visible to the actor developer, not publicly.

### Support

Found a bug or have a feature request? Open an issue in the Issues tab on this actor's page. For custom solutions or enterprise integrations, reach out through the Apify platform.

# Actor input Schema

## `input` (type: `string`):

Type anything: a business name ("Domino's Pizza Belfast BT9 6AA"), a Google Maps place URL, a share.google short link, a place ID (ChIJ...), a feature ID (0x...:0x...), or a CID. The actor auto-detects the shape and resolves it to a place before scraping reviews. For bulk runs use the advanced 'places' or 'placeUrls' lists instead.

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

Maximum reviews to collect per place. Set to 0 for NO LIMIT — collect every review the place has, all the way back (pagination stops naturally when Google runs out). If a positive value is lower than a place's review count, coverage status is reported as 'capped' (an honest, explained cap, never a silent truncation). Collection is always newest-first, so a low cap drops the OLDEST reviews. No limit uses the most memory and runtime.

## `outputProfile` (type: `string`):

signals (default) = full reputation intelligence on top of every field. compat = exact compass/Google-Maps-Reviews-Scraper field set for drop-in migration and raw ingestion (no intelligence layer). minimal = reviewId, text, stars, publishedAt only.

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

auto (default) lets the actor classify the 'input' string by shape. search forces free-text search. places uses the 'places' bulk list. placeUrls uses the 'placeUrls' bulk list (compass parity).

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

Bulk list of business names / queries, optionally with lat-lng to disambiguate. Each item: a plain string query, or an object {"query": "...", "lat": 54.5, "lng": -5.9}.

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

Bulk list of Google Maps place URLs, place IDs, feature IDs, CIDs, or share.google links (compass parity).

## `reviewsOrigin` (type: `string`):

google = Google-native reviews only (recommended, full coverage). all = include third-party aggregated reviews (Google reports reduced hotel coverage here; the actor flags this as 'degraded' when relevant).

## `sort` (type: `string`):

Pagination always uses 'newest' internally to guarantee complete coverage (Google's date-range sort silently stops scraping). Date-range filtering is applied client-side after fetch. This field only affects the order of the emitted review rows.

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

Two-letter UI language for the Google Maps requests (e.g. en, fr, de).

## `watchlistName` (type: `string`):

Set a name to enable managed cross-run monitoring. The actor persists per-place state in a named key-value store and returns deltas (what changed since last run), a reputation timeline, and rank movement. Use the same name on a schedule for a daily reputation feed.

## `comparisonDatasetId` (type: `string`):

Diff this run against any prior Apify dataset (an old export or a competitor's run). Returns the same delta block as watchlist mode, on your first run. If both this and watchlistName are set, watchlistName wins.

## `includeReviewImages` (type: `boolean`):

Include reviewImageUrls on review records.

## `includeReviewerMetadata` (type: `boolean`):

Include reviewer fields (name, reviewerId, reviewerNumberOfReviews, isLocalGuide, etc.).

## `failOnPartialCoverage` (type: `boolean`):

If any place returns a coverage status other than 'complete', fail the whole run instead of finishing with a silent gap. For trust-critical pipelines.

## `debugCoverage` (type: `boolean`):

Emit the full coverageProof ledger (pages fetched, retries, duplicates dropped, stop condition) on every place, even when coverage is complete.

## `includeClientSummary` (type: `boolean`):

Assemble a deterministic wins / risks / changes summary per place, ready to drop into a monthly client deck. Most meaningful with a watchlist or comparison dataset.

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

Proxy settings. Residential proxies are strongly recommended for Google Maps. Defaults to Apify residential.

## Actor input object example

```json
{
  "input": "Domino's Pizza Belfast BT9 6AA",
  "maxReviewsPerPlace": 200,
  "outputProfile": "signals",
  "mode": "auto",
  "places": [],
  "placeUrls": [],
  "reviewsOrigin": "google",
  "sort": "newest",
  "language": "en",
  "includeReviewImages": true,
  "includeReviewerMetadata": true,
  "failOnPartialCoverage": false,
  "debugCoverage": false,
  "includeClientSummary": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `reviews` (type: `string`):

Review rows, per-place intelligence records, and the run summary in the default dataset.

## `runSummary` (type: `string`):

The portfolio / coverage rollup record (SUMMARY) in the default key-value store.

# 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 = {
    "input": "Domino's Pizza Belfast BT9 6AA",
    "maxReviewsPerPlace": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryanclinton/google-maps-reviews-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 = {
    "input": "Domino's Pizza Belfast BT9 6AA",
    "maxReviewsPerPlace": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("ryanclinton/google-maps-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "input": "Domino'\''s Pizza Belfast BT9 6AA",
  "maxReviewsPerPlace": 200
}' |
apify call ryanclinton/google-maps-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ryanclinton/google-maps-reviews-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Maps Reviews Scraper + Reputation Intelligence",
        "description": "Scrapes Google Maps reviews from a business name, postcode, or place URL. Returns the complete review set with a coverage audit (claimed vs collected), sentiment, complaint themes, rating trajectory, response health, and a ranked reputation attention queue.",
        "version": "1.0",
        "x-build-id": "MY1BsiYcKJUOaKzos"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ryanclinton~google-maps-reviews-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ryanclinton-google-maps-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ryanclinton~google-maps-reviews-scraper/runs": {
            "post": {
                "operationId": "runs-sync-ryanclinton-google-maps-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ryanclinton~google-maps-reviews-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-ryanclinton-google-maps-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "input": {
                        "title": "Business name, place URL, or place ID",
                        "type": "string",
                        "description": "Type anything: a business name (\"Domino's Pizza Belfast BT9 6AA\"), a Google Maps place URL, a share.google short link, a place ID (ChIJ...), a feature ID (0x...:0x...), or a CID. The actor auto-detects the shape and resolves it to a place before scraping reviews. For bulk runs use the advanced 'places' or 'placeUrls' lists instead."
                    },
                    "maxReviewsPerPlace": {
                        "title": "Max reviews per place",
                        "minimum": 0,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Maximum reviews to collect per place. Set to 0 for NO LIMIT — collect every review the place has, all the way back (pagination stops naturally when Google runs out). If a positive value is lower than a place's review count, coverage status is reported as 'capped' (an honest, explained cap, never a silent truncation). Collection is always newest-first, so a low cap drops the OLDEST reviews. No limit uses the most memory and runtime.",
                        "default": 200
                    },
                    "outputProfile": {
                        "title": "Output profile",
                        "enum": [
                            "signals",
                            "compat",
                            "minimal"
                        ],
                        "type": "string",
                        "description": "signals (default) = full reputation intelligence on top of every field. compat = exact compass/Google-Maps-Reviews-Scraper field set for drop-in migration and raw ingestion (no intelligence layer). minimal = reviewId, text, stars, publishedAt only.",
                        "default": "signals"
                    },
                    "mode": {
                        "title": "Input mode",
                        "enum": [
                            "auto",
                            "search",
                            "places",
                            "placeUrls"
                        ],
                        "type": "string",
                        "description": "auto (default) lets the actor classify the 'input' string by shape. search forces free-text search. places uses the 'places' bulk list. placeUrls uses the 'placeUrls' bulk list (compass parity).",
                        "default": "auto"
                    },
                    "places": {
                        "title": "Places (bulk list)",
                        "type": "array",
                        "description": "Bulk list of business names / queries, optionally with lat-lng to disambiguate. Each item: a plain string query, or an object {\"query\": \"...\", \"lat\": 54.5, \"lng\": -5.9}.",
                        "default": []
                    },
                    "placeUrls": {
                        "title": "Place URLs / IDs (bulk list)",
                        "type": "array",
                        "description": "Bulk list of Google Maps place URLs, place IDs, feature IDs, CIDs, or share.google links (compass parity).",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "reviewsOrigin": {
                        "title": "Reviews origin",
                        "enum": [
                            "google",
                            "all"
                        ],
                        "type": "string",
                        "description": "google = Google-native reviews only (recommended, full coverage). all = include third-party aggregated reviews (Google reports reduced hotel coverage here; the actor flags this as 'degraded' when relevant).",
                        "default": "google"
                    },
                    "sort": {
                        "title": "Sort",
                        "enum": [
                            "newest",
                            "relevant",
                            "highest",
                            "lowest"
                        ],
                        "type": "string",
                        "description": "Pagination always uses 'newest' internally to guarantee complete coverage (Google's date-range sort silently stops scraping). Date-range filtering is applied client-side after fetch. This field only affects the order of the emitted review rows.",
                        "default": "newest"
                    },
                    "language": {
                        "title": "Language",
                        "type": "string",
                        "description": "Two-letter UI language for the Google Maps requests (e.g. en, fr, de).",
                        "default": "en"
                    },
                    "watchlistName": {
                        "title": "Watchlist name",
                        "type": "string",
                        "description": "Set a name to enable managed cross-run monitoring. The actor persists per-place state in a named key-value store and returns deltas (what changed since last run), a reputation timeline, and rank movement. Use the same name on a schedule for a daily reputation feed."
                    },
                    "comparisonDatasetId": {
                        "title": "Comparison dataset ID",
                        "type": "string",
                        "description": "Diff this run against any prior Apify dataset (an old export or a competitor's run). Returns the same delta block as watchlist mode, on your first run. If both this and watchlistName are set, watchlistName wins."
                    },
                    "includeReviewImages": {
                        "title": "Include review images",
                        "type": "boolean",
                        "description": "Include reviewImageUrls on review records.",
                        "default": true
                    },
                    "includeReviewerMetadata": {
                        "title": "Include reviewer metadata",
                        "type": "boolean",
                        "description": "Include reviewer fields (name, reviewerId, reviewerNumberOfReviews, isLocalGuide, etc.).",
                        "default": true
                    },
                    "failOnPartialCoverage": {
                        "title": "Fail on partial coverage",
                        "type": "boolean",
                        "description": "If any place returns a coverage status other than 'complete', fail the whole run instead of finishing with a silent gap. For trust-critical pipelines.",
                        "default": false
                    },
                    "debugCoverage": {
                        "title": "Always emit the coverage proof ledger",
                        "type": "boolean",
                        "description": "Emit the full coverageProof ledger (pages fetched, retries, duplicates dropped, stop condition) on every place, even when coverage is complete.",
                        "default": false
                    },
                    "includeClientSummary": {
                        "title": "Include client summary (agency report mode)",
                        "type": "boolean",
                        "description": "Assemble a deterministic wins / risks / changes summary per place, ready to drop into a monthly client deck. Most meaningful with a watchlist or comparison dataset.",
                        "default": false
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings. Residential proxies are strongly recommended for Google Maps. Defaults to Apify residential.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
