# App Store & Google Play Reviews Scraper (`garje/app-store-play-reviews-scraper`) Actor

Scrape every review Apple App Store and Google Play expose, in one flat schema with ISO dates, working review links, country on every row and exact limits. Plain HTTP, no browser.

- **URL**: https://apify.com/garje/app-store-play-reviews-scraper.md
- **Developed by:** [Aniruddha Garje](https://apify.com/garje) (community)
- **Categories:** Developer tools, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.12 / 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

## App Store & Google Play Reviews Scraper

Export reviews from the Apple App Store and Google Play for any app, country and language, in one table. I built it on the stores' public JSON endpoints with plain HTTP requests, so there is no browser to slow it down.

### At a glance

| | |
|---|---|
| Use it when | You need reviews from the Apple App Store, Google Play or both, for one or more apps and countries. |
| Input you need | App Store IDs or links and Google Play package names or links in `apps` (required); optionally `countries`, `sort`, `maxReviewsPerApp`, `since`. |
| What you get | One row per review with rating, text, ISO date, app version, country and, on Google Play, the developer reply. |
| Cost | $0.12 per 1,000 reviews saved. |
| Limits | Apple's public feed stops at 500 reviews per country and sort order. Reviewer names are off by default. |

I built this Actor and test it against what the live site shows. The check runs again every week.

### What you get

One row per review, with the same fields for both stores:

| Field | Example | Notes |
|---|---|---|
| `store` | `googlePlay` | `appStore` or `googlePlay` |
| `appId` | `com.whatsapp` | App Store ID or Play package name |
| `country` | `us` | Every row carries the country it came from |
| `language` | `en` | Google Play only; Apple's feed follows the country |
| `reviewId` | `d3bda379-...` | Stable ID from the store |
| `rating` | `4` | 1 to 5 |
| `title` | `Great app` | App Store only |
| `text` | `I love this app` | |
| `date` | `2026-10-06T15:54:48.000Z` | ISO 8601, UTC, always present |
| `appVersion` | `2.26.38.73` | When the store provides it |
| `thumbsUpCount` | `12` | Helpful votes |
| `developerReply`, `developerReplyDate` | | Google Play (Apple's public feed has no replies) |
| `reviewUrl` | | A link that opens: the review on Google Play, the app's reviews page on the App Store |
| `schemaVersion` | `1` | Changes only if the schema ever changes |

Reviewer names and profile links are **off by default**. Turn on **Include reviewer names and profile links** to add `userName`, `userUrl` (App Store) and `userImage` (Google Play).

### What this Actor handles for you

1. **Complete paging.** This Actor pages through everything each store returns. Google Play pages run until the store has no more reviews. Apple's public feed has a hard limit of 10 pages of 50 reviews (500) per country and sort order; page 11 answers HTTP 400. Apple's feed also returns some pages empty even though they hold reviews (seen on 8 October 2026); the Actor retries each empty page, carries on to the next one, and lists any page that stayed empty in `RUN_SUMMARY`, so you know exactly what may be missing. The Actor tells you when the 500 limit is reached, and you can collect more App Store reviews by adding countries, each of which has its own limit. `maxReviewsPerApp` is honoured exactly, and the run summary says why a store ran out if it did.
2. **Many countries in one run.** Every country you list is fetched separately, and every row carries its `country`.
3. **A stable schema.** One flat schema for both stores, versioned with `schemaVersion`. Fields a store does not have are present as `null`, never missing.
4. **Dates and links on every row.** `date` is always an ISO timestamp. `reviewUrl` always opens a real page: the review itself on Google Play, and the app's reviews page on the App Store (Apple has no per-review page).
5. **Errors do not stop the run, and any app format works.** Pages that fail are retried, and an app a store does not have is reported in the run summary while the run continues (`continueOnError`, on by default). Apps can be App Store IDs, App Store URLs, Play package names or Play URLs, mixed in one list.

### Input

| Field | Default | What it does |
|---|---|---|
| `apps` | | IDs, package names or URLs, both stores mixed |
| `stores` | both | Which stores to read |
| `countries` | `["us"]` | Two-letter country codes |
| `languages` | `["en"]` | Google Play languages |
| `sort` | `newest` | `newest`, `mostHelpful` or `rating` (Apple has no rating sort and uses newest) |
| `maxReviewsPerApp` | 500 | Per app, store and country (and language on Play). 0 means no limit |
| `since` | | Only reviews on or after this date |
| `ratings` | all | Keep only these stars |
| `includeDeveloperReplies` | true | Reply text and date |
| `includeReviewerInfo` | false | Reviewer names and profile links |
| `continueOnError` | true | Skip and report a failing app instead of stopping |

### Pricing

Pay per result: **$0.12 per 1,000 reviews**. You are charged only for reviews saved to your dataset, never for errors, retries or duplicates. If you set a maximum cost per run, the Actor stops cleanly when it is reached.

### Reliability

If a run is restarted or migrated, it resumes where it stopped and does not deliver or charge the same review twice. Each run saves a `RUN_SUMMARY` record with counts per app and country, why each store stopped, and any errors.

### Scheduled monitoring

Schedule the Actor weekly with `since` set to last week's date to receive only reviews from that date on.

### Use cases

Each one is a ready task you can open, run and copy:

- [Export app reviews from several countries](https://apify.com/garje/app-store-play-reviews-scraper/examples/app-reviews-several-countries): one app, four countries, one table with the country on every row.
- [Monitor new app reviews since a date](https://apify.com/garje/app-store-play-reviews-scraper/examples/app-reviews-new-since-date): only reviews from a date on. Schedule it daily and move the date forward.
- [Export only 1 and 2 star app reviews](https://apify.com/garje/app-store-play-reviews-scraper/examples/app-reviews-one-two-stars): read the complaints first, with app version and developer reply.

### Read more

I write up what the data shows, with the run ID behind every number:

- [Collect only new app reviews each week](https://garje-data-notes.laude--pify.workers.dev/articles/collect-only-new-app-reviews-each-week.html)
- [How often developers reply to Google Play reviews](https://garje-data-notes.laude--pify.workers.dev/articles/developer-replies-in-google-play-reviews.html)
- [What 4,608 app reviews look like across the App Store and Google Play](https://garje-data-notes.laude--pify.workers.dev/articles/star-ratings-in-4608-app-reviews.html)
- [Facts and limits of this Actor](https://garje-data-notes.laude--pify.workers.dev/facts/app-store-play-reviews-scraper.html) and its [changelog](https://garje-data-notes.laude--pify.workers.dev/changelog/app-store-play-reviews-scraper.html)

# Actor input Schema

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

App Store IDs or URLs (for example 310633997 or https://apps.apple.com/us/app/whatsapp-messenger/id310633997) and Google Play package names or URLs (for example com.whatsapp). Mix both stores in one list.

## `stores` (type: `array`):

Which stores to read. Apps from a store that is not selected are skipped.

## `countries` (type: `array`):

Two-letter ISO 3166 country codes, for example us or gb. Every row carries the country it came from. Apple's feed gives up to 500 reviews per country and sort order, so more countries means more App Store reviews.

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

Google Play language codes such as en, de or pt-BR. Apple's feed has no language filter; it follows the country.

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

newest, mostHelpful or rating. Apple's feed has no rating sort, so App Store apps fall back to newest when rating is chosen.

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

Honoured exactly for each app, store, country (and language on Google Play). Set 0 for no limit. If a store runs out first, the log and the RUN_SUMMARY record say why.

## `since` (type: `string`):

ISO date or date-time, for example 2026-09-01. Only reviews on or after it are returned, which suits scheduled runs. <a href="https://garje-data-notes.laude--pify.workers.dev/articles/collect-only-new-app-reviews-each-week.html" target="_blank">Read an example</a>.

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

Keep only these star ratings, for example 1 and 2 for complaints. Leave empty for all. <a href="https://garje-data-notes.laude--pify.workers.dev/articles/star-ratings-in-4608-app-reviews.html" target="_blank">Read an example</a>.

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

Adds the developer's reply text and date (Google Play). Apple's public feed does not include replies. <a href="https://garje-data-notes.laude--pify.workers.dev/articles/developer-replies-in-google-play-reviews.html" target="_blank">Read an example</a>.

## `includeReviewerInfo` (type: `boolean`):

Off by default for privacy. When on, rows add userName, userUrl (App Store) and userImage (Google Play).

## `continueOnError` (type: `boolean`):

Skip an app or page that keeps failing and report it in RUN_SUMMARY, instead of stopping the run.

## `canary` (type: `object`):

For scheduled monitoring runs. Example: {"baselineStore": "canary-baseline", "thresholdPoints": 5}. The first run saves the null rate of key fields (and the mix of a status field) to that named key-value store; later runs compare and fail with an alert if any value moves by more than thresholdPoints percentage points. Leave empty for normal runs.

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

Apify Proxy datacenter IPs are enough for both stores.

## Actor input object example

```json
{
  "apps": [
    "310633997",
    "com.whatsapp"
  ],
  "stores": [
    "appStore",
    "googlePlay"
  ],
  "countries": [
    "us",
    "gb"
  ],
  "languages": [
    "en"
  ],
  "sort": "newest",
  "maxReviewsPerApp": 200,
  "since": "2026-09-01",
  "ratings": [
    "1",
    "2"
  ],
  "includeDeveloperReplies": true,
  "includeReviewerInfo": false,
  "continueOnError": true,
  "canary": {
    "baselineStore": "canary-baseline",
    "thresholdPoints": 5
  },
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        "310633997",
        "com.whatsapp"
    ],
    "countries": [
        "us"
    ],
    "maxReviewsPerApp": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("garje/app-store-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": [
        "310633997",
        "com.whatsapp",
    ],
    "countries": ["us"],
    "maxReviewsPerApp": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

```

## MCP server setup

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