# App Store Reviews Monitor (iOS, Multi-Country) (`brightish_ascent/app-store-reviews-monitor`) Actor

Unofficial; not affiliated with Apple. Monitor Apple App Store reviews for many apps and countries via Apple's public reviews feed. Star filter, rating summary per feed, only-new and edited reviews for scheduled runs.

- **URL**: https://apify.com/brightish_ascent/app-store-reviews-monitor.md
- **Developed by:** [Michael Rice](https://apify.com/brightish_ascent) (community)
- **Categories:** Business, Developer tools, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 review rows

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 Reviews Monitor (iOS, Multi-Country)

Unofficial tool. Not affiliated with or endorsed by Apple Inc. App Store is a trademark of Apple Inc.

Collect and monitor Apple App Store reviews for one or many apps across many countries. The data comes from **Apple's own public customer-reviews feed** and the **iTunes Lookup API**. No login and no scraping of web pages. Schedule it to get only **new** and **edited** reviews, with a rating summary for each app and country.

### What you get

- **One row per review:** star rating, title, text, app version, date, vote counts, and the app's review page URL. The reviewer nickname is included only if you turn on `includeReviewerName`.
- **Many apps × many storefronts in one run:** up to 40 app/country feeds (for example 4 apps × 10 countries).
- **Star filter:** for example, only 1–2 star reviews for complaint alerts.
- **Per-feed statistics in `OUTPUT`:** average rating, share of 1–2 star reviews, and newest/oldest review date over the reviews read. App info from the Lookup API: name, developer, current version, average rating and rating count.
- **Monitor mode** (`onlyNewSinceLastRun`):
  - The first run is a baseline.
  - Later runs return only `new` reviews and `changed` reviews. A review counts as changed when the user edited the rating, title or text, or it now shows a different app version. Changed rows carry a `previous` object with the old values.
  - Because the feed is newest-first, paging stops as soon as a full page is already known, which keeps scheduled runs to a request or two per feed.
- **Exact `truncated` flag.** In monitor mode, rows that didn't fit are delivered on the next run.

### Input example

```json
{
  "apps": ["389801252", "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580"],
  "countries": ["us", "gb", "de"],
  "ratings": ["1", "2"],
  "onlyNewSinceLastRun": true,
  "maxItems": 500
}
```

### Output example (full row)

```json
{
  "reviewKey": "389801252:us:14689718900",
  "appId": "389801252", "appName": "Instagram", "country": "us", "reviewId": "14689718900",
  "rating": 1, "title": "Terrible", "content": "Really bad", "appVersion": "452.0.0",
  "updatedAt": "2026-10-20T15:16:23.000Z", "voteSum": 0, "voteCount": 0,
  "url": "https://apps.apple.com/us/app/id389801252?see-all=reviews",
  "updateType": "new", "previous": null, "changeHash": "104bd19a3363c7c6",
  "source": "Apple App Store customer reviews RSS"
}
```

### Limits (from Apple)

- **500 reviews per feed:** Apple's feed exposes only the **500 most recent reviews** per app and storefront (10 pages of 50). Older reviews can't be retrieved this way. A feed that reaches this limit shows `feedCapReached: true` in `OUTPUT`. It doesn't set `truncated`, which only covers rows we could have delivered but didn't.
- **Written reviews only:** star-only ratings without text are not in the feed.
- **Unknown storefront codes:** Apple rejects them, and they are listed in `feedsFailed` with a hint.
- **Request pacing:** at least 350 ms between requests, with retries on 429/5xx errors.

### Pricing

Pay per event: a small charge per Actor start plus one charge per review row (`review`). The run's maximum total charge is respected.

### FAQ

**How often should I schedule it?** For busy apps, daily is plenty. Apple's feed only holds the latest 500 reviews per country, so very popular apps should run at least daily so nothing scrolls out of reach.

**Where's my monitor state?** In a named key-value store, `app-store-reviews-monitor-state`, in your own account. There's one record per input set.

Support: digitalpromohub.support@gmail.com

# Actor input Schema

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

App Store app IDs (e.g. 389801252) or apps.apple.com app URLs.

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

2-letter App Store storefront codes, e.g. "us", "gb", "de". Default \["us"]. apps x countries max 40 per run.

## `maxPagesPerFeed` (type: `integer`):

Apple serves up to 10 pages of 50 most-recent reviews (500) per app and country. Lower this for faster, cheaper runs.

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

Keep only reviews with these star ratings (e.g. 1 and 2 for complaint monitoring). Empty = all. Feed statistics in OUTPUT always use every review read.

## `includeReviewerName` (type: `boolean`):

Add the reviewer's public App Store nickname to each row. Off by default (data minimisation).

## `onlyNewSinceLastRun` (type: `boolean`):

Remember delivered reviews per input set in a named key-value store in your account. Later runs return only new reviews and edited reviews (rating, title, text or app version changed, with previous values). First run = baseline (all reviews read, as new).

## `maxItems` (type: `integer`):

Maximum dataset rows per run (each row is one charged event). In monitor mode, rows over the limit are delivered on the next run.

## `outputFormat` (type: `string`):

full = all fields; compact = reviewKey, updateType, appName, country, rating, title, content, appVersion, updatedAt, previous, changeHash.

## Actor input object example

```json
{
  "apps": [
    "389801252"
  ],
  "countries": [
    "us"
  ],
  "maxPagesPerFeed": 10,
  "includeReviewerName": false,
  "onlyNewSinceLastRun": false,
  "maxItems": 500,
  "outputFormat": "full"
}
```

# Actor output Schema

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

Default dataset. full rows: reviewKey (appId:country:reviewId), appId, appName, country, reviewId, rating (1-5), title, content, appVersion, updatedAt, voteSum, voteCount, reviewerName (only with includeReviewerName), url (app's App Store reviews page), updateType (monitor mode: new | changed; otherwise null), previous (changed rows: rating, title, content, appVersion, updatedAt), changeHash, source. compact rows: reviewKey, updateType, appName, country, rating, title, content, appVersion, updatedAt, previous, changeHash.

## `runSummary` (type: `string`):

Fields: ok, count, chargeEvent, chargedCount, chargeLimitReached (only when hit), outputFormat, matchingReviews, apps (appId, appName, developer, currentVersion, averageUserRating, userRatingCount from the iTunes Lookup API), feeds (appId, appName, country, pagesRead, stopReason, feedCapReached, reviewsRead, avgRating, lowRatingShare = share of 1-2 star reviews, newestReviewAt, oldestReviewAt; computed over all reviews read this run), feedsFailed, appsNotFound, truncated (true only when more reviews exist than were delivered, incl. maxPagesPerFeed cut-offs; Apple's own 500-review limit is reported as feedCapReached instead), truncationNote, message, warnings, onlyNewSinceLastRun, watermarkStore, watermarkKey, baselineRun, newCount, changedCount (counted from pushed rows), skippedUnchanged, feedsStoppedEarly, emptyPageRetries (empty feed pages re-requested via an alternate Apple URL, because Apple's CDN sometimes caches an empty page), emptyPageRecovered (of those, how many then returned reviews), apiRequests (total, retries, throttled, capped, minIntervalMs, perRunCap, userAgent), disclaimer. On failure: ok false, error.

# 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": [
        "389801252"
    ],
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("brightish_ascent/app-store-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": ["389801252"],
    "countries": ["us"],
}

# Run the Actor and wait for it to finish
run = client.actor("brightish_ascent/app-store-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": [
    "389801252"
  ],
  "countries": [
    "us"
  ]
}' |
apify call brightish_ascent/app-store-reviews-monitor --silent --output-dataset

```

## MCP server setup

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