# Google Play Reviews Scraper & Monitor (`pkpakpoom/google-play-reviews-scraper`) Actor

Scrape Google Play reviews in any language, with ratings, app versions and developer replies. Monitor mode returns only the reviews posted since your last run.

- **URL**: https://apify.com/pkpakpoom/google-play-reviews-scraper.md
- **Developed by:** [Pakpoom](https://apify.com/pkpakpoom) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / 1,000 reviews

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Google Play Reviews Scraper & Monitor

Know what users say about your app, or your competitors' apps, without opening Google Play by hand. Collect reviews with ratings, app versions and developer replies for as many apps as you like, in the review language you choose. Schedule monitor mode and each run returns only the reviews posted since the last one.

**Typical uses**

- Watch new 1-2 star reviews of your own app after every release (set `ratings` to `[1, 2]`; each review carries the app version it was written on).
- Follow competitors' reviews and feature requests in each market's language.
- Feed reviews into a spreadsheet, dashboard or LLM for sentiment and topic analysis.

Plain HTTP, no browser, no login.

**Pay per review: $0.10 per 1,000 reviews.** No monthly fee, no minimum (you need an Apify account). Error items and empty results are never charged, and neither are duplicate reviews within a run.

### What you get

- **Many apps in one run.** Paste Google Play URLs or package names (`com.whatsapp`).
- **Any review language.** Google Play serves reviews per language; pick the language with `language`. Thai, Japanese, Korean, Arabic and Russian were tested.
- **Monitor mode.** Set `mode` to `newSinceLastRun`, schedule the Actor, and each run returns the reviews that appeared since the previous run of the same monitor. The first run backfills up to your limit.
- **Filters that save money.** Date range, star ratings, newest or most relevant first. A newest-first run with `dateFrom` stops paginating as soon as it passes that date.
- **Developer replies** (text and date) when the developer answered.
- **Reviewer names are off by default.** Names are personal data; turn on `includeUserNames` only with a lawful basis.
- **Failures don't fail the run.** A package that does not exist, or a throttled app, becomes an `error` item (never charged); the rest of the run continues.

### Input

| Field | Default | What it does |
|---|---|---|
| `apps` | prefilled | Google Play URLs or package names. One entry per app. |
| `country` | `us` | The Google Play store used for requests. It does not change which reviews come back; see "Store country and languages". |
| `language` | `en` | Review language (`hl`). This is what changes which reviews you get. |
| `sort` | `newest` | `newest`, `mostRelevant`, `ratingHigh` (newest rated 4-5), `ratingLow` (newest rated 1-2). |
| `maxReviewsPerApp` | `1000` | Limit per app, 1 to 25,000. For more, see "How many reviews can I get?". |
| `dateFrom` / `dateTo` | none | ISO dates, inclusive. |
| `ratings` | all | Only these star ratings, for example `[1, 2]`. |
| `mode` | `all` | `all` or `newSinceLastRun`. |
| `monitorId` | derived | Names a monitor so several independent ones can run. When empty, it is derived from your input, so changing the apps, language or filters starts a new monitor and a new backfill. Set it for scheduled monitors. |
| `includeDeveloperReplies` | `true` | Attach developer replies. |
| `includeUserNames` | `false` | Add reviewer names (personal data). |
| `maxConcurrency` | `5` | Apps fetched in parallel. |
| `failOnError` | `false` | For scheduled runs with alerts: end the run as **failed** if any app ends in an error item. Reviews already returned stay saved and charged. |
| `minReviews` | `0` | For scheduled runs with alerts: end the run as **failed** if fewer reviews than this came back in total. In monitor mode a quiet interval can return few reviews, so use it only when you expect a minimum. |

#### Example: the newest 500 reviews of one app

```json
{ "apps": ["com.spotify.music"], "maxReviewsPerApp": 500 }
```

#### Example: German reviews of two apps

```json
{ "apps": ["com.whatsapp", "com.instagram.android"], "language": "de", "maxReviewsPerApp": 2000 }
```

#### Example: weekly monitor of new low-star reviews

```json
{ "apps": ["com.yourcompany.app"], "mode": "newSinceLastRun", "ratings": [1, 2], "monitorId": "weekly-lowstars" }
```

Save this input as a task, then schedule the task weekly in Apify Console. The first run backfills; every later run returns reviews that are new.

### Store country and languages

In our test, the Google Play store did not change which reviews came back. We requested 20 popular apps from five stores (`us`, `gb`, `de`, `fr`, `in`) with `language: en`, and the stores returned **the same reviews**: the median overlap across 200 store pairs was 100% and the lowest was 97.5%. Only English was compared across stores.

What that means for you:

- `country` is the store used for requests. Leave it at `us` unless the app is not available in the US store.
- To get different reviews, change `language`. One language per run.
- The number of reviews depends strongly on the language. One app can have 3 reviews in one language and more than 1,500 in another. A language with no reviews for an app returns nothing; that is not an error.
- An app listed twice (for example as a URL and as a package name) is fetched and charged once.

### Monitor mode

- State is kept in a named key-value store in **your** account (`google-play-reviews-scraper-state`), one record per monitor. Delete the record to start over with a backfill.
- Reviews already returned are remembered even when a run stops early (aborted, or out of budget), so the next run neither repeats them nor skips the ones it did not reach.
- The time watermark moves only after a completed pass, which is a run that reached reviews older than the previous watermark. It moves to the newest review time that pass saw in the feed, never to the time of the run, so a stale or late feed cannot make it skip reviews.
- **Edited reviews.** Google Play can list a review again, with a newer date, after its author edits it. A review that was already returned is not returned again, so an edit to an already-delivered review is not re-delivered. The scan never stops at a review it has seen. It stops after a whole page of reviews older than the last completed pass.
- The state holds a fingerprint of the most recent review IDs of each app (up to 40,000 across all apps of a monitor) and stays under 1 MB (about 0.6 MB at the largest). An edited review that is older than that window is returned again as new.
- Google Play serves the review feed from caches of different age. In our tests a run sometimes saw a feed up to about 30 minutes behind the previous run and returned nothing; the reviews arrived with the next run, and none were skipped or repeated. Schedule monitors at intervals of an hour or more.
- **Limit and backlog.** If a run after the first stops at `maxReviewsPerApp` before it reaches reviews it already returned, more new reviews may remain. The run says so in its status message and in `PAIR_STATS` (`moreNewReviewsRemain: true`), the saved watermark does not move, and the next run continues where this one stopped: nothing is skipped and nothing is returned twice. Set `maxReviewsPerApp` above the number of new reviews you expect between runs so one run is enough. The first run is a backfill and simply stops at your limit.
- Monitor mode returns reviews in newest-first order; `sort: mostRelevant` is ignored in this mode.

### Output

One dataset item per review:

```json
{
  "platform": "googleplay",
  "appId": "com.netflix.mediaclient",
  "country": "in",
  "language": "en",
  "reviewId": "221f4a73-f2cf-4036-880c-436978f6b43f",
  "rating": 4,
  "text": "there needs to be less regional restrictions because I want to watch ...",
  "appVersion": "9.86.0 build 5 74592",
  "reviewedAt": "2026-10-04T11:26:00.000Z",
  "thumbsUpCount": 0,
  "developerReply": null,
  "reviewUrl": "https://play.google.com/store/apps/details?id=com.netflix.mediaclient&reviewId=221f4a73-f2cf-4036-880c-436978f6b43f",
  "scrapedAt": "2026-10-10T12:47:07.702Z",
  "source": "gplay-batchexecute-UsvDTd"
}
```

| Field | Notes |
|---|---|
| `reviewId`, `rating`, `text`, `reviewedAt`, `scrapedAt` | Never null. `text` is an empty string when the reviewer left only stars. |
| `appVersion`, `thumbsUpCount`, `reviewUrl` | Can be null when Google Play does not provide them. |
| `developerReply` | `{ "text", "repliedAt" }`, or `null` when there is no reply or replies are turned off. |
| `userName` | Only when `includeUserNames` is `true`. |

Google Play reviews have no title, so there is no title field.

An error item looks like `{ "type": "error", "app": "...", "country": "...", "message": "..." }` and is never charged. A summary per app and country (reviews returned, duplicates skipped, why the pair ended, seconds) is stored in the `PAIR_STATS` record of the run's key-value store.

### Empty and not-found results

- **The package does not exist on Google Play:** one `error` item for that app, not charged.
- **The app exists but has no reviews in your language:** an empty result, no items, no error, not charged. The run log says so.

### Pricing

Pay per event:

| Event | Price |
|---|---|
| Each review returned | **$0.0001** ($0.10 per 1,000) |
| Actor start | $0.00005 |

Worked examples:

- 2,000 reviews of one app: 2 × $0.10 = **$0.20**.
- 20 apps × 2,000 reviews (40,000 reviews): **$4**.
- A weekly monitor that finds 150 new reviews: 150 × $0.0001 = **$0.015** a run, plus the start fee.

Set a spending limit on the run and the Actor stops cleanly at the limit; it does not return reviews you have not paid for.

### FAQ

**How many reviews can I get?** We measured two large apps (WhatsApp and Instagram, English, US store) with a limit of 25,000: each returned 25,000 reviews in about two minutes and stopped at the limit, not because Google Play ran out. We have not measured beyond 25,000. The limit is 25,000 reviews per app per run. For more, run again with `dateTo` set to the oldest date returned by the previous run.

**Do I need a proxy or a login?** No. The Actor reads public data and needs neither.

**What happens if Google throttles requests?** The Actor retries with exponential backoff. After repeated throttling it switches to an Apify datacenter proxy when your plan includes one; otherwise the affected pair returns an uncharged error item and the rest of the run continues. Throttling has not been observed on the platform in the measurements.

**Is the App Store supported?** No. This Actor is for Google Play.

**Is reviewer data collected?** Only public review content. Reviewer names are off by default.

**Is this affiliated with Google?** No. It is an independent tool and is not affiliated with, endorsed by or sponsored by Google. Google Play is a trademark of Google LLC.

### Changelog

- **1.0** — First public release.

# Actor input Schema

## `apps` (type: `array`):

Google Play URLs or package names. One entry per app.

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

Two-letter code of the Google Play store used for requests (gl). It does not change which reviews come back; change it only if the app is not available in the US store.

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

Review language / UI locale (Google Play `hl`), e.g. en.

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

ratingHigh/ratingLow return the newest reviews rated 4-5 / 1-2 (no native Google Play sort).

## `maxReviewsPerApp` (type: `integer`):

Limit per app, 1 to 25,000. For more, run again with the To date set to the oldest date you received.

## `dateFrom` (type: `string`):

ISO date (YYYY-MM-DD). Older reviews are skipped.

## `dateTo` (type: `string`):

ISO date (YYYY-MM-DD), inclusive.

## `ratings` (type: `array`):

Only these star ratings (1-5).

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

newSinceLastRun returns only reviews published since the previous run of the same monitor (the first run backfills up to the max). Schedule it weekly.

## `monitorId` (type: `string`):

Optional name for the monitor state, so you can run several monitors. Defaults to a hash of apps, countries, language and filters.

## `includeDeveloperReplies` (type: `boolean`):

Attach the developer reply (text and date) to each review when one exists.

## `includeUserNames` (type: `boolean`):

Reviewer names are personal data. Leave off unless you have a lawful basis to process them.

## `failOnError` (type: `boolean`):

For scheduled runs with alerts: end the run as failed when any app ends in an error item (for example a package that does not exist). Reviews already returned stay saved and charged.

## `minReviews` (type: `integer`):

For scheduled runs with alerts: end the run as failed when fewer reviews than this were returned in total. 0 = off. In monitor mode a quiet interval can legitimately return few reviews, so set it only when you expect a minimum.

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

How many apps are fetched in parallel.

## Actor input object example

```json
{
  "apps": [
    "com.whatsapp",
    "https://play.google.com/store/apps/details?id=com.spotify.music"
  ],
  "country": "us",
  "language": "en",
  "sort": "newest",
  "maxReviewsPerApp": 1000,
  "mode": "all",
  "includeDeveloperReplies": true,
  "includeUserNames": false,
  "failOnError": false,
  "minReviews": 0,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

All reviews of the run, one item each (error items included).

## `overview` (type: `string`):

The same items as a compact table view.

## `pairStats` (type: `string`):

For every app and country: reviews returned, reviews skipped as duplicates, why the pair ended, and seconds taken.

# 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 = {
    "apps": [
        "com.whatsapp",
        "https://play.google.com/store/apps/details?id=com.spotify.music"
    ],
    "country": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("pkpakpoom/google-play-reviews-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "apps": [
        "com.whatsapp",
        "https://play.google.com/store/apps/details?id=com.spotify.music",
    ],
    "country": "us",
}

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

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

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

```

## CLI example

```bash
echo '{
  "apps": [
    "com.whatsapp",
    "https://play.google.com/store/apps/details?id=com.spotify.music"
  ],
  "country": "us"
}' |
apify call pkpakpoom/google-play-reviews-scraper --silent --output-dataset

```

## MCP server setup

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

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/oKgrecXSpIWuSWDih/builds/BC6Fke7R5psCfXZqI/openapi.json
