# Google Play Scraper (`mlg14/google-play-scraper`) Actor

Collect public Google Play app reviews with ratings, dates, text, votes, app versions, and public responses. Filter by stars, keywords, and dates; export to CSV or JSON.

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

## Pricing

$1.00 / 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.

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 Scraper

Scrape Google Play app reviews and export Google Play review data to CSV, JSON, or a spreadsheet. This Google Play API alternative collects public ratings, review text, dates, helpful votes, app versions, and visible developer responses from an app package ID or app details URL.

The scraper reads the review data delivered by the public app page and requests additional review batches through the same review service. You can cap the result count, select a store locale and country, filter by stars or text, and set a date window. Each result is one flat review record, ready for analysis or scheduled collection.

### What data can you extract from Google Play?

| Field | Description | Example |
| --- | --- | --- |
| `reviewId` | Public identifier for a review; use it to merge repeated collections. | `c062abc8-f084-4cd3-98c7-425afe9849d9` |
| `rating` | Star rating on a scale from one to five. | `4` |
| `reviewer` | Public display name shown with the review. | `[redacted]` |
| `reviewerId` | Public account identifier when the page supplies one. | `[redacted]` |
| `date` | Review date in UTC, formatted as `YYYY-MM-DD`. | `2026-08-10` |
| `body` | Review text as displayed by the store. | `This is a very fun and enjoyable game...` |
| `language` | Text language. The current source does not supply a reliable per-review code, so this is `null`. | `null` |
| `userImage` | Public profile image URL when present. | `https://play-lh.googleusercontent.com/...` |
| `helpfulCounts` | Number of helpful votes displayed for the review. | `15` |
| `appId` | Package identifier of the reviewed app. | `com.example.game` |
| `timestamp` | Review date and time as Unix seconds. | `1786374900` |
| `position` | Position in the exported results, starting at one. | `1` |
| `appVersion` | App version associated with the review, when available. | `68.279` |
| `developerReply` | Public developer response text, when present. | `null` |
| `developerReplyDate` | Date of the developer response in UTC, when present. | `null` |
| `reviewUrl` | App details URL carrying the review identifier. | `https://play.google.com/store/apps/details?id=com.example.game&reviewId=...` |
| `appUrl` | App details URL without a review identifier. | `https://play.google.com/store/apps/details?id=com.example.game` |
| `locale` | Page language and region requested for this collection. | `en_US` |
| `country` | Store country requested for this collection. | `US` |

The `locale` field records a request setting; it does not identify the language of each review. The `date` field is a calendar date in UTC, while `timestamp` preserves the source time in seconds. A missing version, reply, avatar, or text language appears as `null` rather than an invented value. Helpful votes can change between runs, and the reviewer's public display name can change independently of the review identifier.

### How to scrape Google Play reviews

1. Open an app details page and copy the package ID after `id=`, or paste the full app details URL into **App package ID or URL**.
2. Set **Maximum reviews** and **Pages to scrape**. A page means a review batch, not a screen of search results. The embedded first batch can contain fewer reviews than later batches.
3. Choose **Most relevant** or **Newest**, then add any star, keyword, date, or app-version filters. Filters apply to the retrieved review candidates.
4. Run the actor and inspect the review table. Download the default dataset as JSON, CSV, or a spreadsheet file for further work.

