# Google Maps Reviews Scraper | Filtered Rows (`danthedataman/google-maps-review-rows`) Actor

Export individual Google Maps reviews from place IDs, URLs or searches. Filter stars, dates and text; inspect per-place coverage and available owner replies.

- **URL**: https://apify.com/danthedataman/google-maps-review-rows.md
- **Developed by:** [Eli J](https://apify.com/danthedataman) (community)
- **Categories:** Lead generation, SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 95.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.03 / 1,000 results

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

### What you can export

Use this **Google Maps reviews scraper** to export individual review rows from place URLs, IDs or ranked business searches. Filter by stars, activity-date window and text, and choose the supported review order.

Read the final `COVERAGE` record for each place. Activity dates can reflect edits; original and absolute reply dates are available only on successfully enriched owner-reply rows.

### Try the example input

Use the Console prefill or supply your own place identifiers. Empty input `{}` starts no source work, exits successfully and logs guidance.

```json
{
  "placeIds": [
    "ChIJ0SnrKjO0RIYRCg9HH9d0gqE"
  ],
  "maxReviews": 50,
  "reviewsSort": "lowestRanking"
}
```

For a lowest-rated date window, scan newest activity first, then sort the matching reviews. This avoids dropping the date boundary when the requested order is lowest-rated.

```json
{
  "searchStringsArray": ["auto repair"],
  "locationQueries": ["Austin, Texas, USA"],
  "maxPlacesPerSearch": 3,
  "reviewsStartDate": "2026-09-01",
  "reviewsEndDate": "2026-10-06T23:59:59.999Z",
  "filterStars": [1, 2],
  "reviewsSort": "lowestRanking",
  "maxReviews": 0,
  "maxReviewPages": 20,
  "maxRunSeconds": 180
}
```

URLs and CIDs:

```json
{
  "startUrls": [{"url": "https://www.google.com/maps?cid=11637992854370324234"}],
  "placeIds": ["0x8644b4332aeb29d1:0xa18274d71f470f0a"],
  "maxReviews": 25,
  "personalData": false
}
```

Both inputs in this example identify the same place; it is scraped once.

All supplied source lists are combined; place IDs are deduplicated within the run. Search locations form a Cartesian product with terms. Google Maps search URLs and share/short links are resolved through anonymous HTTP redirects. A Place ID must resolve to its own business; a similarly named result is never substituted. A place URL with no resolvable identifier is sent to ERRORS; only search URLs expand into ranked searches.

### Python API

Install `apify-client==3.2.1`, set `APIFY_API_KEY`, and substitute the published Actor identifier below. This example uses the Console prefill and a USD 0.05 charge ceiling:

```python
import os
from datetime import timedelta
from decimal import Decimal
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_API_KEY"])
run = client.actor("ACTOR_ID_OR_USERNAME/google-maps-review-rows").call(
    run_input={
        "placeIds": ["ChIJ0SnrKjO0RIYRCg9HH9d0gqE"],
        "maxReviews": 50,
        "reviewsSort": "lowestRanking",
    },
    memory_mbytes=256,
    run_timeout=timedelta(seconds=300),
    max_total_charge_usd=Decimal("0.05"),
    logger=None,
)
if run is None:
    raise RuntimeError("Actor run did not return a result")
print(run.status)
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)
coverage = client.key_value_store(run.default_key_value_store_id).get_record("COVERAGE")
print(coverage)
```

### Inputs

Schema fields use Console prefill rather than source defaults. Runtime fallback values apply only to omitted/null options. `maxReviews:0` and `maxReviewPages:0` mean unlimited. Empty source lists, `filterStars:[]`, `maxPlacesPerSearch:0` or an inverted date window start no work.

| Field | Type | Behavior |
| --- | --- | --- |
| `startUrls` | array | Place, CID, share/short-link or Maps search URLs. All supplied source lists are combined; duplicate place IDs are scraped once. |
| `placeIds` | array | Public Place IDs, 0x feature IDs or decimal CIDs. An unknown identifier must resolve to its own place. |
| `searchStringsArray` | array | Search every term at each locationQueries entry. Results follow Google's ranked search pages. |
| `locationQueries` | array | Free-text locations. An empty list searches each term without an added location. |
| `searchCoordinates` | array | Optional \[longitude, latitude] camera for term searches. This is a search bias, not a geographic census or boundary. |
| `maxPlacesPerSearch` | integer | Runtime fallback 20. Zero starts no work. |
| `maxReviews` | integer | Runtime fallback 0 means unlimited. A positive cap produces PARTIAL coverage when it truncates matches. |
| `reviewsSort` | string | Runtime fallback newest. Star/date-filtered highest or lowest scans newest then sorts the matched window. |
| `filterStars` | array | Integers 1–5. Omitted means all five ratings; an empty array starts no work. |
| `reviewsStartDate` | string | Inclusive activity timestamp boundary: ISO date/time or duration such as 30 days. Date-only means midnight UTC; months are 30 days and years 365 days. |
| `reviewsEndDate` | string | Inclusive activity timestamp boundary. A date-only value includes midnight UTC, not the whole calendar day. Inverted windows start no work. |
| `reviewsFilterString` | string | Case-insensitive substring in full original or translated text. Leading and trailing spaces are significant. Not Google's semantic review search. |
| `reviewsOrigin` | string | Filter the origin label returned by Google. Partner origins are source-dependent and not yet platform-proven. Runtime fallback all. |
| `language` | string | Requested language, runtime fallback en. Original text and separately returned translations are retained. |
| `countryCode` | string | Two-letter search/review country bias, runtime fallback us. |
| `personalData` | boolean | Runtime fallback true. False clears reviewer identity/profile fields and reviewUrl; review ID, text and owner reply remain. |
| `maxReviewPages` | integer | Runtime fallback 0 means unlimited. A positive page cap marks uncompleted scans PARTIAL. |
| `maxRunSeconds` | integer | Runtime fallback 0 disables the budget. The clock begins when the source client opens; includes retries. Exhaustion marks interrupted places PARTIAL. |
| `maxRetriesPerRequest` | integer | Runtime fallback 3; includes the first attempt. Blocks, transport failures and malformed answers rotate to fresh sessions. |
| `proxyConfiguration` | object | Omitted means Apify datacenter proxy. useApifyProxy:false uses direct egress. Residential is used only when explicitly configured. |

### Review rows

The dataset contains only `recordType:"review"` artifacts. Each row carries the place identity/context and these fields:

- Place: `placeId`, `fid`, `cid`, `title`, `address`, `neighborhood`, `street`, `city`, `postalCode`, `state`, `countryCode`, `categoryName`, `categories`, `location`, `totalScore`, `reviewsCount`, `phone`, `website`, `description`, `price`, `imageUrl`, `url`.
- Review: `reviewId`, `reviewUrl`, `name`, `text`, `textTranslated`, `publishAt`, `publishedAtDate`, `activityTimestampMs`, `timestampKind`, `likesCount`, `reviewerId`, `reviewerUrl`, `reviewerPhotoUrl`, `reviewerNumberOfReviews`, `reviewerNumberOfPhotos`, `isLocalGuide`, `reviewOrigin`, `stars`, `responseFromOwnerText`, `responseFromOwnerTextTranslated`, `responseFromOwnerPublishedAge`, `responseFromOwnerDate`, `responseFromOwnerTimestampUs`, `originalPublishedAtDate`, `originalTimestampUs`, `editedAtDate`, `editedTimestampUs`, `reviewImages`, `reviewImageUrls`, `reviewContext`, `reviewDetailedRating`, `visitedIn`, `originalLanguage`.
- Run context: `language`, `translatedLanguage`, `searchString`, `sourceUrl`, `scrapedAt`, `scanCoverage`, `coverageReason`.

Optional source fields are null or empty collections when absent. Photos are public review image URLs; no image binary is downloaded. `reviewImages` preserves each source photo's `imageUrl`, `photoId`, `caption`, `aspectRatio` (dimensionless source ratio), `thumbnailUrl` and relative `publishedAge` when available. The Search feed does not supply dimensions, coordinates or an absolute upload epoch for these photos. Full original and translated review and owner texts are normalized once from source HTML: `<br>` becomes a newline and HTML entities decode once; nested literal spellings such as `&amp;lt;` remain `&lt;`. Text filters match the normalized review content. The separate owner-reply age remains a relative source string. Context and detailed ratings preserve the labels returned in the requested language.

Example (selected fields):

```json
{
  "recordType": "review",
  "placeId": "ChIJ0SnrKjO0RIYRCg9HH9d0gqE",
  "title": "Example business",
  "reviewId": "example-review",
  "stars": 2,
  "text": "Example original review",
  "textTranslated": null,
  "publishedAtDate": "2026-09-19T00:00:00Z",
  "activityTimestampMs": 1789776000000,
  "timestampKind": "activity",
  "reviewImageUrls": [],
  "scanCoverage": "PARTIAL",
  "coverageReason": "review_limit"
}
```

**Timestamp meaning:** `publishedAtDate` is the absolute activity time supplied by this feed, mirrored as epoch milliseconds in `activityTimestampMs`. For edited reviews it may be the edit time. It is not a separately verified original publication timestamp. Date filters use this activity time. `publishAt` and `responseFromOwnerPublishedAge` preserve relative source strings. Rows with owner replies request the anonymous review permalink detail by review ID. `responseFromOwnerDate` and `responseFromOwnerTimestampUs` carry the absolute original reply time; `originalPublishedAtDate`/`originalTimestampUs` carry the original review time, and `editedAtDate`/`editedTimestampUs` carry the activity time only when it differs from the original. These epochs use microseconds; ISO strings retain that precision. These fields remain null on rows without owner replies. No date is inferred from relative text. A blocked or unreadable detail retains the review and relative age, leaves these fields null and records PARTIAL/owner_reply_enrichment_failed in COVERAGE when the scan was otherwise complete, plus the reason in ERRORS. An existing scan-limit or failure reason is preserved.

Fractional date boundaries require a clock with seconds, such as `2026-09-19T00:00:00.5Z`. Fractions after only hours or minutes are rejected rather than treated as fractional seconds. Lower boundaries remain inclusive across pages; completion by date cutoff requires a timestamp strictly below the boundary.

### Coverage and failures

Read the final **COVERAGE** record via the Actor output link. Each entry includes place ID, title, source, published review count, requested date boundaries, status/reason, unique reviews read, matching reviews, rows delivered, pages read and oldest activity time. Source failures before the count/window is known may have fewer context fields.

- **COMPLETE** means the observed newest stream crossed strictly below the inclusive lower date boundary, or a terminal page yielded at least the published count. It applies to the requested window/filters at that read time, not to deleted, hidden or later reviews.
- **PARTIAL** means a review/page cap, time budget, charge limit, source error, repeated pagination, ordering failure, storage failure, owner-reply enrichment failure or terminal count shortfall prevented that proof.
- `scanCoverage` on dataset rows describes the scan before delivery. The final KVS status is authoritative because a later billing/storage stop can truncate delivery.
- Google’s count and feed may differ or change during a scan. A count shortfall remains PARTIAL. Valid zero-review or zero-match answers are successful runs.
- Google relevant order is retained for `mostRelevant`. It must page to the terminal count for a complete filtered result; date cutoff is used only on newest streams.
- Star/date-filtered highest/lowest scans the full available window before choosing a capped subset, using a temporary disk-backed store. This can read many more reviews than it delivers.

Read **ERRORS** for failed inputs and interrupted source reads. Errors are not billable rows. Invalid input is recorded in ERRORS and exits with guidance before any source attempt. A run in which every attempted source fails and no review is delivered is marked FAILED. Partial runs with delivered rows can succeed; inspect COVERAGE instead of treating the run status as a completeness guarantee.

### Billing and egress

Billing uses Apify pay-per-event dataset delivery: one delivered review row is the unit. A charge-limit preflight starts no source work, and a limit reached during delivery stops the run before the next place. Errors and COVERAGE entries are key-value records, not review events. Pricing is set by the publisher; check the Actor's current pricing before running.

Apify datacenter proxy is the runtime fallback. Explicit direct egress is supported. Blocks, temporary HTTP failures and unreadable responses retry with fresh cookie-free sessions, up to the configured attempt count. Residential proxy groups are used only if explicitly configured. No browser, account login or Google API key is required.

### Current limits

Search follows Google's ranked pages and a camera bias; it is not a geographic census. Dense-area tiling, polygons, category/place filters, website contact crawling, place galleries, review updates/change feeds and saved-dataset sorting are outside this review-only Actor.

The six origin selectors filter labels in the returned feed; this Actor has no proof of fetching every hotel's partner-review feed. Original/edit and absolute reply dates are available only on successfully enriched owner-reply rows. Photo dimensions, coordinates and absolute upload times are unavailable from the Search review payload. A measured 30-day service success rate is not yet available.

# Actor input Schema

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

Place, CID, share/short-link or Maps search URLs. All supplied source lists are combined; duplicate place IDs are scraped once.

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

Public Place IDs, 0x feature IDs or decimal CIDs. An unknown identifier must resolve to its own place.

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

Search every term at each locationQueries entry. Results follow Google's ranked search pages.

## `locationQueries` (type: `array`):

Free-text locations. An empty list searches each term without an added location.

## `searchCoordinates` (type: `array`):

Optional \[longitude, latitude] camera for term searches. This is a search bias, not a geographic census or boundary.

## `maxPlacesPerSearch` (type: `integer`):

Runtime fallback 20. Zero starts no work.

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

Runtime fallback 0 means unlimited. A positive cap produces PARTIAL coverage when it truncates matches.

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

Runtime fallback newest. Star/date-filtered highest or lowest scans newest then sorts the matched window.

## `filterStars` (type: `array`):

Integers 1–5. Omitted means all five ratings; an empty array starts no work.

## `reviewsStartDate` (type: `string`):

Inclusive activity timestamp boundary: ISO date/time or duration such as 30 days. Date-only means midnight UTC; months are 30 days and years 365 days.

## `reviewsEndDate` (type: `string`):

Inclusive activity timestamp boundary. A date-only value includes midnight UTC, not the whole calendar day. Inverted windows start no work.

## `reviewsFilterString` (type: `string`):

Case-insensitive substring in full original or translated text. Not Google's semantic review search. Leading and trailing spaces are significant.

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

Filter the origin label returned by Google. Partner origins are source-dependent and not yet platform-proven. Runtime fallback all.

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

Requested language, runtime fallback en. Original text and separately returned translations are retained.

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

Two-letter search/review country bias, runtime fallback us.

## `personalData` (type: `boolean`):

Runtime fallback true. False clears reviewer identity/profile fields and reviewUrl; review ID, text and owner reply remain.

## `maxReviewPages` (type: `integer`):

Runtime fallback 0 means unlimited. A positive page cap marks uncompleted scans PARTIAL.

## `maxRunSeconds` (type: `integer`):

Runtime fallback 0 disables the budget. The clock begins when the source client opens; includes retries. Exhaustion marks interrupted places PARTIAL.

## `maxRetriesPerRequest` (type: `integer`):

Runtime fallback 3; includes the first attempt. Blocks, transport failures and malformed answers rotate to fresh sessions.

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

Omitted means Apify datacenter proxy. useApifyProxy:false uses direct egress. Residential is used only when explicitly configured.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.google.com/maps?cid=11637992854370324234"
    }
  ],
  "placeIds": [
    "ChIJ0SnrKjO0RIYRCg9HH9d0gqE"
  ],
  "searchStringsArray": [
    "auto repair"
  ],
  "locationQueries": [
    "Austin, Texas, USA"
  ],
  "searchCoordinates": [
    -97.7431,
    30.2672
  ],
  "maxReviews": 50,
  "reviewsSort": "lowestRanking",
  "filterStars": [
    1,
    2
  ]
}
```

# Actor output Schema

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

No description

## `coverage` (type: `string`):

No description

## `errors` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "placeIds": [
        "ChIJ0SnrKjO0RIYRCg9HH9d0gqE"
    ],
    "maxReviews": 50,
    "reviewsSort": "lowestRanking"
};

// Run the Actor and wait for it to finish
const run = await client.actor("danthedataman/google-maps-review-rows").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 = {
    "placeIds": ["ChIJ0SnrKjO0RIYRCg9HH9d0gqE"],
    "maxReviews": 50,
    "reviewsSort": "lowestRanking",
}

# Run the Actor and wait for it to finish
run = client.actor("danthedataman/google-maps-review-rows").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 '{
  "placeIds": [
    "ChIJ0SnrKjO0RIYRCg9HH9d0gqE"
  ],
  "maxReviews": 50,
  "reviewsSort": "lowestRanking"
}' |
apify call danthedataman/google-maps-review-rows --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,danthedataman/google-maps-review-rows"
        }
    }
}
```

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/yUHytxtuUZ0UQShzD/builds/eoO9F4WfZgtorDXn0/openapi.json
