# App Store & Google Play Reviews Scraper (`plainscrape/app-reviews-monitor`) Actor

Scrape reviews and app stats from Google Play and the App Store in one run. Many apps and countries, developer replies, and an incremental mode that only returns new reviews on scheduled runs.

- **URL**: https://apify.com/plainscrape/app-reviews-monitor.md
- **Developed by:** [Team Tickory](https://apify.com/plainscrape) (community)
- **Stats:** 2 total users, 1 monthly users, 84.6% runs succeeded, 1 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

## App Store & Google Play Reviews Scraper

Reviews and app statistics from **both stores in one run**: any number of apps, any number of
countries, developer replies where the store exposes them, and an **incremental mode** that returns
only the reviews that are new since the previous run. Schedule it daily and you have a review
monitor for support, ASO or sentiment pipelines without re-paying for reviews you already have.

No proxies, no logins: both stores are read through their public endpoints.

### What you get

Two record types in the same dataset, told apart by `type`.

**`review`**

| Field | Notes |
|---|---|
| `store` | `app_store` or `google_play` |
| `appId`, `country`, `language` | `language` is set for Google Play only |
| `reviewId`, `rating`, `title`, `text`, `author`, `date` | `title` is App Store only; `date` is ISO 8601 |
| `version` | App version the review was written for |
| `helpfulVotes` | Thumbs-up / helpful count |
| `developerReply` | `{text, date}` for Google Play; `null` on the App Store, whose public feed does not expose replies |
| `url` | Link to the review |

**`app`** (one per app and country, optional): title, developer, rating, rating count, review count,
installs (Google Play), price, genre, version, release and update dates, release notes, content
rating, icon, store URL.

Tasks that fail are written as `type: "error"` records with the reason, never dropped silently.

### Input

| Field | Default | Notes |
|---|---|---|
| `apps` | required | App Store / Google Play URLs, Apple numeric ids, Android package names. Mix freely. |
| `countries` | `["us"]` | One fetch per app and country. |
| `languages` | `["en"]` | Google Play review languages. Ignored for the App Store. |
| `sort` | `recent` | `recent` or `helpful`. |
| `maxReviewsPerApp` | `500` | 0 = all available. |
| `onlyNewReviews` | `false` | Incremental mode (see below). |
| `stateStoreName` | `app-reviews-monitor-state` | Where incremental state lives. |
| `sinceDate` | none | Stop once reviews older than this date are reached. |
| `minRating`, `maxRating` | 1, 5 | Keep only reviews in this star range, e.g. 1–2 for complaints. |
| `includeAppDetails` | `true` | Add the `app` snapshot record. |
| `concurrency` | 4 | Parallel app/country tasks. |

#### Incremental mode

With `onlyNewReviews: true` the actor remembers, per app and country, the newest review ids and
date it has delivered, in the named key-value store. The next run pages newest-first and stops as
soon as it reaches a review it has already delivered. Only new reviews are written and charged.
The first run delivers the newest `maxReviewsPerApp` reviews and seeds the state.

Use different `stateStoreName` values for independent monitors (for example per client).

### Pricing

Pay per event. You pay only for what is written to the dataset.

| Event | Price |
|---|---:|
| Review | $0.0001 ($0.10 per 1,000) |
| App snapshot | $0.001 |

### Limits, stated honestly

- **App Store: at most 500 reviews per app and country.** Apple's public feed serves 10 pages of
  50\. That is Apple's limit, not a setting.
- **App Store feeds can be empty** for some app and country combinations even when the app has
  reviews. The actor reports these in its status message instead of failing.
- **App Store developer replies** are not in the public feed and are returned as `null`.
- Google Play exposes reviews per **language**, not per country: the same English reviews are
  shown in every country. The actor therefore fetches Google Play reviews once per app and
  language (with `country` set to the first country in your list) and uses `countries` only for
  the per-country app snapshot. Pass several `languages` to cover a multilingual user base.
- Review counts shown in the stores include ratings without text; only reviews with text are
  retrievable.

### Example

```json
{
  "apps": ["https://apps.apple.com/us/app/youtube/id544007664", "com.whatsapp"],
  "countries": ["us", "gb", "de"],
  "languages": ["en", "de"],
  "onlyNewReviews": true,
  "maxReviewsPerApp": 200
}
```

### Changelog

- 0.1: first release.
- 0.1.6: fix runs failing on dataset schema validation (app snapshot rating is a decimal).
- 0.1.7: App Store pages that come back empty in Apple's JSON feed are retried from the XML feed of the same page.

# Actor input Schema

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

App Store or Google Play URLs, Apple numeric ids, or Android package names. Mix both stores freely.

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

Two-letter store country codes. Each app is fetched once per country.

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

Google Play review languages, e.g. en, de, he. Ignored for the App Store.

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

recent = newest first (required for incremental mode); helpful = most helpful first.

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

0 = everything available. The App Store feed never returns more than 500 per app and country.

## `onlyNewReviews` (type: `boolean`):

Remember the newest reviews seen per app and country and emit only reviews that are new since the last run. Ideal for scheduled monitoring.

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

Named key-value store that keeps the incremental state. Use different names for independent monitors.

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

ISO date, e.g. 2026-09-01. Stops paging once older reviews are reached (sort=recent).

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

Keep reviews with at least this many stars.

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

Keep reviews with at most this many stars.

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

Also write one app record per app and country: rating, rating count, installs, version, last update.

## `concurrency` (type: `integer`):

Parallel app/country tasks.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/youtube/id544007664",
    "com.whatsapp"
  ],
  "countries": [
    "us"
  ],
  "languages": [
    "en"
  ],
  "sort": "recent",
  "maxReviewsPerApp": 500,
  "onlyNewReviews": false,
  "stateStoreName": "app-reviews-monitor-state",
  "minRating": 1,
  "maxRating": 5,
  "includeAppDetails": true,
  "concurrency": 4
}
```

# Actor output Schema

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

All records of the run in the default dataset: type=review for reviews, type=app for app snapshots, type=error for failed tasks.

## `reviewsOnly` (type: `string`):

Only review records, as JSON.

## `csv` (type: `string`):

The same records as CSV for spreadsheets.

# 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": [
        "https://apps.apple.com/us/app/youtube/id544007664",
        "com.whatsapp"
    ],
    "countries": [
        "us"
    ],
    "languages": [
        "en"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("plainscrape/app-reviews-monitor").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": [
        "https://apps.apple.com/us/app/youtube/id544007664",
        "com.whatsapp",
    ],
    "countries": ["us"],
    "languages": ["en"],
}

# Run the Actor and wait for it to finish
run = client.actor("plainscrape/app-reviews-monitor").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": [
    "https://apps.apple.com/us/app/youtube/id544007664",
    "com.whatsapp"
  ],
  "countries": [
    "us"
  ],
  "languages": [
    "en"
  ]
}' |
apify call plainscrape/app-reviews-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plainscrape/app-reviews-monitor"
        }
    }
}
```

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/SeUVHT7hzbUTBnWlg/builds/lmcul646TYJG2VdS8/openapi.json