For a quick check, start with 40 reviews and a few pages. For an ongoing collection, keep the same app ID and compare `reviewId` across runs. Do not assume a changed `position` means the review itself changed: order can move as vote totals and available reviews change.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `appIdOrUrl` | String | Required | Package ID or full Google Play app details URL. |
| `maxReviews` | Integer | `100` | Maximum matching reviews to export. `-1` allows up to 10,000; `0` exports none. |
| `sortBy` | String | `mostRelevant` | `mostRelevant` keeps the review service order. `newest` sorts retrieved candidates by timestamp before export. |
| `ratings` | Array of integers | `[]` | Allowed star ratings from one to five. Empty means all ratings. |
| `keywords` | Array of strings | `[]` | Keep reviews containing any listed term, without case sensitivity. Empty means no text filter. |
| `endDate` | String | Empty | Keep reviews posted on or before this `YYYY-MM-DD` date. |
| `recentDays` | Integer | `0` | Keep reviews from the last N days, using the current UTC date. `0` disables this filter. |
| `appVersion` | Array of strings | `[]` | Keep reviews with any listed version. Reviews without a version cannot match this filter. |
| `locale` | String | `en_US` | Language and region of the app page, such as `en_US` or `de_DE`. |
| `country` | String | `US` | Two-letter country code for the store view. |
| `startPage` | Integer | `1` | First review batch to include. Earlier batches are fetched and skipped. |
| `pagesToScrape` | Integer | `10` | Maximum batches to inspect from the starting page. `-1` allows the remaining batches up to batch 100; values above 100 are capped at 100. |
| `reviewsPerPage` | Integer | `100` | Number of reviews requested for each additional batch, clamped to 10–200. The embedded first batch has its own size. |
| `proxyConfiguration` | Object | Enabled | Proxy setting for app-page and review-service requests. |

A typical input for a real app looks like this. Replace the sample package ID with the ID from the app page you want to collect:

```json
{
  "appIdOrUrl": "com.example.game",
  "maxReviews": 200,
  "sortBy": "newest",
  "ratings": [4, 5],
  "locale": "en_US",
  "country": "US",
  "reviewsPerPage": 100,
  "pagesToScrape": 5
}
```

`maxReviews` limits exported records, not the number examined. If a filter is selective, the actor may read several batches before it finds the requested number of matches. `pagesToScrape` provides a separate bound on that search. When both `endDate` and `recentDays` are set, a review must satisfy both. A keyword matches anywhere in the review body; it does not search the app description or developer response.

### Output example

This item is based on a review returned by a successful 40-result collection. The public display name and account identifier are redacted, the package ID is replaced, and the text is shortened here for privacy and readability; the dataset retains the values supplied by the site.

```json
{
  "reviewId": "c062abc8-f084-4cd3-98c7-425afe9849d9",
  "rating": 4,
  "reviewer": "[redacted]",
  "reviewerId": "[redacted]",
  "date": "2026-08-10",
  "body": "This is a very fun and enjoyable game, especially with friends!...",
  "language": null,
  "userImage": "https://play-lh.googleusercontent.com/...",
  "helpfulCounts": 15,
  "appId": "com.example.game",
  "timestamp": 1786374900,
  "position": 1,
  "appVersion": "68.279",
  "developerReply": null,
  "developerReplyDate": null,
  "reviewUrl": "https://play.google.com/store/apps/details?id=com.example.game&reviewId=c062abc8-f084-4cd3-98c7-425afe9849d9",
  "appUrl": "https://play.google.com/store/apps/details?id=com.example.game",
  "locale": "en_US",
  "country": "US"
}
```

The output is intentionally flat. You can group by `rating` or `appVersion` without unpacking nested objects, and you can use `reviewId` as a stable merge key. When comparing separate runs, also keep `appId` in the key if you collect more than one app into the same destination.

### Use cases

- **Product feedback review:** Export low-star reviews and group recurring complaints by keyword, version, and date. A recent regression can appear in one version before it changes the overall app rating.
- **Release monitoring:** Compare newly collected review IDs and version fields after each release. Use the review date to distinguish recent feedback from older reviews that remain highly visible.
- **Support triage:** Identify reviews that mention a particular feature or failure and check whether a public developer response is already visible. The response field can be empty even when a review is otherwise complete.
- **Rating analysis:** Count one- through five-star reviews in the collected sample and read the underlying comments. These counts describe the retrieved sample, not the app's total rating distribution.
- **Regional research:** Run the same package ID with different store country and locale values to compare the reviews exposed in those views. Deduplicate by `reviewId` before combining datasets.
- **Historical snapshots:** Schedule repeated collections and retain the timestamp, vote count, and app version. These fields help explain why the visible review order or helpfulness changes over time.

