# Google Reviews Response Backlog Builder (`zinin/google-reviews-response-backlog`) Actor

Build a response queue from Google Maps review datasets. Separate answered, unanswered and unknown records, show rating and age evidence, and prioritize each useful review assessment.

- **URL**: https://apify.com/zinin/google-reviews-response-backlog.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Business, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.60 / 1,000 review assessments

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Reviews Response Backlog Builder — turn a reviews batch into a ranked response queue

Feed it an existing Google Maps reviews dataset and get back one review assessment per unique review — answered/unanswered/unknown status, a disclosed urgency score, and bounded keyword-bucket signals — never an AI reading of the review or an automated reply.

![Google Reviews Response Backlog Builder — hero diagram](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/d94b27ab62fe5f65d0258277507ea8d341761c8d/value-add-20260923/google-reviews-response-backlog/readme-hero.webp)

This Actor does not scrape Google Maps. It reads a reviews batch you already have — most naturally from [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper) and/or [Google Maps Places Scraper](https://apify.com/compass/crawler-google-places) — and turns it into one response-backlog record per unique `reviewId`: whether the owner has answered, how urgent an unanswered review is, and which of eight fixed issue-keyword buckets its text matched. It is a triage tool for teams who already have a reviews batch — not a review reader, not a reply sender, and not a sentiment-accuracy engine.

### What you get

For each distinct review found across your source batch, you get one assessment record containing:

- **`entityId`**/**`reviewId`** — the canonical, trimmed review ID (identical on both fields).
- **`status`**/**`responseStatus`** — `answered`, `unanswered`, or `unknown`. `unknown` covers a missing/wrong-typed `responseFromOwnerText` field **or** a detected `responseDateTextConflict` (see below) — never a confident guess.
- **`urgency`**/**`urgencyScore`** — `resolved`/`urgent`/`moderate`/`low`/`unknown`, from a disclosed additive rule (base points for unanswered, a star-rating component, an age component versus your `staleDaysThreshold`) — `urgencyScore` is `null` when `urgency` is `unknown`.
- **`issueKeywordBuckets`** and **`keywordsSupported`** — bounded matches against eight fixed keyword buckets (`refund`, `rude_staff`, `cleanliness`, `wait_time`, `safety`, `billing`, `quality`, `cancellation`) on text confirmed English or confirmed machine-translated to English; `keywordsSupported: false` when the language couldn't be confirmed.
- **`reviewText`**/**`reviewTextTranslated`** — original/translated text, each truncated to 1000 characters with its own truncation flag.
- **`publishedAtDate`**, **`responseFromOwnerDate`**, **`ageDays`**/**`ageBasis`** — strict calendar-valid ISO-8601 review-event dates (date-only accepted), age computed only when a valid observation reference exists — genuinely `null`/unknown otherwise, never guessed from wall-clock time.
- **`responseDateTextConflict`** — `true` when a valid `responseFromOwnerDate` coexists with a blank/null **or** genuinely absent `responseFromOwnerText`, audited across every duplicate row sharing this `reviewId`, not just the newest one. A genuinely absent text only reaches this check when a supported keyword match elsewhere already cleared the row's usefulness gate; with no keyword match it is a free diagnostic before the conflict is ever evaluated.
- **`evidence`** — the matched field, value, and rule behind every claim, tied to a `sourceRowIndex`.
- **Honest diagnostics for everything else** — an invalid row, a source-reported failure, a missing identity, or insufficient evidence are all free, separate rows.

What this Actor is explicitly **not**: no AI-based review reading, no automated reply generation or sending, no calibrated sentiment-accuracy score, no broad or general-purpose language coverage. Reviewer names, profile IDs/URLs, and photos are never emitted, even if present in the source row.

### Who uses it

- **Multi-location operators** turning each new reviews-scraper batch into a triaged, prioritized response work queue instead of scrolling Google Maps by hand.
- **Customer-service leads** who need a defensible, evidence-linked reason a review is flagged urgent, not an opaque score.
- **Reputation/marketing teams** aggregating issue-keyword totals (refunds, staff conduct, cleanliness, wait times) across a location batch for weekly reporting.
- **Data-quality teams** using `responseDateTextConflict` and `keywordsSupported` as a signal for how clean an upstream reviews scrape actually is.
- **Franchise and multi-brand groups** running the same batch shape across dozens of locations, where a consistent, disclosed urgency rule matters more than any single location manager's judgment call.

None of these use cases require, and this Actor never claims to provide, an automated response draft, a sentiment score calibrated against real customer outcomes, or coverage of languages beyond confirmed-English/English-translated text.

Not a scraper: point it at an existing reviews Dataset producing the row shape in **Input contract**, most naturally [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper) (`users30d=6845`, `successfulRuns30d=12,829,388`) or [Google Maps Places Scraper](https://apify.com/compass/crawler-google-places) (`users30d=37173`, `successfulRuns30d=3,751,440`). This Actor never starts either one, and never fetches a page from the open web.

### How to run

1. **Console (fastest for a one-off run).** Open the Actor, go to Input, paste rows into **Source records (inline)** or fill in **Source Dataset**, leave defaults, hit **Start**.
2. **From an existing reviews Dataset.** Put its Dataset ID in `datasetId` — read with your own run's permissions, no separate authorization step.
3. **Chained via Integrations.** Add this Actor as an "Actor/task run" integration on your reviews-scraper run; map `{{resource.defaultDatasetId}}` into this Actor's `datasetId` field explicitly (see **Integration recipes**) for a `READ` grant — a bare Dataset ID or webhook payload alone does not grant access.
4. **Via the API.** Call `POST /v2/acts/WgO1bFVK3eBJuKd7C/runs` (or `run-sync-get-dataset-items`) with `datasetId` (or `items`) and, for a spending ceiling, the run option `maxTotalChargeUsd`.
5. **Set your SLA window.** `staleDaysThreshold` (default 7) controls when age escalation kicks in; 4x that threshold adds a larger escalation.
6. **Read the results.** One row per unique review lands in the run's default Dataset, with a matching `OUTPUT`/`SUMMARY` record.

Every run is independent processing — see **Operating guide** for what that means for re-running against the same batch.

### Pricing

Pay-per-event, one event type: **`result-found`** ("Review assessment" in the platform's pricing UI — the primary pricing noun used throughout this listing, the Dataset terminology, and the pricing record itself) — one delivered review assessment (one unique `reviewId`). There is **no start fee**, and every free diagnostic (an invalid row, a source-reported failure, a missing identity, a budget stop, an empty batch) requests no charge event at all.

| Tier | Price per review assessment | Discount |
|---|---|---|
| Free (default) | $0.008 | — |
| Bronze | $0.0072 | 10% |
| Silver | $0.0064 | 20% |
| Gold | $0.0056 | 30% |
| Platinum | $0.0056 | 30% |
| Diamond | $0.0056 | 30% |

These are Apify's platform-wide account pricing tiers — not a volume schedule this Actor invents — assigned automatically by your account's usage; you don't select one. One review assessment is one unique `reviewId`, never billed per duplicate source row.

**A note on the evidence in this README:** every output example below comes from the product owner's own verification runs against this Actor's accepted build — cost to the owner, not sales or customer revenue. Source observation dates shown are the original, retained timestamps from that real snapshot, not a fresh re-scrape performed for this README.

Set the platform's own **Max total charge (USD)** run option to cap spend. If testing the zero-paid path via a raw API call, do not rely on literal `maxTotalChargeUsd: 0` — use a small positive value below every tier's price (e.g. $0.005, below Gold/Platinum/Diamond's $0.0056) instead, and confirm the run's actual effective cap after it starts rather than trusting the requested value silently — see **Evidence and boundaries**.

### Input contract

| Field | Type | Default | Notes |
|---|---|---|---|
| `items` | array | curated public prefill | Inline source rows; overridden by `datasetId`/`runId`/`payload` when set. Max 1000 items. |
| `datasetId` | string (resourcePicker) | — | Existing source Dataset, read with your run's permissions. Overrides inline rows. |
| `runId` | string | — | Alternative to `datasetId`: reads that run's own default Dataset with your run's permissions. |
| `payload` | object | — | Optional `ACTOR.RUN.SUCCEEDED` webhook payload with `resource.defaultDatasetId`/`resource.id`. A bare payload does not grant access. |
| `maxInputRows` | integer | 100 (max 1000) | Source rows examined before grouping; also bounded to 4 MiB total, 64 KiB/row. |
| `maxResults` | integer | 10 (max 1000) | Useful review assessments delivered, independent of `maxTotalChargeUsd`. |
| `observedAt` | string (full ISO timestamp) | empty / unknown | Optional source observation reference used to compute review age; no wall-clock fallback. A date-only value or a timestamp without a timezone is rejected — this field always requires a full timestamp. |
| `staleDaysThreshold` | integer | 7 | Days before an unanswered review adds urgency points; 4x this adds a larger escalation. |

**Source precedence** (never merged): explicit `datasetId`/`payload.resource.defaultDatasetId` > `runId`/`payload.resource.id` > inline `items`. A malformed or conflicting reference is rejected outright, not silently ignored.

Expected source row shape: `reviewId`, `reviewUrl`, `text`/`textTranslated`, `stars`, `publishedAtDate`, `responseFromOwnerText`, `responseFromOwnerDate`, `placeId`/`title`, `originalLanguage`/`translatedLanguage`. A row missing `reviewId` is a free `missing_identity` diagnostic. A `responseFromOwnerText` field that is genuinely absent from the row (never captured by the upstream scraper) is treated as `unknown`, distinct from a field the upstream scraper explicitly recorded as empty/null (an observed unanswered review) — the runtime does not conflate "never captured" with "confirmed empty."

### Output examples

**Example 1 — a clean unanswered review (`final-prefill`, real public snapshot, exact Dataset row).**

```json
{
  "entityId": "Ci9DQUlRQUNvZENodHljRjlvT2trM2RXcG5WVmhoUW1kaVduWlVVSGs1YmpCTE1FRRAB",
  "found": true,
  "status": "unanswered",
  "action": "respond_when_available",
  "confidence": 1,
  "sourceRowCount": 1,
  "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2trM2RXcG5WVmhoUW1kaVduWlVVSGs1YmpCTE1FRRAB",
  "reviewUrl": null,
  "reviewUrlTruncated": false,
  "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
  "placeTitle": "Yellowstone National Park",
  "placeTitleTruncated": false,
  "stars": 5,
  "publishedAtDate": "2026-09-22T22:12:29.184Z",
  "ageDays": null,
  "ageBasis": null,
  "responseStatus": "unanswered",
  "responseFromOwnerDate": null,
  "urgency": "low",
  "urgencyScore": 10,
  "issueKeywordBuckets": [],
  "keywordsSupported": true,
  "originalLanguage": "en",
  "translatedLanguage": null,
  "reviewText": "Really great can't see everything in one visit in a relaxed pace but can see a lot over a few days, recommend that you pull off the road to view wildlife to avoid causing traffic or accidents, also don't bring pets a lot of the places in the park hav",
  "reviewTextTruncated": false,
  "reviewTextTranslated": null,
  "reviewTextTranslatedTruncated": false,
  "responseDateTextConflict": false,
  "duplicateSourceRowCount": 1,
  "evidence": [
    { "field": "responseFromOwnerText", "value": null, "rule": "observed_unanswered: source explicitly recorded no owner response", "sourceRowIndex": 0 },
    { "field": "publishedAtDate", "value": "2026-09-22T22:12:29.184Z", "rule": "age_unknown: publishedAtDate is valid but no observation reference (options.observedAt, row.scrapedAt, or context.observedAt) is available to compute age from", "sourceRowIndex": 0 },
    { "field": "publishedAtDate", "value": null, "rule": "urgency_partial: review age unknown (no valid options.observedAt, row.scrapedAt, or context.observedAt available), urgency score excludes the age component", "sourceRowIndex": 0 }
  ],
  "businessPartial": true,
  "sourceRowIndexes": [0],
  "partial": true,
  "sourceCoverage": { "complete": true, "reason": null, "droppedOversized": 0 },
  "product": "google-reviews-response-backlog",
  "schemaVersion": "1.0",
  "sourceDatasetId": null,
  "sourceRunId": null,
  "suppliedSourceRunId": null,
  "sourceRunIdVerified": null,
  "processedAt": "2026-09-23T03:13:14.633Z"
}
```

`ageDays`/`ageBasis` are genuinely `null` here — no valid `observedAt`/`scrapedAt` was supplied in this run, so age is honestly unknown rather than derived from wall-clock time. `sourceDatasetId`/`sourceRunId` are `null` because this run used inline `items`, not a Dataset/run source.

**Example 2 — a machine-translated review ("partial-columns" scenario), exact Dataset row.** This actor's own `adversarial-provenance.json` states: *"Historical label only. Actual source values retained; explicit found:true/partial:true injected as a controlled incomplete-source flag. This is a derived adversarial scenario, not observed upstream incompleteness or actual column removal."* Every field below is unedited from the actor's own real `partial-columns` receipt — the "partial-columns" label describes the test scenario, not a claim that any column was actually removed from this row:

```json
{
  "entityId": "Ci9DQUlRQUNvZENodHljRjlvT21reGMwbHdTa2M1YVVWclpsUXpibkExVDE5SlltYxAB",
  "found": true,
  "status": "unanswered",
  "action": "respond_when_available",
  "confidence": 1,
  "sourceRowCount": 1,
  "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT21reGMwbHdTa2M1YVVWclpsUXpibkExVDE5SlltYxAB",
  "reviewUrl": null,
  "reviewUrlTruncated": false,
  "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
  "placeTitle": "Yellowstone National Park",
  "placeTitleTruncated": false,
  "stars": 5,
  "publishedAtDate": "2026-09-22T14:36:37.163Z",
  "ageDays": null,
  "ageBasis": null,
  "responseStatus": "unanswered",
  "responseFromOwnerDate": null,
  "urgency": "low",
  "urgencyScore": 10,
  "issueKeywordBuckets": [],
  "keywordsSupported": true,
  "originalLanguage": "es",
  "translatedLanguage": "en",
  "reviewText": "Impresionante!!! Hermoso y unico!! Tiwnes que ir",
  "reviewTextTruncated": false,
  "reviewTextTranslated": "Amazing!!! Beautiful and unique!! You have to go",
  "reviewTextTranslatedTruncated": false,
  "responseDateTextConflict": false,
  "duplicateSourceRowCount": 1,
  "evidence": [
    { "field": "responseFromOwnerText", "value": null, "rule": "observed_unanswered: source explicitly recorded no owner response", "sourceRowIndex": 2 },
    { "field": "publishedAtDate", "value": "2026-09-22T14:36:37.163Z", "rule": "age_unknown: publishedAtDate is valid but no observation reference (options.observedAt, row.scrapedAt, or context.observedAt) is available to compute age from", "sourceRowIndex": 2 },
    { "field": "publishedAtDate", "value": null, "rule": "urgency_partial: review age unknown (no valid options.observedAt, row.scrapedAt, or context.observedAt available), urgency score excludes the age component", "sourceRowIndex": 2 }
  ],
  "businessPartial": true,
  "sourceRowIndexes": [2],
  "partial": true,
  "sourceCoverage": { "complete": true, "reason": null, "droppedOversized": 0 },
  "product": "google-reviews-response-backlog",
  "schemaVersion": "1.0",
  "sourceDatasetId": null,
  "sourceRunId": null,
  "suppliedSourceRunId": null,
  "sourceRunIdVerified": null,
  "processedAt": "2026-09-23T03:13:27.014Z"
}
```

`keywordsSupported: true` here because `translatedLanguage: "en"` and `textTranslated` were both present — keyword matching ran against the English translation, never the Spanish original. `found`/`partial` are both already genuinely `true` from ordinary business logic (an unanswered review with an unknown age is a real partial result) — the adversarial scenario's own injection makes no visible difference on this particular row, which is exactly why the provenance note insists on calling it a historical label rather than an actual incompleteness signal.

**Example 3 — below-price boundary run, exact free diagnostic row (real settled receipt).** With `maxTotalChargeUsd` set to $0.005 (below every tier's $0.0056+ price), the run reads no source at all:

```json
{
  "product": "google-reviews-response-backlog",
  "schemaVersion": "1.0",
  "status": "budget_stopped",
  "reason": "Run charge limit cannot cover any result; no source was read.",
  "found": false,
  "processedAt": "2026-09-23T03:13:40.264Z"
}
```

**Example 4 — empty inline source, exact free diagnostic row (real settled receipt).**

```json
{
  "product": "google-reviews-response-backlog",
  "schemaVersion": "1.0",
  "status": "no_results",
  "found": false,
  "reason": "no_usable_rows_in_batch",
  "sourceCoverage": { "complete": true, "reason": null, "droppedOversized": 0 },
  "processedAt": "2026-09-23T03:13:49.519Z"
}
```

### Field dictionary

**Result fields:** `entityId`/`reviewId` (canonical, trimmed ID); `status`/`responseStatus` (`answered`/`unanswered`/`unknown`); `urgency`/`urgencyScore` (`resolved`/`urgent`(≥50)/`moderate`(≥20)/`low`, additive score or `null` when unknown); `issueKeywordBuckets` (subset of `refund`/`rude_staff`/`cleanliness`/`wait_time`/`safety`/`billing`/`quality`/`cancellation`); `keywordsSupported` (`false` when the language couldn't be confirmed English/English-translated); `reviewText`/`reviewTextTranslated` + `reviewTextTruncated`/`reviewTextTranslatedTruncated` (1000-char cap each); `reviewUrl` + `reviewUrlTruncated` (2048-char cap); `placeTitle` + `placeTitleTruncated` (500-char cap); `placeId` (type-validated, nulled if malformed); `originalLanguage`/`translatedLanguage` (35-char cap, nulled if malformed); `publishedAtDate`/`responseFromOwnerDate` (strict calendar-valid ISO-8601, date-only or full timestamp, nulled if malformed — never truncated into a fake date); `ageDays`/`ageBasis` (`null` unless a valid full-timestamp observation reference exists); `responseDateTextConflict` (see below); `duplicateSourceRowCount`/`sourceRowCount`/`sourceRowIndexes`; `confidence`; `partial`; `evidence` (up to 50 entries).

**`responseDateTextConflict`:** `true` when a valid `responseFromOwnerDate` is present alongside a blank/null **or** genuinely absent `responseFromOwnerText`, checked across *every* duplicate row sharing a `reviewId` — an older duplicate's contradiction is never masked by a newer, clean-looking one. An explicit blank/null text always reaches this check, since it already makes the row useful on its own. A genuinely absent text reaches it only when the row cleared the usefulness gate some other way — a supported keyword match on the review text; absent text with no keyword match fails the usefulness gate first and becomes a free `insufficient_review_evidence` diagnostic instead, never reaching the conflict check. When the conflict does apply, it forcibly reclassifies `responseStatus`/`status`/`urgency`/`urgencyScore`/`statusBreakdown`/`urgencyBreakdown` all to `unknown`, sets `partial:true` and `action:"manual_verification_needed"`, and remains a **paid**, evidenced result.

**Free Dataset diagnostic states:** `invalid_row` (unusable row shape); `source_reported_failure` (a hard-failure signal — `found:false`, `failed:true`, or a status like `failed`/`error`/`timed-out`/`aborted` — always wins over a contradictory `found:true`/`partial:true` claim on the same row); `missing_identity` (no usable `reviewId`); `invalid_identity_type`; `insufficient_source_data`; `insufficient_review_evidence` (uniquely also carries `responseStatus`/`stars`/`publishedAtDate`/`evidence` alongside the diagnostic); `invalid_domain_options`; run-level `budget_stopped`/`no_results`/`max_results_reached`. A non-empty `error`/`errors` field with no hard-failure signal present is retained as a harmless warning ONLY when the same row explicitly carries both `found:true` and `partial:true`; in every other case it produces the free `source_reported_failure` diagnostic.

**Run records (`OUTPUT` and `SUMMARY`)** contain the same summary object. Their counters describe confirmed delivery, which can differ from raw Dataset rows after an ambiguous write or charge.

| Field | Meaning |
|---|---|
| `schemaVersion` / `product` | Output contract version and this product's slug. |
| `outcome` | `delivered`, `budget_stopped`, `max_results_reached`, `no_results`, `diagnostic`, or `failed`. |
| `errorCode` / `fatalFailure` | Machine-readable failure code or null, and whether the run ended in a fatal failure. |
| `requested` | Source rows counted within the read bound, including encountered oversized rows; not the requested useful-result limit. |
| `usefulResults` | Useful transform results after per-row oversized-result (size) filtering, counted before the later result-limit/budget delivery cap. |
| `delivered` / `paid` | Useful rows whose Dataset write and result event are confirmed. |
| `free` | Confirmed free diagnostic writes; this can exceed the initial 200-diagnostic limit. |
| `failed` | Useful results dropped because their serialized row exceeded 64 KiB. |
| `deliveryUncertain` | A Dataset write or charge acknowledgment is uncertain, including `FREE_WRITE_FAILED` and `DELIVERY_UNKNOWN`; inspect raw rows and counters before retrying. |
| `replayBlocked` | This run's prior ledger claim prevented replay; this does not deduplicate a new run. |
| `sourcePartial` / `sourcePartialReason` | Whether source reading was incomplete and why: `max_input_rows_reached`, `total_byte_cap_reached`, `oversized_rows_dropped`, `pagination_interrupted`, `invalid_json_page`, `unexpected_shape`, or `unexpected_page_size`; otherwise null reason. |
| `sourceDroppedOversized` | Source rows skipped for exceeding the 64 KiB row bound. |
| `budgetStopped` / `resultsCapped` | Delivery stopped at the charge limit or useful-result count limit. |
| `businessPartialResults` | All partial transform results before delivery, size, budget or result-limit filtering; not a delivered count. |
| `oversizedDiagnosticsDropped` | Diagnostics that could not be written within the row-size bound. |
| `diagnosticsTruncated` | The initial combined business/source-notice list exceeded 200 and was sliced; later notices are separate. |
| `partialOverall` | Overall incompleteness from source, business results, dropped rows/diagnostics, diagnostic truncation, budget stopping or result-count capping. |
| `budgetMode` / `maxTotalChargeUsd` | Verified charging mode and effective charge cap; these are run-level accounting state, not source metrics. |
| `replaySafety` | The explicit warning that a new run is new processing and may charge again. |
| `processedAt` | This Actor's execution timestamp, not the upstream observation time or review publication date. |
| `businessSummary` | Product summary: `totalSourceRows`, `resultsCount`, business-only `diagnosticsCount`, `statusBreakdown`, `urgencyBreakdown`, `issueBuckets`, and optional observation reference `observedAt`. |

Additional free Dataset states are `invalid_input`, `source_unavailable`, `partial_source`, `source_rows_dropped`, and `result_dropped_oversized`. `reason` and `error` explain the condition when present; `count` reports dropped rows/results on the relevant notice; `mode` appears only on the source-resolution `invalid_input`/`source_unavailable` diagnostic and describes which source-resolution mode (`dataset`/`inline`/`run`/`null`) was attempted — never a budget mode, which is the separate run-level `OUTPUT.budgetMode`; `delivered` and `totalUseful` describe confirmed delivery versus available useful results on a stopping notice. `sourceCoverage` reports source `complete`, `reason`, and `droppedOversized`. Decorated rows also carry `product`, `schemaVersion`, source Dataset/run IDs, source row references, `businessPartial`, unified `partial`, and `processedAt`; early run-level diagnostics need not carry all decorated product fields. The overview's `observedAt` capture-time column may be blank when the source capture is unknown. Review event dates remain separate. The shared runtime deliberately supplies no processing-time fallback as `context.observedAt`.

### Evidence and boundaries

**No blanket 200-free-row cap, and `businessSummary` does not count every notice.** The initial diagnostics array the shared runtime writes consists of this product's own `report.diagnostics` **plus** the runtime's own `partial_source`/`source_rows_dropped` notices, combined and then sliced to a 200-row write cap. `businessSummary.diagnosticsCount` counts business-only diagnostics — uncapped, so it can exceed 200. `diagnosticsTruncated` covers that combined (business + runtime-notice) list, not the business-only count. Separately, later notices — `result_dropped_oversized`, `budget_stopped`, `max_results_reached` — are written **on top of** the 200-row cap and are not counted against it. This actor's own `mixed-boundary` receipt (995 synthetic-failure rows ahead of 5 real rows, `maxResults:1`) confirms it exactly: `businessSummary.diagnosticsCount: 995` (uncapped business count), 200 `source_reported_failure` rows actually written to the free cap, plus **1** additional `max_results_reached` notice on top — **201 total free rows**, `diagnosticsTruncated: true`.

**Hard-failure priority.** `isHardFailure` always overrides a contradictory `found:true`/`partial:true` claim on the same row — a source cannot simultaneously report "this row failed" and "this row is usable." `isSourceFailure` with no hard-failure signal is a harmless warning only when the row explicitly carries both `found:true` and `partial:true`; otherwise it is the free `source_reported_failure` diagnostic.

**Zero-charge cap normalization.** A raw API `maxTotalChargeUsd: 0` is normalized per a recorded observation — a positive value below every tier's price (Example 3 above used $0.005) reliably guarantees zero paid results, but the run's *effective* enforced cap should be inspected after the run starts, not simply trusted from the request.

**Billing sequencing.** Dataset-write happens **before** charge confirmation and is non-transactional. An ambiguous outcome finalizes the run as `FAILED` with no automatic retry and a possible unconfirmed row. There is no cross-run exactly-once delivery guarantee — a new run is new processing and can bill again.

**Date semantics.** `publishedAtDate`/`responseFromOwnerDate` are the review's own event/content timestamps — date-only is accepted, fixed at UTC midnight internally for age math, never reformatted in output. `options.observedAt`/`row.scrapedAt`/`context.observedAt` are the run's observation-time references and always require a **full** timestamp — a bare date is rejected there. When no valid reference exists at all, age is genuinely unknown (`ageDays: null`), never fabricated from code-execution wall-clock time.

**Language gate.** Keyword matching runs only against text confirmed `originalLanguage` en/en-\* or confirmed `translatedLanguage` en/en-\* with `textTranslated` present. An absent/unrecognized language is never assumed English.

**Exact numeric and length caps, as coded, not paraphrased:** `MAX_EVIDENCE = 50` entries per result; `MAX_TEXT_LENGTH = 1000` characters (`reviewText`/`reviewTextTranslated`); `MAX_IDENTITY_LENGTH = 500` characters (`reviewId`, `placeId` where applicable); `MAX_URL_LENGTH = 2048` characters (`reviewUrl`); `MAX_TITLE_LENGTH = 500` characters (`placeTitle`); `MAX_DATE_LENGTH = 64` characters (`publishedAtDate`, `responseFromOwnerDate`); `MAX_LANGUAGE_TAG_LENGTH = 35` characters (`originalLanguage`, `translatedLanguage`). Shared runtime source-read bounds: at most 1000 rows, 4 MiB total source JSON, 64 KiB per row. A value exceeding its cap is truncated (text fields, with the matching `...Truncated` flag set) or nulled (identity/date/language fields, since a truncated ID or date would be a fabricated value, not a shortened real one) rather than silently dropped or echoed verbatim.

### Decision routing

- `responseStatus: "unanswered"` + `urgency: "urgent"` → route to immediate response queue.
- `responseStatus: "unknown"` (including a `responseDateTextConflict` reclassification) → route to manual verification, never to an automated "already answered" bucket.
- `keywordsSupported: false` → exclude from keyword-driven issue reporting; still valid for status/urgency triage.
- Free diagnostics (`missing_identity`, `invalid_row`, etc.) → route to a data-quality backlog for the upstream scraper, not the response queue.
- `issueKeywordBuckets` containing `refund` or `billing` → route to finance/ops alongside the standard response queue, since these categories often need a decision beyond a customer-service reply.
- `duplicateSourceRowCount > 1` with conflicting per-occurrence evidence → treat the delivered assessment as the audited, cross-checked answer; don't re-derive status from a single duplicate row picked at random.

### Commercial playbooks

- **Weekly response-backlog digest.** Run against each location's latest reviews export; sort delivered rows by `urgencyScore` descending for a Monday-morning action list.
- **Issue-keyword rollup.** Aggregate `businessSummary.issueBuckets` across a multi-location batch to spot a chain-wide refund or cleanliness spike before it shows up in survey data.
- **Data-quality audit for a reviews vendor.** Use `responseDateTextConflict` and `invalid_row`/`missing_identity` diagnostic rates as an objective signal of how clean a vendor's export actually is.
- **SLA-conformance reporting.** Compare `urgencyBreakdown` week over week after tuning `staleDaysThreshold` to your actual team response commitment, rather than relying on a manager's spot check of a handful of locations.
- **Franchise scorecards.** Run the same `staleDaysThreshold` across every location in a group and rank locations by their `unanswered`/`urgent` share — a consistent, disclosed rule applied uniformly, not a subjective per-manager read of "how bad" a location's reviews look.

### Integration recipes

![Google Reviews Response Backlog Builder — workflow diagram](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/d94b27ab62fe5f65d0258277507ea8d341761c8d/value-add-20260923/google-reviews-response-backlog/readme-workflow.webp)

This Actor's ID is **`WgO1bFVK3eBJuKd7C`**. A natural upstream source is [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper) (Actor ID `Xb8osYTtOjlsgI6k9`); [Google Maps Places Scraper](https://apify.com/compass/crawler-google-places) (`compass~crawler-google-places`) provides separate place-level context; its place rows are not interchangeable with review rows. The recipes below describe how to configure each mechanism — not a claim that any specific trigger is currently installed.

**1. Console-native chaining (no code).** On your reviews-scraper run page, open **Integrations**, add an "Actor/task run" action targeting this Actor, and let the Integrations UI populate the payload with `{{resource.defaultDatasetId}}`. Map that value explicitly into this Actor's `datasetId` field — not the generic `payload` pass-through alone — to request READ access under LIMITED\_PERMISSIONS; the caller must already be authorized, and a private source can still deny access.

**2. Direct webhook (`POST /v2/webhooks`), full recipe.** Configure a webhook on your upstream reviews-scraper Actor for the `ACTOR.RUN.SUCCEEDED` event, scoped via `condition.actorId`, targeting this Actor's run-creation endpoint:

```json
{
  "isApifyIntegration": true,
  "shouldInterpolateStrings": true,
  "eventTypes": ["ACTOR.RUN.SUCCEEDED"],
  "condition": { "actorId": "Xb8osYTtOjlsgI6k9" },
  "requestUrl": "https://api.apify.com/v2/acts/WgO1bFVK3eBJuKd7C/runs?maxTotalChargeUsd=0.50",
  "doNotRetry": true,
  "headersTemplate": "{\"Authorization\": \"Bearer <APIFY_TOKEN>\"}",
  "payloadTemplate": "{\"datasetId\": \"{{resource.defaultDatasetId}}\", \"payload\": {\"resource\": {\"defaultDatasetId\": \"{{resource.defaultDatasetId}}\", \"id\": \"{{resource.id}}\"}}, \"staleDaysThreshold\": 7}"
}
```

Submit this to `POST https://api.apify.com/v2/webhooks` with your own `Authorization: Bearer <APIFY_TOKEN>` header. The `<APIFY_TOKEN>` placeholder is never a literal token embedded in the template — it is filled in from your own secrets-provisioning store at the moment the webhook is registered, not an implicit platform template variable that resolves automatically. `doNotRetry: true` avoids a duplicate-charge risk from an automatic retry. `datasetId` is set explicitly — the resource picker or an authorized Integrations mapping can only **request** READ access; it never **guarantees** the platform actually grants it. A positive `maxTotalChargeUsd` on the request URL caps this chained run's spend; avoid the literal value `0` (see **Evidence and boundaries**).

**3. Generic automation tools (n8n, Zapier, Make, or your own tooling).** Use a generic Apify/HTTP node to call `run-sync-get-dataset-items` on this Actor's ID with a JSON body containing `datasetId` and your chosen `staleDaysThreshold`/`maxResults` — a plain authenticated HTTPS POST, no native Apify-integration support required. The same `isApifyIntegration`/`payloadTemplate` shape from recipe 2 applies to any integration definition your own tooling submits.

**4. Export / helpdesk-adjacent mapping.** Every run's Dataset supports CSV/Excel/JSON export via the Console or `GET /v2/datasets/{datasetId}/items?format=csv`. `reviewId`, `placeTitle`, `status`, `urgency`, `urgencyScore`, and `issueKeywordBuckets` alone drive a working response queue in a spreadsheet or helpdesk import.

### Operating guide

1. **Confirm your source batch shape first.** Spot-check rows against **Input contract** — each needs a usable `reviewId`, or you'll mostly get `missing_identity` diagnostics.
2. **Set `staleDaysThreshold` to your real SLA**, not the default — it directly changes which reviews score `urgent`.
3. **Start with a small `maxResults` on a new source** to confirm output matches expectations before a full batch.
4. **Set `maxTotalChargeUsd` via the platform run option, and check its effective value after the run starts.** For a guaranteed-zero-paid API call, use a small positive value below $0.0056, not literal `0` — see **Evidence and boundaries**.
5. **Read the `OUTPUT`/`SUMMARY` record, not just the Dataset row count** — it reports `delivered`/`paid`/`free`/`failed`, whether `budgetStopped`/`resultsCapped` ended the run early, and `diagnosticsTruncated`.
6. **If a run finalizes `FAILED`, inspect its Dataset and `OUTPUT` record before retrying** — a blind retry can duplicate already-successful paid assessments.
7. **Re-running is always new billing** — no cross-run dedup key. Reserve it for a new or updated batch, not a routine safety retry.

### FAQ

**Does this Actor scrape Google Maps itself?** No. It only reads an existing reviews batch you or an upstream Actor already collected — no open-web fetch, no upstream Actor started.

**Does it read reviews with AI or judge sentiment?** No. Matching is literal, disclosed keyword-substring matching against eight fixed buckets on text confirmed English or confirmed English-translated — no negation handling, no sentiment score, no broad language coverage.

**Does it send replies to reviewers?** No. It produces a prioritized `action` recommendation (e.g. `respond_when_available`, `manual_verification_needed`) — it never contacts Google Maps or sends anything on your behalf.

**What does a `FAILED` run mean?** The runtime could not safely finish: source unavailability, algorithm/contract failure, invalid pricing or charge-limit state, a ledger/replay failure, an uncertain Dataset write or charge acknowledgment, free-diagnostic write failure, or failure to persist summaries can end the run. Dataset writing precedes charging; these are separate operations. A raw row can exist without a confirmed paid delivery. Inspect the Dataset, run event counters, and available `OUTPUT`/`SUMMARY` records before retrying; missing summaries are themselves a reason to reconcile. There is no automatic retry or promise that a failed run delivered nothing.

**Is `age`/`ageDays` ever guessed from when the run happened to execute?** No. Age is computed only from a valid full-timestamp `options.observedAt`/`row.scrapedAt`/`context.observedAt`; without one, `ageDays` is `null` — genuinely unknown, never backfilled from wall-clock time.

**Can I pass a bare date like `2026-09-23` for `observedAt`?** No — `observedAt` is a reference/meta timestamp and always requires a full ISO timestamp with a timezone (`Z` or an offset). A date-only value or a timestamp missing its timezone is rejected. Bare dates are only accepted for the review's own `publishedAtDate`/`responseFromOwnerDate` content fields.

**What happens if the same review appears twice in my source batch?** Every duplicate occurrence sharing a `reviewId` is counted in `duplicateSourceRowCount` and audited together — including for `responseDateTextConflict`, so an older duplicate carrying a contradiction is never masked by a newer, clean-looking copy. Only one assessment is delivered and billed per unique `reviewId`.

**Does a non-empty `error` field on a source row always mean the row is dropped as a free diagnostic?** Not always. Only an explicit hard-failure signal (`found:false`, `failed:true`, or a recognized failure-status alias) always forces `source_reported_failure`. Absent that, a stray `error`/`errors` value is kept as a harmless warning ONLY when the row explicitly carries both `found:true` and `partial:true` — any other combination still produces the free `source_reported_failure` diagnostic.

**Is the "partial-columns" style example a demonstration of missing upstream data?** No — see Output example 2. Same real values as Example 1's source snapshot; only `partial`/`found` are deliberately set as a controlled test, not a simulation of real column removal.

**Why did one run report 201 free rows instead of the usual 200-row diagnostic cap?** The initial business-diagnostic list is capped at 200 entries when written, but a later result-size/cap notice like `max_results_reached` is added on top and not counted against that cap — see **Evidence and boundaries** for this actor's own confirmed 995/200/201 numbers.

### Sources and rights

Public source example: `TInMu4Nq0if27nzls` — a curated, non-sensitive snapshot of a real upstream reviews run, with original timestamps and provenance retained; reviewer names/profile IDs/photos were never captured in the example and are never emitted by this Actor's own output regardless of source content. All output examples and canary numbers in this README come from the product owner's own verification runs — cost to the owner, not customer sales or revenue.

#### Related tools

Related tools for adjacent workflows in B2B lead generation and data enrichment.

| Actor | What it does |
|---|---|
| [Structured Data Extractor](https://apify.com/zinin/structured-extract) | Pair it in the B2B lead generation and data enrichment workflow: Extract title, meta, JSON-LD, headings, links, emails, and price observations from up to 100 authorized... |
| [URL to Markdown Converter](https://apify.com/zinin/url-to-markdown) | Pair it in the B2B lead generation and data enrichment workflow: Convert up to 100 authorized public HTML pages into clean Markdown for RAG, AI agents, SEO research, and... |
| [Website Contact Scraper — Public Emails & Phones](https://apify.com/zinin/website-contact-extractor) | Pair it in the B2B lead generation and data enrichment workflow: Find public company-site emails, phones, and social profiles with page-level evidence, best-contact... |

| [Google Maps Scraper](https://apify.com/compass/crawler-google-places) | Collect separate place-level context; map place identities to review rows before analysis. |
| [Google Maps Reviews Scraper](https://apify.com/compass/google-maps-reviews-scraper) | Collect the upstream reviews dataset; verify its fields against the input contract before chaining. |

# Actor input Schema

## `items` (type: `array`):

Small real public example: https://api.apify.com/v2/datasets/TInMu4Nq0if27nzls/items?clean=true . This saved snapshot is not a new scrape.

## `datasetId` (type: `string`):

Read an existing source dataset with your run permissions. Overrides inline items. The resource picker can request scoped READ permission. Integration mapping must use an authorized resource; a plain Dataset ID or webhook payload alone does not grant access. Public example: TInMu4Nq0if27nzls.

## `runId` (type: `string`):

Optional Actor run ID to read instead of Source Dataset. Its default Dataset is resolved with your run's own permissions — no elevated access is used, and no upstream Actor is started.

## `maxInputRows` (type: `integer`):

Maximum source records examined before grouping. At most 1000 rows, 4 MiB total and 64 KiB per row. Capped or incomplete source coverage is reported.

## `maxResults` (type: `integer`):

Maximum useful review assessment results delivered, independently of the platform Max total charge setting. Free diagnostics are reported separately.

## `payload` (type: `object`):

Optional ACTOR.RUN.SUCCEEDED event payload with resource.defaultDatasetId and resource.id. Dataset resourcePicker is recommended for private datasets; a payload alone does not grant access.

## `observedAt` (type: `string`):

Optional user-supplied snapshot reference as a full ISO timestamp with Z or an offset, for example 2026-09-23T00:00:00Z. A date alone or timestamp without timezone is rejected. If omitted, a valid source row scrapedAt can supply the age reference; otherwise age stays unknown. Execution time is never substituted, and this Actor does not independently verify the supplied capture instant.

## `staleDaysThreshold` (type: `integer`):

An unanswered review older than this many days adds urgency points; older than 4x this threshold adds a larger escalation. Adjust to match your team's response SLA.

## Actor input object example

```json
{
  "items": [
    {
      "text": "Really great can't see everything in one visit in a relaxed pace but can see a lot over a few days, recommend that you pull off the road to view wildlife to avoid causing traffic or accidents, also don't bring pets a lot of the places in the park hav",
      "textTranslated": null,
      "publishAt": "55 minutes ago",
      "publishedAtDate": "2026-09-22T22:12:29.184Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2trM2RXcG5WVmhoUW1kaVduWlVVSGs1YmpCTE1FRRAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": "en",
      "translatedLanguage": null,
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": null,
      "textTranslated": null,
      "publishAt": "an hour ago",
      "publishedAtDate": "2026-09-22T21:35:36.564Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT25CNFJFMVBjbWhXUlY5RFNsVk9RMHN6VDFFNVYzYxAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": null,
      "translatedLanguage": null,
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": "Impresionante!!! Hermoso y unico!! Tiwnes que ir",
      "textTranslated": "Amazing!!! Beautiful and unique!! You have to go",
      "publishAt": "8 hours ago",
      "publishedAtDate": "2026-09-22T14:36:37.163Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT21reGMwbHdTa2M1YVVWclpsUXpibkExVDE5SlltYxAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": "es",
      "translatedLanguage": "en",
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": "MAJU",
      "textTranslated": "PROCEED",
      "publishAt": "9 hours ago",
      "publishedAtDate": "2026-09-22T13:52:49.980Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2sxbGRVaEdTSEJ6VW1OaE1WOWpOVGxCVnkxNk5sRRAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": "id",
      "translatedLanguage": "en",
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": null,
      "textTranslated": null,
      "publishAt": "16 hours ago",
      "publishedAtDate": "2026-09-22T06:47:27.869Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2s4eGNYZDBZbGhpWTFrd09WRmxhMlJTUTBFNFIwRRAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": null,
      "translatedLanguage": null,
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    }
  ],
  "maxInputRows": 100,
  "maxResults": 10,
  "staleDaysThreshold": 7
}
```

# Actor output Schema

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

API URL for the default dataset items produced by this run.

## `summary` (type: `string`):

API URL for the OUTPUT record 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 = {
    "items": [
        {
            "text": "Really great can't see everything in one visit in a relaxed pace but can see a lot over a few days, recommend that you pull off the road to view wildlife to avoid causing traffic or accidents, also don't bring pets a lot of the places in the park hav",
            "textTranslated": null,
            "publishAt": "55 minutes ago",
            "publishedAtDate": "2026-09-22T22:12:29.184Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2trM2RXcG5WVmhoUW1kaVduWlVVSGs1YmpCTE1FRRAB",
            "reviewUrl": null,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": null,
            "responseFromOwnerDate": null,
            "responseFromOwnerText": null,
            "originalLanguage": "en",
            "translatedLanguage": null,
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": true,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
            }
        },
        {
            "text": null,
            "textTranslated": null,
            "publishAt": "an hour ago",
            "publishedAtDate": "2026-09-22T21:35:36.564Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT25CNFJFMVBjbWhXUlY5RFNsVk9RMHN6VDFFNVYzYxAB",
            "reviewUrl": null,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": null,
            "responseFromOwnerDate": null,
            "responseFromOwnerText": null,
            "originalLanguage": null,
            "translatedLanguage": null,
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": true,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
            }
        },
        {
            "text": "Impresionante!!! Hermoso y unico!! Tiwnes que ir",
            "textTranslated": "Amazing!!! Beautiful and unique!! You have to go",
            "publishAt": "8 hours ago",
            "publishedAtDate": "2026-09-22T14:36:37.163Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT21reGMwbHdTa2M1YVVWclpsUXpibkExVDE5SlltYxAB",
            "reviewUrl": null,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": null,
            "responseFromOwnerDate": null,
            "responseFromOwnerText": null,
            "originalLanguage": "es",
            "translatedLanguage": "en",
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": true,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
            }
        },
        {
            "text": "MAJU",
            "textTranslated": "PROCEED",
            "publishAt": "9 hours ago",
            "publishedAtDate": "2026-09-22T13:52:49.980Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2sxbGRVaEdTSEJ6VW1OaE1WOWpOVGxCVnkxNk5sRRAB",
            "reviewUrl": null,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": null,
            "responseFromOwnerDate": null,
            "responseFromOwnerText": null,
            "originalLanguage": "id",
            "translatedLanguage": "en",
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": true,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
            }
        },
        {
            "text": null,
            "textTranslated": null,
            "publishAt": "16 hours ago",
            "publishedAtDate": "2026-09-22T06:47:27.869Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2s4eGNYZDBZbGhpWTFrd09WRmxhMlJTUTBFNFIwRRAB",
            "reviewUrl": null,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": null,
            "responseFromOwnerDate": null,
            "responseFromOwnerText": null,
            "originalLanguage": null,
            "translatedLanguage": null,
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": true,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
            }
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/google-reviews-response-backlog").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 = { "items": [
        {
            "text": "Really great can't see everything in one visit in a relaxed pace but can see a lot over a few days, recommend that you pull off the road to view wildlife to avoid causing traffic or accidents, also don't bring pets a lot of the places in the park hav",
            "textTranslated": None,
            "publishAt": "55 minutes ago",
            "publishedAtDate": "2026-09-22T22:12:29.184Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2trM2RXcG5WVmhoUW1kaVduWlVVSGs1YmpCTE1FRRAB",
            "reviewUrl": None,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": None,
            "responseFromOwnerDate": None,
            "responseFromOwnerText": None,
            "originalLanguage": "en",
            "translatedLanguage": None,
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": True,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities.",
            },
        },
        {
            "text": None,
            "textTranslated": None,
            "publishAt": "an hour ago",
            "publishedAtDate": "2026-09-22T21:35:36.564Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT25CNFJFMVBjbWhXUlY5RFNsVk9RMHN6VDFFNVYzYxAB",
            "reviewUrl": None,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": None,
            "responseFromOwnerDate": None,
            "responseFromOwnerText": None,
            "originalLanguage": None,
            "translatedLanguage": None,
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": True,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities.",
            },
        },
        {
            "text": "Impresionante!!! Hermoso y unico!! Tiwnes que ir",
            "textTranslated": "Amazing!!! Beautiful and unique!! You have to go",
            "publishAt": "8 hours ago",
            "publishedAtDate": "2026-09-22T14:36:37.163Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT21reGMwbHdTa2M1YVVWclpsUXpibkExVDE5SlltYxAB",
            "reviewUrl": None,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": None,
            "responseFromOwnerDate": None,
            "responseFromOwnerText": None,
            "originalLanguage": "es",
            "translatedLanguage": "en",
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": True,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities.",
            },
        },
        {
            "text": "MAJU",
            "textTranslated": "PROCEED",
            "publishAt": "9 hours ago",
            "publishedAtDate": "2026-09-22T13:52:49.980Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2sxbGRVaEdTSEJ6VW1OaE1WOWpOVGxCVnkxNk5sRRAB",
            "reviewUrl": None,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": None,
            "responseFromOwnerDate": None,
            "responseFromOwnerText": None,
            "originalLanguage": "id",
            "translatedLanguage": "en",
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": True,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities.",
            },
        },
        {
            "text": None,
            "textTranslated": None,
            "publishAt": "16 hours ago",
            "publishedAtDate": "2026-09-22T06:47:27.869Z",
            "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2s4eGNYZDBZbGhpWTFrd09WRmxhMlJTUTBFNFIwRRAB",
            "reviewUrl": None,
            "reviewOrigin": "Google",
            "stars": 5,
            "rating": None,
            "responseFromOwnerDate": None,
            "responseFromOwnerText": None,
            "originalLanguage": None,
            "translatedLanguage": None,
            "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "title": "Yellowstone National Park",
            "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
            "_exampleProvenance": {
                "sourceActorId": "Xb8osYTtOjlsgI6k9",
                "sourceRunId": "JKEBQz9m3B6cmfOCu",
                "sourceDatasetId": "D1fpb3EAjsRD4E20w",
                "curatedPublicSnapshot": True,
                "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities.",
            },
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("zinin/google-reviews-response-backlog").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 '{
  "items": [
    {
      "text": "Really great can'\''t see everything in one visit in a relaxed pace but can see a lot over a few days, recommend that you pull off the road to view wildlife to avoid causing traffic or accidents, also don'\''t bring pets a lot of the places in the park hav",
      "textTranslated": null,
      "publishAt": "55 minutes ago",
      "publishedAtDate": "2026-09-22T22:12:29.184Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2trM2RXcG5WVmhoUW1kaVduWlVVSGs1YmpCTE1FRRAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": "en",
      "translatedLanguage": null,
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": null,
      "textTranslated": null,
      "publishAt": "an hour ago",
      "publishedAtDate": "2026-09-22T21:35:36.564Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT25CNFJFMVBjbWhXUlY5RFNsVk9RMHN6VDFFNVYzYxAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": null,
      "translatedLanguage": null,
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": "Impresionante!!! Hermoso y unico!! Tiwnes que ir",
      "textTranslated": "Amazing!!! Beautiful and unique!! You have to go",
      "publishAt": "8 hours ago",
      "publishedAtDate": "2026-09-22T14:36:37.163Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT21reGMwbHdTa2M1YVVWclpsUXpibkExVDE5SlltYxAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": "es",
      "translatedLanguage": "en",
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": "MAJU",
      "textTranslated": "PROCEED",
      "publishAt": "9 hours ago",
      "publishedAtDate": "2026-09-22T13:52:49.980Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2sxbGRVaEdTSEJ6VW1OaE1WOWpOVGxCVnkxNk5sRRAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": "id",
      "translatedLanguage": "en",
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    },
    {
      "text": null,
      "textTranslated": null,
      "publishAt": "16 hours ago",
      "publishedAtDate": "2026-09-22T06:47:27.869Z",
      "reviewId": "Ci9DQUlRQUNvZENodHljRjlvT2s4eGNYZDBZbGhpWTFrd09WRmxhMlJTUTBFNFIwRRAB",
      "reviewUrl": null,
      "reviewOrigin": "Google",
      "stars": 5,
      "rating": null,
      "responseFromOwnerDate": null,
      "responseFromOwnerText": null,
      "originalLanguage": null,
      "translatedLanguage": null,
      "placeId": "ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "title": "Yellowstone National Park",
      "url": "https://www.google.com/maps/search/?api=1&query=Yellowstone%20National%20Park&query_place_id=ChIJVVVVVVXlUVMRu-GPNDD5qKw",
      "_exampleProvenance": {
        "sourceActorId": "Xb8osYTtOjlsgI6k9",
        "sourceRunId": "JKEBQz9m3B6cmfOCu",
        "sourceDatasetId": "D1fpb3EAjsRD4E20w",
        "curatedPublicSnapshot": true,
        "redaction": "Allowlisted public business/product fields only; review excerpts and social captions truncated; no reviewer identities."
      }
    }
  ]
}' |
apify call zinin/google-reviews-response-backlog --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/google-reviews-response-backlog"
        }
    }
}
```

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/WgO1bFVK3eBJuKd7C/builds/0Dhtdw9HCZVrhNh5e/openapi.json
