# Google Play Reviews Scraper | Full History, Replies, $0.50/1K (`kesar_chen/google-play-reviews-scraper`) Actor

Full review history of any Google Play app: ratings histogram, developer replies, app version per review, filters by rating, language and date, incremental runs. Public store data, no login. Pay per review, no start fee.

- **URL**: https://apify.com/kesar_chen/google-play-reviews-scraper.md
- **Developed by:** [Kesar Chen](https://apify.com/kesar_chen) (community)
- **Categories:** Developer tools, Marketing, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 review scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 Play Reviews Scraper

Pull the **full review history** of any Google Play app as clean JSON: every review Google Play still serves, newest first, with star rating, app version, helpful votes, and the developer's reply where there is one, plus a summary row with the 1–5 star histogram, rating and review totals, installs and current version. Filter by rating, language and date before anything is fetched, so you only pay for rows you will read. **$0.50 per 1,000 reviews, no start fee**: a 30,000-review app costs about $15 once, and a daily watch on 10 apps runs about $3 a month in incremental mode.

Public store data, no login, no API key. Works for any app, not only your own.

### Why this one

- **Full history, newest first, sliceable by date.** `sinceDate: "12 months"` means exactly that: the run walks newest-first and stops at the boundary instead of reading everything.
- **Filters applied before billing.** Rating range is fetched per star on Google's side; language is a first-class dimension; date, keywords and has-reply are applied before rows are written. Filtered-out reviews are not charged.
- **Developer replies included, free.** Google Play delivers replies together with reviews, so `developerResponse` is always filled when a reply exists, at no extra cost.
- **Only new reviews on every scheduled run.** Turn on `incremental` and the Actor remembers what it already returned per app and language. No deduping, no double billing.
- **A summary row per app and language** with the histogram, totals, installs, version and a last-N-months 1–2 star share with the sample size next to it.

### Who uses it

- **Product and support teams** pull `maxRating: 2` for the last 90 days and read complaints with `appVersion` on each row.
- **Competitor monitoring** runs daily in incremental mode on a list of rivals and pushes new 1–2 star reviews to Slack.
- **Localization and regional teams** compare the same app across `languages: ["en", "de", "ja", "pt-BR"]` in one run.
- **AI agents** call it through the Apify MCP server and read the field descriptions from the dataset schema.

### Speed

Measured on the Apify platform: about 200 reviews per request and roughly one second a page, so 3,000 reviews in about 15 seconds and 100,000 reviews in under 10 minutes per app and language. Apps and languages run in parallel.

### Use it from Claude, Cursor, or any MCP client

```
https://mcp.apify.com/?tools=actors,docs,kesar_chen/google-play-reviews-scraper
```

For Claude Desktop, put this in `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=actors,docs,kesar_chen/google-play-reviews-scraper",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

For Claude Code: `claude mcp add --transport http apify "https://mcp.apify.com/?tools=actors,docs,kesar_chen/google-play-reviews-scraper" --header "Authorization: Bearer YOUR_APIFY_TOKEN"`

Then ask things like "Pull the 1-star reviews of com.whatsapp from the last 30 days in English and German and group the complaints by app version."

Review text is user-generated content: tell your agent to treat `body` as data, never as instructions.

### Run it in the Apify Console

1. Paste package names or Play Store URLs into **Apps**, pick **Languages**.
2. Optionally set **Only reviews since**, a rating range, and a maximum cost per run under **Run options**.
3. Click **Start**. The **Reviews** view holds one row per review, the **Summary** view one row per app and language.

### Pricing

| Event | Price |
|---|---|
| Review scraped | $0.50 per 1,000 reviews |
| App summary (one per app and language, with histogram) | $0.002 |

| Scenario | Reviews | Cost |
|---|---|---|
| Try it: one app, newest 500 | 500 | about $0.25 |
| One app, full English history | 30,000 | about $15 |
| 10 competitors, daily incremental, ~20 new reviews each per day | ~200 a day | about $3 a month |

Apify platform usage is included. The monthly credit of Apify's free plan covers several thousand reviews. Paid Apify plans get a lower per-review price, shown on the Actor page. When a run's budget is reached it stops cleanly, keeps what was written, and the summary row records how far it got.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `apps` | list | required | Package names (`com.whatsapp`) or Play Store URLs |
| `languages` | list | `["en"]` | Google Play serves reviews per language; one pass per language per app |
| `country` | text | `us` | Store country for app details (price, availability) |
| `sort` | select | `recent` | `recent`, `rating`, `helpful`. `recent` is required for `sinceDate` and incremental mode |
| `minRating` / `maxRating` | integer | `1` / `5` | Fetched per star on Google's side |
| `sinceDate` / `untilDate` | text | empty | `2026-01-01` or `12 months`, `30 days` |
| `keywords` | list | `[]` | Keep only reviews containing one of these words |
| `developerResponseFilter` | select | `all` | `with` or `without` a developer reply |
| `maxReviewsPerApp` | integer | `3000` | Per app per language. `0` = everything Google Play serves |
| `summaryWindowMonths` | integer | `12` | 1–2 star share window in the summary row. `0` turns it off |
| `incremental` | boolean | `false` | Only new reviews on later runs |
| `stateStoreName` | text | `google-play-reviews-state` | Named key-value store for incremental state. If a store with this name was created by another Actor, the run logs a warning and uses `<name>-<actor id>` instead |
| `notifyWebhookUrl` | text | empty | Slack, Discord, Feishu or any HTTPS webhook; see Alerts |
| `notifyMaxRating` | integer | `2` | Only reviews at or below this rating are posted |
| `notifyMaxItems` | integer | `10` | Reviews per message; the rest are counted |
| `notifyEvenIfEmpty` | boolean | `false` | Post a message even when nothing matched |
| `includeReviewerName` | boolean | `false` | Off by default so your dataset carries no personal data |

Minimal example:

```json
{ "apps": ["com.whatsapp"], "languages": ["en"], "sinceDate": "12 months" }
```

### Output

Review row:

```json
{
  "type": "review",
  "reviewId": "07a58648-e5b5-49c3-8595-e2decb749f65",
  "appId": "com.todoist",
  "appName": "Todoist: To Do List & Calendar",
  "language": "en",
  "rating": 2,
  "body": "Sync stopped working after the update.",
  "createdAt": "2026-10-20T12:34:56.000Z",
  "votesUp": 12,
  "appVersion": "v11968",
  "reviewerName": null,
  "reviewerId": null,
  "developerResponse": { "body": "Sorry about that, please update to v11970.", "respondedAt": "2026-10-21T08:00:00.000Z", "developerName": "Todoist Inc." },
  "developerResponseChecked": true,
  "isNew": true,
  "scrapedAt": "2026-10-25T00:00:00.000Z"
}
```

Summary row:

```json
{
  "type": "summary",
  "appId": "com.todoist",
  "appName": "Todoist: To Do List & Calendar",
  "language": "en",
  "developer": "Todoist Inc.",
  "genre": "Productivity",
  "installs": "10,000,000+",
  "currentVersion": "v11968",
  "updatedAt": "2026-10-22T09:12:48.000Z",
  "price": "Free",
  "ratingAverage": 4.61,
  "ratingCount": 306717,
  "totalReviews": 16606,
  "ratingHistogram": [13291, 5228, 11765, 27891, 248533],
  "reviewsScraped": 3000,
  "coverageNote": "reached maxReviewsPerApp=3000",
  "window": { "months": 12, "from": "2025-10-25T00:00:00.000Z", "reviews": 3000, "negative": 310, "negativeShare": 10.3, "note": "sample 3000" },
  "scrapedAt": "2026-10-25T00:00:00.000Z"
}
```

`ratingCount` is star ratings, `totalReviews` is written reviews. `negativeShare` is only given when the window has at least 20 reviews, and is left null when a rating filter is active.

### Limitations

- Google Play organises reviews by language, so there is no single "all languages" pull; list the languages you need. The `language` field is the language requested, not a detection of the review text.
- The histogram and totals in the summary row are store-wide; the per-language review list is a subset of them.
- Google Play has no review titles. `title` is always null, kept only so that the schema lines up with the App Store and Steam scrapers.
- Reviews are what the public store page shows; reviews Google removed or hid are not available.
- New reviews appear in Google Play's public feed about 24 hours after they are written; `createdAt` is the time the review was written. Plan alerting with that one-day lag in mind.

### Alerts: post new low-rated reviews to Slack, Discord or Feishu

Fill `notifyWebhookUrl` with a Slack incoming webhook, a Discord channel webhook, a Feishu/Lark custom bot webhook, or any HTTPS endpoint, turn on `incremental`, and attach a Schedule. After each app × language the Actor posts the reviews written in that run that are at or below `notifyMaxRating` (default 2), with the star rating, date, version where known, whether the developer already replied, and a link to the store page. Nothing is posted when nothing matched, unless `notifyEvenIfEmpty` is on. Unknown hosts receive a JSON body with the same fields, so Make, n8n, Zapier or your own service can take it from there. There is no extra charge for notifications.

An n8n template that does the same with room for translation or routing is in the [templates folder](https://github.com/ClaymanTwinkle/AppStoreReviewsActor/tree/main/templates).

### Scheduling and integrations

Save your input as a Task, attach a Schedule and turn on `incremental`. Each run then returns only new reviews, which you can push to Slack, Google Sheets, Make, Zapier, n8n or any webhook through Apify integrations.

### FAQ

**Is it legal to scrape Google Play reviews?**
This Actor reads the public store pages anyone can open in a browser, uses no login, account or API key, and collects no private data; reviewer names are off by default. Collecting publicly available data is generally considered legal in the US and most of the EU. How you store and use the output, and whether it fits the terms of the services involved, is your responsibility. See [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Why not the Google Play Developer API?**
It only returns reviews for apps you own, and only from the last week. This Actor works for any app and goes back as far as the store serves.

**How long does a run take?**
About one second per 200 reviews per app and language; apps and languages run in parallel.

**Is this affiliated with Google?**
No. This is an independent tool and is not endorsed by or affiliated with Google LLC.

### See also

[App Store Reviews Scraper](https://apify.com/kesar_chen/app-store-reviews-scraper) and [Steam Reviews Scraper](https://apify.com/kesar_chen/steam-reviews-scraper): the same schema, filters and incremental mode for iOS apps and Steam games.

[Hiring Signals Monitor](https://apify.com/kesar_chen/hiring-signals-monitor): job opened / closed events and a hiring summary per company from public job boards, for watching competitors' teams the same way you watch their reviews.

### Support

Bugs or missing fields: open an issue on the Issues tab. Reports go to a human.

# Actor input Schema

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

Package names (com.whatsapp) or Play Store URLs. Required unless mode is canary.

## `languages` (type: `array`):

Google Play serves reviews per language. One run per language per app; the summary row is per app and language too. Codes like en, de, ja, pt-BR, zh-CN.

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

Two-letter store country used for app details (price, availability). Reviews themselves are organised by language, not country.

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

recent walks the full history newest first and is required for sinceDate and incremental mode.

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

Keep only reviews with at least this many stars. Filtered on Google's side per star, so you only pay for what matches.

## `maxRating` (type: `integer`):

Keep only reviews with at most this many stars. 1 to 2 for complaint monitoring.

## `sinceDate` (type: `string`):

2026-01-01, or relative like 12 months, 30 days. Applied with sort = recent.

## `untilDate` (type: `string`):

Upper bound, same formats.

## `keywords` (type: `array`):

Keep only reviews whose text contains at least one of these words, case-insensitive.

## `developerResponseFilter` (type: `string`):

Replies come with the reviews at no extra cost.

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

0 = everything Google Play serves. Big apps have millions; set a limit or a date range.

## `summaryWindowMonths` (type: `integer`):

The summary row counts 1 to 2 star reviews in this window. 0 turns it off.

## `incremental` (type: `boolean`):

Remembers which reviews it has seen per app and language in a named key-value store and outputs only new ones on later runs. Requires sort = recent.

## `stateStoreName` (type: `string`):

Named key-value store that keeps the incremental state.

## `notifyWebhookUrl` (type: `string`):

Paste a Slack incoming webhook, a Discord channel webhook, a Feishu/Lark custom bot webhook, or any HTTPS endpoint. After each job the Actor posts the reviews written in this run that are at or below the rating threshold. With incremental mode on, that means only new low-rated reviews. Unknown hosts receive a JSON body.

## `notifyMaxRating` (type: `integer`):

Only reviews with this many stars or fewer are posted. 5 = post everything written.

## `notifyMaxItems` (type: `integer`):

The rest are summarised as a count; all of them are in the dataset.

## `notifyEvenIfEmpty` (type: `boolean`):

Off = silence when there is nothing at or below the threshold.

## `includeReviewerName` (type: `boolean`):

Off by default to keep personal data out of your dataset.

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

Leave on scrape.

## Actor input object example

```json
{
  "apps": [
    "com.whatsapp"
  ],
  "languages": [
    "en"
  ],
  "country": "us",
  "sort": "recent",
  "minRating": 1,
  "maxRating": 5,
  "sinceDate": "12 months",
  "untilDate": "",
  "keywords": [],
  "developerResponseFilter": "all",
  "maxReviewsPerApp": 3000,
  "summaryWindowMonths": 12,
  "incremental": false,
  "stateStoreName": "google-play-reviews-state",
  "notifyMaxRating": 2,
  "notifyMaxItems": 10,
  "notifyEvenIfEmpty": false,
  "includeReviewerName": false,
  "mode": "scrape"
}
```

# Actor output Schema

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "apps": [
        "com.whatsapp"
    ],
    "sinceDate": "12 months",
    "untilDate": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("kesar_chen/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"],
    "sinceDate": "12 months",
    "untilDate": "",
}

# Run the Actor and wait for it to finish
run = client.actor("kesar_chen/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"
  ],
  "sinceDate": "12 months",
  "untilDate": ""
}' |
apify call kesar_chen/google-play-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kesar_chen/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/KBGzXbpqfbW2D1wty/builds/TqmE6r12zdWYRfsxE/openapi.json