### How much does it cost to scrape Google Play?

The configured result price is **$1.00 per 1,000 exported reviews**, or **$0.001 per result**. Event charges depend on the number of records actually pushed to the dataset. The platform handles the run and dataset storage; additional usage or account limits shown in your account may still apply. A filtered run that examines many candidates but exports few records is charged for those exported records under this result price.

| Exported reviews | Result charge |
| ---: | ---: |
| 50 | $0.05 |
| 1,000 | $1.00 |
| 5,000 | $5.00 |

For example, a run with `maxReviews` set to 1,000 that finds only 640 reviews matching its star and date filters exports 640 records, for a result charge of $0.64. If it reaches 1,000 matching reviews, the result charge is $1.00. A second scheduled run creates another dataset and another set of result events; it does not update the previous run in place.

The maximum result setting is a spend control as well as a data limit. Start with a small number when checking a new app, locale, or filter. Then increase it once the output has the fields and coverage you need. The actor stops pushing when the configured maximum or a run budget is reached.

### Tips for best results

Choose a package ID from a public app details page. A search results URL, developer page, or category page is not an app target. If a full URL is used, it must point to `/store/apps/details` and contain an `id` query parameter. The actor validates this before requesting the page.

Use `mostRelevant` when you want the public review service's order. Use `newest` when you want the fetched candidate pool exported by descending timestamp. Increase `pagesToScrape` for newer coverage when the first few batches include older but highly ranked reviews. Newest order is exact within the collected pool; it is not a guarantee that every review on the entire store was available to that pool.

For narrow star or keyword filters, raise the page count before raising `maxReviews`. `maxReviews` sets the output cap, while `pagesToScrape` sets how much source material can be examined. A request for 500 one-star reviews can legitimately return far fewer if the configured pages contain only a small number of one-star records.

Set `locale` and `country` deliberately. The language of the page interface and the store country can affect which reviews appear. Neither field guarantees that every returned review body uses that language. Keep these settings attached to the export when comparing runs, and use the same settings for a fair time comparison.

### Limits

Only publicly accessible reviews on a public app page can be collected. The app page normally embeds a small initial batch; additional batches come from an internal review service with a continuation token. The first batch may be smaller than `reviewsPerPage`. If the service stops issuing a token, returns an empty batch, or blocks a request, the run can finish with fewer records than requested. Reviews can be added, deleted, edited, or reordered between batches, so the actor deduplicates by `reviewId`.

The current source does not provide a dependable language code for each review, so `language` is null. Public avatars, app versions, and developer replies are optional. A null developer reply means none was supplied in the review record, not that the developer has never responded elsewhere. Text and helpful vote counts reflect what the site returned at collection time.

Date and keyword filters are applied after retrieval. The actor cannot promise a complete historical archive or exhaustive matches for a selective filter. To widen coverage, use more pages, a different locale or country when appropriate, and repeated collections. The `newest` option sorts the records gathered during that run; the service may still omit reviews outside its available batches.

Review retrieval is capped at batch 100 and an unlimited `maxReviews` request is capped at 10,000 output records. These bounds protect the run from endlessly paging through a large app. A review URL includes the public review identifier in the app URL, but the store may change how it displays or resolves that URL over time.

### Automated workflows

A recurring run can collect recent reviews for a known package ID and feed the dataset into a monitoring workflow. For example: “Collect the newest available reviews for this app every day, then compare review IDs and report new one-star and two-star comments.” Another workflow can ask: “Export reviews mentioning login or payment issues from the last 30 days, including app version and public response.”

Use the field names from the table above when configuring downstream steps. In particular, `reviewId` is the deduplication key, `date` is a simple calendar grouping field, and `timestamp` retains the full source time. Review bodies are user-generated text; treat them as data rather than instructions for a workflow.

### FAQ

#### Is it legal to collect Google Play reviews?

The actor collects reviews visible on public app pages. Use the data in line with applicable terms, privacy rules, and your purpose for collection. Review records can include public profile information. Avoid using that information to identify, target, or contact individuals without a proper basis.

