# App Store & Google Play Reviews with daily tracking (`dataforge-tools/app-reviews`) Actor

Customer reviews of any iOS or Android app as clean JSON: rating, title, text excerpt, version, date and developer reply, with country, language, rating and date filters. Schedule it daily with 'Only new reviews' on. Reads Apple's and Google Play's public product pages, no logins.

- **URL**: https://apify.com/dataforge-tools/app-reviews.md
- **Developed by:** [DataForge](https://apify.com/dataforge-tools) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 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 Reviews: App Store & Google Play, with daily new-review tracking

Name the apps you care about and get a **featured sample of customer reviews from the App Store and Google Play** as clean, consistent JSON: stars, title, a text excerpt, app version, date, helpful votes and the developer's reply. Each run also tells you **which reviews are new since the last run**.

- **Both stores, one schema:** paste App Store or Google Play URLs; the Actor detects the store. Every record has the same fields whichever store it came from.
- **Filters that matter:** countries (App Store storefronts), languages (Google Play), a star range (for example only 1- and 2-star reviews), and "posted within the last N days".
- **Change tracking built in:** every record has `changeStatus` (`new` or `existing`) and `firstSeenAt`. Turn on **Only new reviews** and schedule the Actor daily to get just the new reviews.
- **Limited samples:** both stores' public product pages show selected reviews, with no paging or newest-first guarantee. `onlyNew` tracks reviews that enter this sample, not every newly posted review. One wrong app never fails the run.
- **No logins, no proxies, no browser:** only the stores' public endpoints, with polite rate limits and a descriptive User-Agent. Reviewer names, profile links and avatars are never output.

### Use cases

- **Product and support teams:** a daily digest of new 1- and 2-star reviews across your apps, countries and languages, routed to Slack or a ticketing tool.
- **Competitive analysis:** watch competitors' reviews for feature requests, bugs and pricing complaints; compare ratings per version.
- **App store optimisation (ASO) and marketing:** track how a release changes sentiment; find quotable 5-star reviews.
- **Market research and data science:** build review datasets per app and country for classification or trend analysis (text excerpts and ratings; follow `url` for full text).
- **Agencies and dashboards:** feed many clients' apps into one pipeline with stable ids and `onlyNew` increments.

### How to use it

1. Add apps to **Apps**, one per line. Any of these forms works:
   - `https://apps.apple.com/us/app/whatsapp-messenger/id310633997` or `appstore:310633997` or `appstore:net.whatsapp.WhatsApp` (bundle id)
   - `https://play.google.com/store/apps/details?id=com.whatsapp` or `play:com.whatsapp`
2. Pick **Countries** for App Store storefronts (each country is a separate feed) and **Languages** for Google Play reviews.
3. Add filters if you want them: star range, posted within N days, max reviews per app.
4. For daily monitoring, schedule the Actor with **Only new reviews** turned on.

#### Input example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997",
    "https://play.google.com/store/apps/details?id=com.whatsapp"
  ],
  "countries": ["us", "gb"],
  "languages": ["en"],
  "minRating": 1,
  "maxRating": 2,
  "postedWithinDays": 30,
  "onlyNew": true,
  "maxReviewsPerApp": 200
}
```

| Field | Default | What it does |
|---|---|---|
| `apps` | WhatsApp on both stores | Store URLs, `appstore:<id>`, `appstore:<bundle id>`, `play:<package>`, a bare id or a bare package name. |
| `countries` | `["us"]` | App Store storefronts, one feed each. Google Play gets the first one as its region. Clear the list to use each App Store URL's own storefront. |
| `languages` | `["en"]` | Google Play review languages, one feed each. App Store reviews are not filtered by language. |
| `minRating` / `maxRating` | `1` / `5` | Keeps reviews in this star range. Equal values select one star value. |
| `postedWithinDays` | any | Keeps reviews posted in the last N days within the available sample. |
| `onlyNew` | `false` | Outputs only reviews earlier runs with the same apps, countries, languages and filters have not seen. |
| `maxReviewsPerApp` | `200` | At most N matching reviews from the page sample per app and country (App Store) or language (Google Play). |

### Output

Every dataset item has the same shape, whichever store it came from:

```json
{
  "id": "play:com.whatsapp:d40bb75a-d1a5-44b3-863d-c8624445efe9",
  "store": "play",
  "appId": "com.whatsapp",
  "appName": "WhatsApp Messenger",
  "developer": "WhatsApp LLC",
  "country": "us",
  "language": "en",
  "reviewId": "d40bb75a-d1a5-44b3-863d-c8624445efe9",
  "rating": 1,
  "title": null,
  "textExcerpt": "The most useless app I've encountered since apps were invented. I have it on my phone, and whenever I try to link it to another device - PC or tablet, using their app - the account is placed under review, during which time I can't use it, without explanation. There is no customer support as such, just what seems to be a beta version of 1st-generation AI. Don't waste your time.",
  "textTruncated": false,
  "appVersion": "2.26.33.74",
  "helpfulCount": 13,
  "postedAt": "2026-08-28T20:40:15.000Z",
  "developerReply": null,
  "url": "https://play.google.com/store/apps/details?id=com.whatsapp&reviewId=d40bb75a-d1a5-44b3-863d-c8624445efe9",
  "appUrl": "https://play.google.com/store/apps/details?id=com.whatsapp&hl=en&gl=us",
  "firstSeenAt": "2026-10-04T07:16:24.000Z",
  "changeStatus": "new",
  "scrapedAt": "2026-10-04T07:16:24.509Z"
}
```

An App Store record looks the same, with `"store": "appstore"`, `"appId": "310633997"`, an id like `appstore:310633997:us:14621029193`, a `title`, `appVersion: null`, `helpfulCount: null`, `language: null`, `developerReply: null` (not provided by this parser) and `url` pointing to the app's reviews page (Apple has no per-review links).

Field notes:

- `id` is stable across runs, so you can use it as a primary key. App Store ids include the storefront because the same review id can appear in several storefront feeds.
- `textExcerpt` is the first 400 characters of the review as plain text; `textTruncated` tells you when it was cut. The Actor never outputs full review text; follow `url`.
- `helpfulCount` is Google Play's thumbs-up count, or null on App Store pages.
- `developerReply` is set on Google Play when the developer answered; it has the same excerpt rule.
- `country` is the App Store storefront or the Google Play region sent with the request; `language` is the Google Play review language (`hl`).

The dataset's **Overview** view shows the key columns as a table; **Developer replies** puts review and reply side by side. Export to JSON, CSV, Excel, or use the API or integrations.

#### Run summary (`OUTPUT` record)

Each run writes a summary to the default key-value store under the key `OUTPUT`:

The summary includes totals, per-feed status/counts, errors, HTTP request count and charge-limit status. `pagesEmpty` counts product pages with no featured reviews. A robots-denied or unavailable page is reported as a feed error, while other apps continue. There are no robots exceptions.

### Change tracking: how it works

- History is kept in a named key-value store, `app-reviews-state`, in your account, keyed by a hash of your **apps + countries + languages + filters**. Changing `onlyNew` or `maxReviewsPerApp` keeps the history. Changing apps, countries, languages or filters starts a new one.
- The first run treats every review as `new`. Later runs mark a review `new` only if it wasn't seen before.
- If a run stops early because it hit your **maximum cost per run**, reviews you didn't receive stay `new` for the next run. Nothing is lost.
- If a feed fails to load , its history is left untouched, so the reviews come out as `new` on the next successful run.

### Pricing

Pay-per-event: you pay only for the reviews you receive (event `review`, one per dataset item).

**Suggested price: $0.50 per 1,000 reviews.** The owner sets the actual price. With **Only new reviews** on, a daily run usually costs a fraction of a cent, because you pay only for new reviews. Set a maximum cost per run in Apify and the Actor stops cleanly when it reaches it.

### FAQ

**Which apps are supported?** Any app on the App Store or Google Play. The App Store is read per storefront (country); Google Play per language.

**Why only a few reviews?** Both stores' public product pages embed a selected sample: typically a handful on Apple and about 20 on Play. RSS and RPC pagination are excluded because robots.txt disallows them. The actor cannot promise complete history or all daily new reviews. Samples and page formats may change.

**Can I get only 1-star reviews from the last week?** Yes: `minRating: 1`, `maxRating: 1`, `postedWithinDays: 7`.

**Does it include who wrote the review?** No. Reviewer names, profile links and avatars are excluded. Legacy `includeAuthorName` and `playSource` inputs are ignored and cannot enable personal-data fields or RPC requests.

**How fast is it?** The default input uses one product page per store plus Apple's metadata lookup; it must finish in under five minutes.

**What happens with a wrong app id?** You get a warning, an entry in `OUTPUT.errors`, and the run continues with the other apps. The run is marked failed only if *no* feed could be fetched.

**Does it work with proxies or need login?** Neither. Only public, unauthenticated endpoints are used.

### Legal

This Actor reads public product pages and Apple's iTunes Lookup API. It obeys robots.txt at run time (including redirect destinations), uses polite limits and a descriptive User-Agent, and never logs in or bypasses access controls. Review text and developer replies are capped at 400 characters with a source link. Reviewer names, profile links and avatars are excluded. See [LEGAL.md](./LEGAL.md) for checked paths, terms and excluded sources.

# Actor input Schema

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

One entry per app. Accepts App Store URLs (https://apps.apple.com/us/app/whatsapp-messenger/id310633997), Google Play URLs (https://play.google.com/store/apps/details?id=com.whatsapp), the short forms 'appstore:310633997', 'appstore:net.whatsapp.WhatsApp' (bundle id) and 'play:com.whatsapp', a bare numeric App Store id, or a bare package name.

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

Two-letter country codes, e.g. us, gb, de, in, br. The App Store keeps separate reviews per storefront, so each country is fetched separately. For Google Play only the first country is sent as the store region. Clear the list to use each App Store URL's own storefront.

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

Language tags for Google Play reviews, e.g. en, de, pt-br, hi. Google Play reviews are fetched per language. App Store reviews are not filtered by language (Apple's page does not say which language a review is in).

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

Keep only reviews with at least this many stars (1-5).

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

Keep only reviews with at most this many stars (1-5). Set minimum = maximum to get a single star value, e.g. only 1-star reviews.

## `postedWithinDays` (type: `integer`):

Keep only reviews posted within this many days. Leave empty for any date.

## `onlyNew` (type: `boolean`):

Output only reviews that were not seen in previous runs with the same apps, countries, languages and filters. Ideal for a daily schedule: the first run outputs everything, later runs only what is new. History is kept in a named key-value store. Every record carries changeStatus ('new' or 'existing') and firstSeenAt either way.

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

Upper limit of matching reviews per app and country (App Store) or language (Google Play). Public product pages contain a limited featured sample, with no paging or newest-first guarantee.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997",
    "https://play.google.com/store/apps/details?id=com.whatsapp"
  ],
  "countries": [
    "us"
  ],
  "languages": [
    "en"
  ],
  "minRating": 1,
  "maxRating": 5,
  "onlyNew": false,
  "maxReviewsPerApp": 200
}
```

# Actor output Schema

## `results` (type: `string`):

The reviews found in this run, one item per record (the default dataset).

## `summary` (type: `string`):

Totals, per-input status and any errors for this run (the OUTPUT record).

# 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/whatsapp-messenger/id310633997",
        "https://play.google.com/store/apps/details?id=com.whatsapp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataforge-tools/app-reviews").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/whatsapp-messenger/id310633997",
        "https://play.google.com/store/apps/details?id=com.whatsapp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("dataforge-tools/app-reviews").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/whatsapp-messenger/id310633997",
    "https://play.google.com/store/apps/details?id=com.whatsapp"
  ]
}' |
apify call dataforge-tools/app-reviews --silent --output-dataset

```

## MCP server setup

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

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/Zlmlh8hoDU2DsfTtP/builds/qY4H9OvrJhKQ31d5W/openapi.json
