# App Store Reviews Scraper: All Countries in One Run (`mochiboo/app-store-reviews-all-countries`) Actor

App Store reviews from every country in one run: all 175 Apple storefronts merged, with country, version, date, votes and app details. Date and star filters for daily monitoring.

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

## Pricing

from $0.20 / 1,000 results

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 Scraper: All Countries in One Run

Get App Store reviews from every country in one run. Apple shows each country only its own reviews, so a global app's reviews are split across up to 175 storefronts. This actor reads them all, merges them into one dataset and tags every review with its country, plus the app's name, developer, average rating and rating count. From each country it reads up to the 500 most recent reviews (the most Apple's feed serves), within the "Max reviews in total" cap (1,000 by default, so that a first run stays small; see "How Max reviews in total works" below).

Price: Pay per result, at the rate shown in the pricing panel of this page, plus a very small per-run start fee. You are charged only for the results delivered.

### What you get

- **Every storefront in one run.** Leave "Countries" empty for all 175 App Store storefronts, or list the ones you want (us, gb, de, jp ...). Several apps per run are supported (App Store IDs or apps.apple.com links).
- **Built for monitoring.** "Only reviews since" stops reading a country as soon as reviews get older than your date, and minimum and maximum stars let you pull just the 1 to 2 star reviews. The actor keeps no memory between runs, and an Apify schedule repeats the same input, so a date that never changes re-reads (and re-charges) the same window every time. To fetch only what is new, start the run from your own scheduler or workflow (for example through the Apify API) with a fresh date, or change the date of a saved task each day. Apple's feed runs behind (see "Limits"), so ask for a window with some overlap, for example the last three days, and remove duplicates by `appId` and `reviewId`.
- **Context on every row.** Country, stars, title, text, date (as given by Apple and in UTC), version reviewed, reviewer display name, helpful and total votes, plus the app's name, developer and current version, and the app's average rating and rating count (see "Limits" for which storefront those two come from).

### Input

| field | meaning | default |
|---|---|---|
| Apps | App Store IDs (1232780281) or apps.apple.com / itunes.apple.com links | Notion (1232780281) |
| Countries | two-letter codes; empty = all 175 | all |
| Only reviews since | YYYY-MM-DD (UTC) | none |
| Minimum / maximum stars | 1 to 5 | 1 / 5 |
| Max reviews per country | 1 to 500 | 500 |
| Max reviews in total | 1 to 200000; a hard cap, and what you pay for | 1000 |

Starting the actor without changing anything runs these defaults: Notion, every storefront, up to 1,000 reviews.

### How "Max reviews in total" works

- **It is a hard cap.** A run never writes more reviews than this number, so it is also the most you can be charged for reviews in one run.
- **The default is 1,000**, so that a first run stays small. For a popular app that is only a part of what the stores hold: raise it to read more.
- **A cap smaller than what the stores hold is shared fairly across the stores**, not spent on the first countries in the list. The actor first reads page 1 of the stores in list order to see which of them have reviews (when the cap is smaller than the number of stores that have reviews, it stops as soon as it has found as many stores with reviews as the cap, so only the first stores in the list are read). Then every store with reviews gets about the same number of its most recent reviews (the cap divided by the number of stores). A store that has fewer reviews than its share hands the rest to the others, which are read further, and so on until the cap is used. Stores are never read in full while others get nothing.
- **A run that the cap stopped is not complete.** `RUN_SUMMARY` then says `terminationReason: "max_results"` and `complete: false`. `storesCutShortByCap` counts the stores that were read but still had more reviews when the cap was reached. `storesNotReached` lists, as two-letter codes, the stores that no review was written for although they were not read to the end: stores the run did not get to (the first stores in the list are served first) and stores that still had reviews when the cap was reached. A store that was not read at all may or may not have reviews. Raise the cap, or list fewer countries.
- With several apps, the share is per app and store pair, and a code is listed in `storesNotReached` when at least one of your apps was not read in that store.

### How long a run takes

The actor is polite to Apple: it sends **one request at a time, about one every 0.7 seconds (0.65 to 0.75 seconds between the starts of two requests, or Apple's answer time if that is longer; roughly 1.4 requests a second) for the whole run**, the app lookup and the first pages included. That is on purpose: on 2026-10-20 Apple stopped three runs that read deep at 3 to 5 requests a second with HTTP 403 (see "Limits").

A run makes one request per 50 apps for the app details, one for the first page of every store, and one for every further page of 50 reviews that it reads. So the time is in proportion to the requests, about 85 requests a minute:

- **The default input** (one app, all 175 storefronts, cap 1,000) makes about 180 requests and takes about two minutes (measured on 2026-10-20: 182 requests in 127 seconds).
- A run that reads **every page of every store of a very large app** (up to 10 pages in each of up to 175 stores) makes roughly 1,000 to 1,750 requests, which at this pace is 12 to 20 minutes, whatever the number of rows. Four large apps read page by page in one run: 935 requests in 11 minutes (2026-10-20, on a day when Apple's feed returned empty pages for most stores, so a healthy feed means more pages and more time).
- A run with two or three countries takes seconds.

Give a run that reads many pages a time limit of two hours (Run options, Timeout; or `timeout=7200` in the API call). If the time limit comes first, the run stops cleanly: `terminationReason: "time_budget"`, `complete: false`, and everything read so far is in the dataset.

### Output

One row per review. Fields: `reviewId, appId, appName, developer, appAverageRating, appRatingCount, appCurrentVersion, country, countryName, rating, title, text, reviewedVersion, date, dateUtc, userName, helpfulVotes, totalVotes, page, scrapedAt`.

The reviewer's profile link is not included. Email addresses and phone numbers in common formats that appear in review titles, review text or reviewer display names are replaced with `[email redacted]` and `[phone redacted]` (best effort: unusual formats, such as a bare run of digits, can slip through).

Rows are written while the run goes and are not in date order across stores or pages: sort by `country` and `dateUtc` if you need an order.

### Limits you should know

- Apple's public feed serves at most the **500 most recent reviews per country** (10 pages of 50). Older reviews are not available from it.
- Developer replies are not included.
- **`appAverageRating` and `appRatingCount` are the US storefront's figures on every row**, whatever the row's country (they come from Apple's lookup for the US store). They are not that country's rating.
- Apple's feed runs behind: on 2026-10-20 at 02:43 UTC the newest review across all 140 stores of a large app was from 2026-10-18 16:50 UTC, 34 hours old. "Only reviews since" with today's date can therefore legitimately return nothing.
- Apple's feed is **not always consistent**. On 2026-10-19 a page-1 check of a large app found reviews in 94 of the 175 stores; on 2026-10-20 it found them in 140, and later the same day, at 03:54 UTC, in only 21. An empty first page cannot be told from a store that has no reviews, so such a run still ends as `finished` with `complete: true`. When **more than 30% of the requested stores return nothing**, `RUN_SUMMARY` says so in `note` ("Apple's feed returned nothing for N of M stores; this happens on some days, run again later.") and `storesEmpty` gives the number; the run log shows the same warning. An app that has reviews in only a few countries triggers the note as well, and then it is simply true. Otherwise run again later.
- Now and then Apple also serves one page empty in the middle of a store while the pages after it are fine. The actor skips such a page and reads on (about 50 reviews of that store are then missing from the run); two empty pages in a row end that store. It does not retry an empty page. `RUN_SUMMARY` counts them in `emptyPagesSkipped` and `storesCutShortByEmptyPage`. On a degraded day this is common: one run on 2026-10-20 at 06:31 UTC skipped 66 pages and ended 29 stores early, and still finished with `complete: true` and the note described above. An empty page at the very end is not counted: on 2026-10-20 Apple's last-page link for Notion in Italy said 7, page 6 held the last reviews and page 7 was empty, so nothing was missing.
- If Apple refuses a request (for example with HTTP 403), the run stops at once with `terminationReason: "blocked"`, keeps the reviews it has already read, lists the stores it did not get to in `storesNotReached`, and does not try again or work around the refusal. The run is then marked failed in the Apify Console, with the rows it read in the dataset.
- A very large run (many apps, all countries, 500 each) may stop early when it nears the run time limit; `RUN_SUMMARY` then says `terminationReason: "time_budget"` and `complete: false`, and everything fetched until then is in the dataset. A run with a very large number of app and store pairs (the first page of every pair is held in memory) can stop with `memory_limit`; split such a job over several runs.
- Review text is written by public reviewers and is returned as published (apart from the redaction above).

### Run summary

The key-value store record `RUN_SUMMARY` holds:

- `complete`: true when the run was not stopped early (by the cap, the time limit, a block or failed requests). It does not cover empty pages or empty stores that Apple served by mistake: check `note`, `storesEmpty` and `emptyPagesSkipped`.
- `terminationReason`: `finished`, `max_results`, `charge_limit`, `time_budget`, `blocked`, `request_failed`, `invalid_input` or `memory_limit`.
- `note`: null, or the plain sentence about an empty Apple feed described under "Limits".
- `rowsPushed`, `requests`, `failedRequests`, `apps`, `reviewsSeen`, `duplicates`, `error`, `seconds`.
- `storesWithReviews` (app and store pairs whose first page had reviews), `storesEmpty` (pairs whose first page had none), `storesAtTenPageCap` (pairs with the full 10 pages), `storesCutShortByCap`, `storesNotReached`, `emptyPagesSkipped`, `storesCutShortByEmptyPage`.

### Measured on the platform

Measured on 2026-10-20 at 1,024 MB. Apple's feed was in a poor state for most of that day (see "Limits"), so the numbers about reviews depend on the day; the numbers about requests and time do not.

- **Pace:** 182, 265 and 935 requests took 127, 185 and 656 seconds, which is 1.43 requests a second each time, whatever the answers held. Apple did not refuse any of the 1,386 requests of the four runs made at this pace.
- **Default input** (Notion, every storefront, cap 1,000) with a healthy feed, earlier the same day: 1,000 reviews from 140 countries, between 1 and 11 per country (GB 10, UAE 10, US 11); `complete` was false because 74 countries still had reviews when the cap was reached. With the poor feed at 06:13 UTC: 310 reviews from 11 countries in 128 seconds, `storesEmpty` 164 and the note.
- **Every page of every store** (Instagram, 2-star reviews only, so that the dataset stays small; every page is still requested): 265 requests in 185 seconds, 205 reviews from 39 countries, 28 stores with the full 10 pages, 66 empty pages skipped.
- **Four large apps, every page** (Instagram, Spotify, WhatsApp, Facebook, 2-star reviews only): 935 requests in 656 seconds, 453 reviews, 85 stores with the full 10 pages, 201 empty pages skipped. Both runs ended as `finished`, with the note.
- 3 countries, 100 reviews each (earlier the same day, healthy feed): 300 reviews, 100 per country. All countries, 20 per country: 1,661 reviews from 140 countries, all 20 output fields filled in 100% of the rows.
- "Max reviews in total" 300: 300 reviews from all 140 countries, 1 to 3 each. "Max reviews in total" 100, smaller than the number of countries with reviews: 100 reviews, one each from the first 100 countries (in list order) that have reviews, and `storesNotReached` listed the 51 countries that were not read.
- Peak memory 34 to 40 MiB in the runs above. **Recommended memory: 1,024 MB.**

Keywords: app store reviews, app store reviews scraper, ios app reviews, apple app reviews all countries, app review monitoring, app store sentiment, aso, app reviews export.

# Actor input Schema

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

App Store IDs (e.g. 1232780281) or App Store URLs (e.g. https://apps.apple.com/us/app/notion-notes-tasks-ai/id1232780281).

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

Two-letter App Store country codes (us, gb, de, jp ...). Leave empty for all 175 storefronts. The actor sends about 85 requests a minute, one at a time, so reading the first page of every storefront takes about two minutes and reading every page of every storefront of a very large app 12 to 20 minutes.

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

YYYY-MM-DD (UTC). Stops reading a country once reviews are older than this. For monitoring, move the date forward between runs; Apple's feed runs about a day and a half behind, so leave some overlap and remove duplicates by app and review ID.

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

Keep reviews with at least this many stars.

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

Keep reviews with at most this many stars. Use 1 to 2 for complaint alerts.

## `maxReviewsPerCountry` (type: `integer`):

Apple serves at most 500 recent reviews per country (10 pages of 50). Use a smaller number to take only the newest ones from each store.

## `maxResults` (type: `integer`):

Hard cap on reviews for the whole run, and the number of reviews you pay for. If the stores hold more than this, the cap is shared fairly: every store with reviews gets about the same number of its most recent reviews, so no country is left out while others are read in full. The run summary lists any store that was not read.

## Actor input object example

```json
{
  "apps": [
    "1232780281"
  ],
  "countries": [],
  "sinceDate": "",
  "minRating": 1,
  "maxRating": 5,
  "maxReviewsPerCountry": 500,
  "maxResults": 1000
}
```

# Actor output Schema

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

One row per review: country, rating, title, text, date, version, votes and app details.

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

What the run wrote and how it ended (the RUN_SUMMARY record in the default key-value store).

# 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": [
        "1232780281"
    ],
    "maxResults": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("mochiboo/app-store-reviews-all-countries").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": ["1232780281"],
    "maxResults": 1000,
}

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

```

## MCP server setup

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

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/sF4StfAgr4tEi6kOH/builds/2RCc8q7TIfx2qpcKI/openapi.json
