# Google Play Reviews & App Details Scraper (`zkill3r/google-play-reviews-scraper`) Actor

Scrape Google Play reviews (stars, text, date, likes, developer replies, app version) and app details (rating, histogram, installs, price, developer email) by package name, URL or search term. Fast, cheap, HTTP-only.

- **URL**: https://apify.com/zkill3r/google-play-reviews-scraper.md
- **Developed by:** [Zee Lex](https://apify.com/zkill3r) (community)
- **Categories:** Developer tools, E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.08 / 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

## Google Play Reviews & App Details Scraper

Scrape **Google Play reviews** and **app details** for any Android app, by package name, Play Store URL or search term.

- **Reviews:** star rating, text, date, thumbs-up count, app version, developer reply and reply date, reviewer name and avatar, and a direct link to each review.
- **App details:** title, rating, ratings and reviews counts, star histogram, installs, price, genre, content rating, release and update dates, version, minimum Android version, description, icon, screenshots and developer contact details.

Fast and cheap: plain HTTP, no browser. The default memory is 512 MB.

### What it does

1. Takes your apps (`com.whatsapp` or `https://play.google.com/store/apps/details?id=com.whatsapp`) and, optionally, search terms that it turns into the top N apps.
2. For each app, saves one **app** item (unless you turn that off) and pages through **reviews** until it reaches your limit, your date cutoff, or the last review Google Play makes available.
3. Validates every item, skips unknown apps with a warning, and writes a `RUN_SUMMARY` record with per-app counts, errors, stop reasons and the percentage of missing values for every field.

### Use cases

- **App review monitoring:** track new reviews of your app every day, with `reviewsSince` and a schedule
- **Competitor research:** compare ratings, star histograms, install counts and complaints across competing apps
- **Sentiment analysis:** export review text with stars, dates and app versions into your NLP pipeline or an LLM
- **Customer support QA:** find unanswered 1-star reviews (`filterByStars: 1`, empty `replyText`)
- **Market research:** search a category ("budget planner") and pull details and reviews of the top apps

### Input example

```json
{
    "appIds": [
        "com.whatsapp",
        "https://play.google.com/store/apps/details?id=org.telegram.messenger&hl=en"
    ],
    "searchTerms": ["budget planner"],
    "maxAppsPerSearch": 3,
    "country": "us",
    "language": "en",
    "maxReviewsPerApp": 500,
    "sort": "newest",
    "reviewsSince": "2026-10-01",
    "includeAppDetails": true
}
```

| Field | Default | Notes |
|---|---|---|
| `appIds` | – | Package names or Play URLs |
| `searchTerms` | – | Resolved to the top `maxAppsPerSearch` apps |
| `maxAppsPerSearch` | `5` | |
| `country` / `language` | `us` / `en` | Google Play `gl` / `hl` |
| `maxReviewsPerApp` | `100` | `0` = all available |
| `sort` | `newest` | `newest`, `most_relevant`, `rating` |
| `filterByStars` | – | 1–5 |
| `reviewsSince` | – | ISO date; with `newest`, paging stops at the first older review |
| `includeAppDetails` | `true` | One `app` item per app |
| `maxConcurrency` | `5` | |
| `proxyConfiguration` | Apify datacenter proxy | |

### Output sample

Each dataset item has `"type": "review"` or `"type": "app"`.

**Review**

```json
{
    "type": "review",
    "appId": "com.whatsapp",
    "reviewId": "gp:AOqpTOE1aaa",
    "userName": "Aisha Rahman",
    "userImage": "https://play-lh.googleusercontent.com/a/...",
    "score": 5,
    "text": "Works great, video calls are clear.",
    "thumbsUpCount": 12,
    "appVersion": "2.26.21.73",
    "date": "2026-10-27T00:00:00.123Z",
    "replyText": null,
    "replyDate": null,
    "reviewUrl": "https://play.google.com/store/apps/details?id=com.whatsapp&reviewId=gp%3AAOqpTOE1aaa",
    "country": "us",
    "language": "en"
}
```

**App**

```json
{
    "type": "app",
    "appId": "com.whatsapp",
    "title": "WhatsApp Messenger",
    "developer": "WhatsApp LLC",
    "developerId": "5700313618786177705",
    "developerEmail": "android@support.whatsapp.com",
    "developerWebsite": "http://www.whatsapp.com/",
    "score": 4.24,
    "ratingsCount": 200123456,
    "reviewsCount": 9871234,
    "histogram": { "1": 18000000, "2": 4000000, "3": 7000000, "4": 15000000, "5": 156000000 },
    "installs": "5,000,000,000+",
    "minInstalls": 5000000000,
    "price": 0,
    "free": true,
    "currency": "USD",
    "genre": "Communication",
    "contentRating": "Rated for 3+",
    "released": "Oct 18, 2010",
    "updated": "2026-10-25T00:00:00.000Z",
    "version": "2.26.21.73",
    "androidVersion": "5.0",
    "description": "WhatsApp from Meta is a FREE messaging app...",
    "icon": "https://play-lh.googleusercontent.com/...",
    "screenshots": ["https://play-lh.googleusercontent.com/..."],
    "url": "https://play.google.com/store/apps/details?id=com.whatsapp&hl=en&gl=us"
}
```

Field notes:

- `date`, `replyDate` and `updated` are ISO 8601 (UTC).
- `released` is the text Google Play shows, in the requested language (e.g. "Oct 18, 2010").
- `androidVersion` is the minimum version number, or `"VARY"` when Google Play says "Varies with device".
- `version` is `null` if Google doesn't show it (often "Varies with device" apps).
- `price` is in `currency` units; `free` is true only when the price is exactly 0.
- Missing values are `null` (or `[]`), never guessed, so `RUN_SUMMARY` shows exactly what Google didn't provide.

### Run summary

`RUN_SUMMARY` in the default key-value store (linked from the run's Output tab) contains:

- **`perApp`**: `status` (`ok` / `not_found` / `failed`), `reviewsSaved`, `pagesFetched`, `errors`, `stopReason` (e.g. `maxReviewsPerApp reached`, `reviewsSince reached`, `no more pages`), duplicates skipped, filtered-out reviews and which reviews RPC was used
- **`totals`**: items saved, apps OK / not found / failed, invalid items dropped, retries and their reasons
- **`missingFieldPercentages`**: the % of missing values for every review and app field
- **`invalidInputs`** and **`searches`** (which apps each search term resolved to)

The run **fails** (non-zero exit) if no items were saved, with a message saying why. Unknown apps are logged as warnings, recorded as `not_found`, and the run continues.

### Reliability

- 429, 5xx, empty or unparsable responses and Google consent redirects are retried up to 4 times. Each retry uses exponential backoff (2, 4, 8, 16 s) and a fresh session (new proxy IP and cookies) for that app.
- Each app keeps its own session and cookies while paging, which keeps continuation tokens consistent.
- Reviews are deduplicated per app by `reviewId`.
- Two reviews endpoints are supported. If Google rejects the current one for an app, the Actor automatically falls back to the older one and records this in `RUN_SUMMARY`.

### Limitations

- Google Play doesn't expose every review. For very popular apps, paging ends after a few thousand reviews per sort order and language, even with `maxReviewsPerApp: 0`. Combining sorts, star filters or languages gets more.
- Reviews are per country and language. The same app can show different reviews for `gl=us` and `gl=gb`.
- `reviewsSince` can stop paging early only with `sort: newest`. With other sorts it just filters.
- Apps that aren't available in the chosen country show up as `not_found`.
- The Actor reads Google Play's internal page data and review endpoint, which Google changes without notice. All layout knowledge is in `src/mappings.ts`. A rising missing-field percentage in `RUN_SUMMARY` is the early warning.
- Reviewer names and avatars are personal data. Make sure your use complies with GDPR, PDPA and similar laws, and with Google Play's terms.

### Pricing

Pay per result: **$0.09 per 1,000 results** (reviews or app records) on the Free plan, $0.085 on Bronze, $0.08 on Silver and $0.075 on Gold+. Platform usage is included. A run that matches no reviews only costs the tiny actor-start fee.

Tested live (Oct 2026): 100 WhatsApp reviews + app details in ~45 s; 50 one-star Grab Malaysia reviews in ~5 s.

### Development

```bash
npm install
npm test                    # unit tests (node --test) on saved fixtures
npm run build && apify run  # local run with storage/key_value_stores/default/INPUT.json
```

**Verifying against real Google responses:** where play.google.com is reachable, run `npm run capture-fixtures`. It saves real responses to `test/fixtures/live/`, and `npm test` then also runs `test/live-fixtures.test.ts` against them. The fixtures in `test/fixtures/` are synthetic: they're built from google-play-scraper's field paths by `make_fixtures.py`.

**Offline end-to-end run:** `npm run mock-server` starts a local stand-in for Google Play that simulates a 429, a rejected reviews RPC and an unknown app. Then run `PLAY_BASE_URL=http://127.0.0.1:4566 node dist/main.js`.

Field paths are ported from the open-source [google-play-scraper](https://github.com/facundoolano/google-play-scraper) (MIT): v10.1.4 for app, search and review mappings, and v10.1.3 for the legacy `UsvDTd` reviews request. That package is not a runtime dependency.

# Actor input Schema

## `appIds` (type: `array`):

Apps to scrape: package names (com.whatsapp) or full Google Play URLs (https://play.google.com/store/apps/details?id=com.whatsapp). Invalid entries are skipped with a warning.

## `searchTerms` (type: `array`):

Optional. Each term is searched on Google Play and the top results are added as apps to scrape, e.g. "budget planner".

## `maxAppsPerSearch` (type: `integer`):

How many top search results to scrape for each search term.

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

Two-letter country code (gl), e.g. us, gb, my, in. Affects which reviews, prices and availability you see.

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

Language code (hl), e.g. en, ms, pt-BR. Reviews are returned in this language where available.

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

Maximum number of reviews to save per app. 0 = all reviews Google Play makes available.

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

Order in which reviews are fetched. "Newest" is required for "Reviews since" to stop paging early.

## `filterByStars` (type: `integer`):

Optional. Only return reviews with exactly this star rating (1-5). Leave empty for all ratings.

## `reviewsSince` (type: `string`):

Optional ISO date (e.g. 2026-10-01). Only reviews on or after this date are saved; with sort "Newest", paging stops as soon as older reviews appear.

## `includeAppDetails` (type: `boolean`):

Also save one app item per app (title, rating, histogram, installs, price, developer contact, version...).

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

How many requests run in parallel. Lower it if you see many 429 retries in RUN_SUMMARY.

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

Proxy settings. Apify datacenter proxy works for most runs; switch to RESIDENTIAL if you see many 429 or consent-page retries.

## Actor input object example

```json
{
  "appIds": [
    "com.whatsapp"
  ],
  "maxAppsPerSearch": 5,
  "country": "us",
  "language": "en",
  "maxReviewsPerApp": 100,
  "sort": "newest",
  "includeAppDetails": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Review items (type = "review").

## `apps` (type: `string`):

App detail items (type = "app").

## `allItems` (type: `string`):

Reviews and app details together, all fields.

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

Per-app counts (reviews saved, pages fetched, errors, stop reason), totals, not-found apps and % missing for every field.

# 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 = {
    "appIds": [
        "com.whatsapp"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zkill3r/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 = {
    "appIds": ["com.whatsapp"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("zkill3r/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 '{
  "appIds": [
    "com.whatsapp"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call zkill3r/google-play-reviews-scraper --silent --output-dataset

```

## MCP server setup

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