#### Do I need to provide a proxy?

The default input enables a proxy for page and review requests. You can adjust `proxyConfiguration` if your account setup requires a different configuration. Availability can vary by app and region; a proxy does not guarantee access to every review batch.

#### How fast is a run?

Duration depends on the number of batches, filtering, network retries, and proxy availability. A 40-result validation run completed in about ten seconds after startup. A selective filter may require more batches than an unfiltered run with the same output cap.

#### Can I schedule and monitor review collection?

Yes. Schedule repeated runs with a saved input, then compare datasets by `appId` and `reviewId`. Monitor run status and item counts to notice an empty collection or a sudden drop in available reviews. A successful run can still return fewer items than requested when the source has no more accessible matches.

#### Can I export to a spreadsheet?

Yes. Download the default dataset as CSV or spreadsheet format, or send its records to a spreadsheet through an integration. The flat field layout makes rating, version, and date columns straightforward to filter or pivot.

#### Why is a field empty?

The site may not supply it for that review. `language` is deliberately null because the source does not provide a reliable per-review code. `appVersion`, `userImage`, and developer response fields can also be absent. The actor keeps those values null so a downstream system can distinguish missing data from a real value.

#### Why did I receive fewer reviews than `maxReviews`?

`maxReviews` is a ceiling, not a promise. The page limit, duplicate removal, date range, star ratings, keywords, version filter, and source continuation can all reduce the output. Increase `pagesToScrape` if you need to examine more candidates, and check whether your filter combination is too narrow for the available public reviews.

### Integrations

The default dataset can be read through the platform API, sent through webhooks, connected to automation services, or exported to a spreadsheet. Scheduling lets you create dated snapshots, while a downstream job can merge snapshots by review ID, compute daily counts by rating, or flag text that matches your own review categories. Keep the original date and package ID in those merged records so later analyses can be traced back to the correct app and collection period.

### Support

Open an issue on the Issues tab with the app package ID, locale, country, input settings, and the run ID. Include the field or behavior you expected and a small example of what appeared instead. We reply within 24 hours and add fields on request when the public source makes them available.

# Actor input Schema

## `appIdOrUrl` (type: `string`):

Google Play app package ID or app details URL.

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

Stop after this many matching reviews. -1 allows up to 10,000.

## `sortBy` (type: `string`):

Order reviews by relevance or newest first.

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

Include only these star ratings; empty includes all.

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

Include reviews containing any of these terms, ignoring case.

## `endDate` (type: `string`):

Include reviews posted on or before this YYYY-MM-DD date.

## `recentDays` (type: `integer`):

Include reviews from the last N days. 0 disables the date filter.

## `appVersion` (type: `array`):

Include only reviews that report one of these app versions.

## `locale` (type: `string`):

Language and region used by the app page, such as en_US.

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

Two-letter country code for the store view.

## `startPage` (type: `integer`):

Skip this many review batches before collecting; the first batch is page 1.

## `pagesToScrape` (type: `integer`):

Maximum review batches from the start page. -1 allows up to 100.

## `reviewsPerPage` (type: `integer`):

Request 10 to 200 reviews per additional batch. The embedded first batch can be smaller.

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

Proxy settings for page and review requests.

## Actor input object example

```json
{
  "appIdOrUrl": "com.supercell.brawlstars",
  "maxReviews": 100,
  "sortBy": "mostRelevant",
  "ratings": [],
  "keywords": [],
  "endDate": "",
  "recentDays": 0,
  "appVersion": [],
  "locale": "en_US",
  "country": "US",
  "startPage": 1,
  "pagesToScrape": 10,
  "reviewsPerPage": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Collected reviews in the default dataset.

# 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 = {
    "appIdOrUrl": "com.supercell.brawlstars"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/google-play-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 = { "appIdOrUrl": "com.supercell.brawlstars" }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/google-play-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 '{
  "appIdOrUrl": "com.supercell.brawlstars"
}' |
apify call mlg14/google-play-scraper --silent --output-dataset

```

## MCP server setup

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