# App Review Monitor: App Store & Google Play, Multi-Country (`themineworks/app-reviews-monitor`) Actor

Monitor App Store and Google Play reviews in one run. Beats Apple's 500-review per-country cap by reading many country storefronts, returning rating, title, text, author, version and date. No login, no API key, pay per review.

- **URL**: https://apify.com/themineworks/app-reviews-monitor.md
- **Developed by:** [The Mine Works](https://apify.com/themineworks) (community)
- **Categories:** Social media, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 📱 App Review Monitor: App Store & Google Play, Multi-Country

> ⚡ Pure HTTP. No login, no API key, no proxy, no browser.

> 💸 You are only charged for a review that actually lands in your dataset. Empty apps, blocked pages and failed runs are never billed.

### Overview

App Review Monitor pulls customer reviews from **both** the Apple App Store and Google Play in a single run, and returns them in one shared schema. Give it an App Store numeric ID, a Google Play package name, or both, and it returns rating, review title, body text, author, app version, helpfulness votes and posting date.

It reads Apple's own public review feed and the Play Store's own review endpoint. There is no scraping of rendered pages, no headless browser, and no proxy bill.

✅ Both stores, one schema · ✅ Past Apple's 500-review ceiling · ✅ No login or key · ✅ MCP-ready for AI agents

### Why this returns more reviews than other App Store scrapers

Apple's review feed is capped at **10 pages of 50 reviews per country storefront**. Page 11 returns an HTTP 400. That is Apple's limit, not a scraper's, so every actor that reads a single storefront tops out at **500 reviews per app** and cannot go further no matter how it is written.

But the cap is **per storefront**, and Apple runs a separate storefront for each country, each holding that country's own reviews. Pass a list of countries and the depth scales with it.

Verified live on WhatsApp (`310633997`) on 2026-08-13: a run capped at 700 returned **482 reviews from the US storefront and a further 218 from India**, in one dataset, past the point where a single-country scraper stops.

```json
{
  "appleAppIds": ["310633997"],
  "countries": ["us", "gb", "in", "de", "br", "jp", "au", "ca"],
  "maxReviewsPerApp": 2000
}
```

Eight storefronts were confirmed returning full 50-review pages, so roughly 3,500 reviews are reachable for a widely-published app against a 500-review single-country ceiling. Reviews are deduplicated on the store's own review ID, so overlapping storefronts never produce a duplicate row.

### Input

```json
{
  "appleAppIds": ["310633997"],
  "googlePlayPackages": ["com.whatsapp"],
  "countries": ["us", "gb", "in"],
  "maxReviewsPerApp": 200,
  "sortBy": "mostrecent"
}
```

| Field | What it takes |
| --- | --- |
| `appleAppIds` | Numeric App Store IDs. From `apps.apple.com/us/app/whatsapp-messenger/id310633997` use `310633997`. A leading `id` is stripped for you. |
| `googlePlayPackages` | Play package names. From `play.google.com/store/apps/details?id=com.whatsapp` use `com.whatsapp`. |
| `countries` | Two-letter storefront codes. This is the lever that gets you past 500 reviews per app. Applies to the App Store only. |
| `maxReviewsPerApp` | Hard cap per app across every country. Your main cost and runtime control. |
| `sortBy` | `mostrecent` or `mosthelpful`. See the note below on Google Play. |

### Output

One row per review, same shape from both stores:

```json
{
  "store": "app_store",
  "app_id": "310633997",
  "app_name": "WhatsApp Messenger",
  "country": "US",
  "review_id": "13284729471",
  "rating": 1,
  "title": "I need my Liquid glass please",
  "text": "I don't know why everyone else's WhatsApp Glass has updated, but mine hasn't...",
  "author": "AnYelOsky",
  "app_version": "26.31.74",
  "vote_sum": 0,
  "vote_count": 0,
  "reviewed_at": "2026-08-10T22:30:45-07:00",
  "scraped_at": "2026-08-13T11:02:14.881Z"
}
```

| Field | Description |
| --- | --- |
| 🏪 `store` | `app_store` or `google_play` |
| 🆔 `app_id` | App Store numeric ID, or Play package name |
| 📱 `app_name` | App title as published (App Store) |
| 🌍 `country` | Storefront the review came from |
| 🔑 `review_id` | The store's own review ID, stable for deduplication across runs |
| ⭐ `rating` | Star rating, 1 to 5 |
| 📄 `title` | Review headline (App Store) |
| 💬 `text` | Review body |
| 👤 `author` | Reviewer display name |
| 🏷️ `app_version` | Version the review was left against, for release-by-release analysis |
| 👍 `vote_sum` / `vote_count` | Helpfulness votes (App Store) |
| 👍 `thumbs_up` | Helpfulness count (Google Play) |
| 📅 `reviewed_at` | When the review was posted |
| 🕒 `scraped_at` | When it was captured |

**A note on Google Play sorting.** Play's review endpoint only serves newest-first. Requesting other orders returns an empty result from Google, so `sortBy: "mosthelpful"` applies to the App Store and Google Play always returns newest-first. We would rather say that plainly than quietly return nothing.

**A note on Google Play and countries.** The `countries` list applies to the App Store. Google Play is queried once per package.

### Review insights (optional, `includeReviewInsights: true`)

Set `includeReviewInsights: true` to get one extra row per app: a star rating breakdown, the most common phrases in negative (1-2 star) reviews, and a month-by-month rating trend. Computed entirely from the reviews already collected for that app in the same run, no AI, no extra fetch. Off by default. Only charged when at least one review was delivered for that app. One row per App Store app (aggregated across every country you queried) and one row per Google Play package.

```json
{
  "_type": "review_insights",
  "store": "app_store",
  "app_id": "310633997",
  "app_name": "WhatsApp Messenger",
  "rating_distribution": {
    "1_star": { "count": 40, "pct": 20.0 },
    "5_star": { "count": 120, "pct": 60.0 }
  },
  "top_complaint_terms": [{ "term": "keeps crashing", "count": 6 }],
  "rating_trend": [
    { "month": "2026-07", "review_count": 90, "avg_rating": 3.9 },
    { "month": "2026-08", "review_count": 110, "avg_rating": 4.1 }
  ],
  "based_on_reviews": 200,
  "based_on_negative_reviews": 40,
  "computed_at": "2026-08-14T10:00:00.000Z"
}
```

| Field | Description |
| --- | --- |
| `rating_distribution` | Count and percent of reviews at each star level |
| `top_complaint_terms` | Most frequent words/phrases in negative review text |
| `rating_trend` | Average rating per month, only when reviews span 2+ distinct months (`null` otherwise) |
| `based_on_reviews` / `based_on_negative_reviews` | Sample size behind the summary |

Note: there is no `reply_rate_negative_pct` field here, unlike our other review-insights actors. Neither Apple's public RSS feed nor Google Play's review endpoint exposes the developer's reply — that data only exists behind Apple's private App Store Connect API — so we leave the field out rather than report a fabricated number.

### Common use cases

**Release monitoring.** Every row carries `app_version`, so you can watch sentiment move release by release and catch a bad build in hours instead of at the next store-rating refresh.

**Competitor tracking.** Point it at a rival's app IDs on a schedule and read what their users complain about, in their own words.

**Support and CX triage.** Filter to 1 and 2 star reviews and route them into a helpdesk or Slack channel before they turn into store-rating damage.

**Localisation and market research.** Because reviews are pulled per storefront, you can compare what users in India, Brazil or Japan say against your home market rather than reading only the US view.

**AI and RAG pipelines.** Feed structured review text into a summariser or an agent that answers "what broke in the last release?"

### Pricing

| Event | Price | You pay when |
| --- | --- | --- |
| Review delivered | $0.0002 | A review lands in your dataset |
| Review insights computed | $0.02 | `includeReviewInsights` is on and at least one review was delivered for that app |

**$0.20 per 1,000 reviews.** Apps with no reviews cost nothing to check, which matters on a scheduled watchlist where most apps are quiet on any given day.

### Run it on a schedule

Review monitoring is this actor's natural mode: reviews arrive continuously and the value is in catching them early.

1. Run once with the input you want repeated, then click **Save as a task**.
2. In the Apify Console go to **Schedules → Create new**.
3. Pick a frequency, daily suits most watchlists, and add the saved task.
4. Wire the dataset to Sheets, Slack or a webhook from the **Integrations** tab.

Nothing is charged just for a schedule existing.

### FAQ

**Do I need an Apple or Google account?**
No. Both sources are public endpoints. There is no login, no API key and no OAuth.

**Why do I get fewer than 500 reviews for a small app?**
Because the app genuinely has fewer. The 500 figure is a ceiling, not a promise, and small or newly launched apps simply have less to return.

**Can I get every review an app has ever had?**
No, and no scraper can. Apple exposes 500 per storefront and Google Play serves a recent window. Adding countries increases the total substantially, but a complete lifetime archive is not available from the public endpoints.

**How do I avoid paying for the same review twice?**
Every row carries the store's own `review_id`. Deduplicate on it across runs, or use it as the key when upserting into your own store.

**Is scraping app reviews legal?**
The actor reads only publicly published reviews from public endpoints. You are responsible for how you store and use the data under the terms that apply to you.

### Use from Claude, ChatGPT and any MCP agent

```
https://mcp.apify.com/?tools=themineworks/app-reviews-monitor
```

Or call it programmatically:

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('themineworks/app-reviews-monitor').call({
  appleAppIds: ['310633997'],
  googlePlayPackages: ['com.whatsapp'],
  countries: ['us', 'gb', 'in'],
  maxReviewsPerApp: 200,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Questions, or need a field we don't return yet? Reach out through the Apify profile.

# Actor input Schema

## `appleAppIds` (type: `array`):

Numeric App Store IDs. Take the number from the store URL: apps.apple.com/us/app/whatsapp-messenger/id310633997 gives 310633997. A leading 'id' is stripped for you.

## `googlePlayPackages` (type: `array`):

Play package names. Take the id parameter from the store URL: play.google.com/store/apps/details?id=com.whatsapp gives com.whatsapp.

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

Two-letter country codes. This is how you get past Apple's 500-review-per-country ceiling: each storefront holds that country's own reviews, so more countries means more depth. Google Play is queried once, not per country.

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

Hard cap per app across every country. This is your main cost and runtime control.

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

Newest first, or most helpful first.

## `includeReviewInsights` (type: `boolean`):

Adds one extra row per app: a rating breakdown, the most common phrases in negative reviews, and a month by month rating trend. Computed entirely from the reviews already collected in this run, no AI, no extra fetch. Off by default. Only charged when at least one review was delivered for that app.

## Actor input object example

```json
{
  "appleAppIds": [
    "310633997"
  ],
  "googlePlayPackages": [
    "com.whatsapp"
  ],
  "countries": [
    "us",
    "gb",
    "in"
  ],
  "maxReviewsPerApp": 100,
  "sortBy": "mostrecent",
  "includeReviewInsights": false
}
```

# 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 = {
    "appleAppIds": [
        "310633997"
    ],
    "googlePlayPackages": [
        "com.whatsapp"
    ],
    "countries": [
        "us",
        "gb",
        "in"
    ],
    "maxReviewsPerApp": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("themineworks/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 = {
    "appleAppIds": ["310633997"],
    "googlePlayPackages": ["com.whatsapp"],
    "countries": [
        "us",
        "gb",
        "in",
    ],
    "maxReviewsPerApp": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("themineworks/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 '{
  "appleAppIds": [
    "310633997"
  ],
  "googlePlayPackages": [
    "com.whatsapp"
  ],
  "countries": [
    "us",
    "gb",
    "in"
  ],
  "maxReviewsPerApp": 100
}' |
apify call themineworks/app-reviews-monitor --silent --output-dataset

```

## MCP server setup

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