# App Store Reviews Scraper | Ratings Across All Countries (`abotapi/app-store-reviews-scraper`) Actor

Collect Apple App Store reviews and ratings for any app across 150+ country storefronts. Get rating, title, body, author, date, app version, and country in clean JSON. Search by app name or paste app URLs; optional app metadata enrichment.

- **URL**: https://apify.com/abotapi/app-store-reviews-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Developer tools, Lead generation, Social media
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## 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 Store Reviews Scraper

Collect Apple App Store reviews and ratings for any app, across country storefronts. Give it an app name or an App Store link and it returns a clean, one-row-per-review dataset: the star rating, review title and body, author, date, the app version reviewed, and the storefront country. Reviews are storefront-specific, so you can sweep many countries to gather the full picture for a single app.

### Why this scraper

- Reviews are the primary output: rating, title, body, author, date, app version, and country on every row.
- Two ways in: search by app name, or paste one or more App Store app links.
- Multi-country sweep: pass a list of storefronts, or use "all" for a broad built-in set of major markets.
- Sort by most recent or most helpful.
- Filter by minimum and maximum star rating.
- Optional app-metadata enrichment: developer, average rating, rating count, genre, icon, price, and release notes attached to each review.
- 25+ output fields, more than typical alternatives, at a predictable per-result price.
- Incremental mode for scheduled/recurring runs: track the same search over time and get back only what's NEW or changed, not the whole dataset again.

### Data you get

> Sample shape, values are illustrative placeholders, not from a live review.

| Field | Example |
|---|---|
| reviewId | 14000000000 |
| appId | 389801252 |
| appName | Sample App |
| country | us |
| rating | 5 |
| title | Sample review title |
| body | Full review text appears here. |
| author | Reviewer Name |
| authorId | 1500000000 |
| authorUri | https://itunes.apple.com/us/reviews/id1500000000 |
| reviewUrl | https://itunes.apple.com/us/review?id=389801252\&type=Purple%20Software |
| appVersion | 1.0.0 |
| reviewDate | 2026-01-01T00:00:00-07:00 |
| voteSum | 0 |
| voteCount | 0 |
| contentType | Application |

With enrichment enabled, each review also carries: appSellerName, appAverageRating, appRatingCount, appAverageRatingCurrentVersion, appRatingCountCurrentVersion, appPrimaryGenre, appContentRating, appBundleId, appIconUrl, appStoreUrl, appPrice, appCurrency, appMinimumOsVersion, appCurrentVersion, appCurrentVersionReleaseDate, appReleaseNotes.

### How to use

Search by app name, US storefront:

```json
{
  "mode": "search",
  "queries": ["Instagram", "Spotify"],
  "appsPerQuery": 1,
  "countries": ["us"],
  "sortBy": "mostRecent",
  "maxItems": 100
}
```

Sweep several countries for one app via its link:

```json
{
  "mode": "url",
  "urls": ["https://apps.apple.com/us/app/instagram/id389801252"],
  "countries": ["us", "gb", "de", "jp"],
  "maxItems": 500
}
```

Only high-rated reviews, with app details attached:

```json
{
  "mode": "url",
  "urls": ["https://apps.apple.com/us/app/instagram/id389801252"],
  "countries": ["us"],
  "minRating": 4,
  "fetchDetails": true,
  "maxItems": 200
}
```

Multiple app links at once:

```json
{
  "mode": "url",
  "urls": [
    "https://apps.apple.com/us/app/instagram/id389801252",
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580"
  ],
  "countries": ["us"],
  "maxItems": 200
}
```

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| mode | string | search | "search" (by app name) or "url" (paste app links). |
| queries | array | \["Instagram"] | App names or keywords (search mode). |
| appsPerQuery | integer | 1 | Top matching apps to take per search term. |
| urls | array | (example) | App Store app links (url mode). |
| countries | array | \["us"] | Storefront codes to collect from. Empty uses the link's storefront or us. "all" sweeps major markets. |
| sortBy | string | mostRecent | "mostRecent" or "mostHelpful". |
| minRating | integer | (none) | Keep reviews at or above this star rating. |
| maxRating | integer | (none) | Keep reviews at or below this star rating. |
| fetchDetails | boolean | false | Attach app metadata to each review. |
| maxItems | integer | 20 | Total review cap. 0 means no limit. |
| maxPages | integer | (none) | Pages per country (50 reviews per page). Leave empty to walk every review page; the storefront naturally stops paginating once it runs out. |
| resumeFromRunId | string | (none) | Previous run ID (or dataset ID) from this actor. Reviews already collected there are skipped, so the run returns only new ones. |
| incrementalMode | boolean | false | For a search you run on a schedule: remember reviews already seen and return only what changed. See "Incremental mode" below. |
| stateKey | string | (none) | Name this monitoring campaign to run several tracked searches independently. Leave empty to derive it automatically from your search settings. |
| emitUnchanged | boolean | false | Also return reviews with changeType = UNCHANGED. Off by default — turning this on bills every scanned review, every run. |
| emitExpired | boolean | false | Also return a row per previously-tracked review no longer found, once a run completes a full scan. Off by default. |
| proxy | object | Apify proxy | Connection settings. |

### Output example

> Sample shape, values are illustrative placeholders.

```json
{
  "reviewId": "14000000000",
  "appId": "389801252",
  "appName": "Sample App",
  "country": "us",
  "rating": 5,
  "title": "Sample review title",
  "body": "Full review text appears here.",
  "author": "Reviewer Name",
  "authorId": "1500000000",
  "authorUri": "https://itunes.apple.com/us/reviews/id1500000000",
  "reviewUrl": "https://itunes.apple.com/us/review?id=389801252&type=Purple%20Software",
  "appVersion": "1.0.0",
  "reviewDate": "2026-01-01T00:00:00-07:00",
  "voteSum": 0,
  "voteCount": 0,
  "contentType": "Application"
}
```

### Incremental mode (recurring/scheduled monitoring)

If you run this actor on a schedule against the same search, turn on `incrementalMode` to get back only what changed since the last run instead of the whole dataset again:

```json
{
  "mode": "url",
  "urls": ["https://apps.apple.com/us/app/instagram/id389801252"],
  "countries": ["us"],
  "maxItems": 200,
  "incrementalMode": true
}
```

Each review gets a `changeType`: `NEW` (never seen before), `UPDATED` (a real field changed — see `changedFields`), `UNCHANGED` (nothing changed; suppressed from the output unless `emitUnchanged` is on), or `REAPPEARED` (was previously marked EXPIRED and is back). Set `emitExpired: true` to also get an `EXPIRED` row for a tracked review that disappeared — but only on a run that completes a full, uncapped scan (a low `maxItems`, a `maxPages` limit, `resumeFromRunId`, or a mid-run migration/resurrect all skip EXPIRED detection for that run rather than risk a false positive).

**What counts as "changed":** rating, title, body, author, review date, and the reviewed app version are real review content — a change to any of these is a real `UPDATED`. `voteSum` and `voteCount` are excluded: other users' helpful-votes accrue on an old review indefinitely, so treating them as a content change would mark almost every historical review UPDATED on every run. When `fetchDetails` is on, the attached app-metadata fields (seller, average rating, rating count, genre, price, current version, release notes, etc.) are also excluded — they describe the current state of the *app*, not the review, and drift independently every time the app itself updates.

**State size:** the saved baseline stores a per-field content fingerprint for each tracked review (never the review's actual title/body text) to stay well under the platform's key-value-store size limit even for large monitoring campaigns. Measured at 50,000 tracked reviews with realistic review-length text: ~99 bytes/review compressed (~4.9 MB total), leaving roughly 1.8x headroom before the actor's own safety guard (which refuses to save — logging a clear error — rather than exceed the platform limit; split a very large campaign across a few `stateKey` values if you hit it). One consequence: an `EXPIRED` row can only report the review's id, app, country, and timestamps — its original title/body/rating are not retained in the incremental baseline and are not reproduced on the tombstone row.

State is keyed automatically by a hash of your search settings (mode, queries/URLs, countries, sort, rating filters, and `fetchDetails`) — two different filter setups never share a baseline, and raising `maxItems`/`maxPages` never starts a new one. Set `stateKey` to name a campaign explicitly, e.g. to track several searches independently or intentionally share one baseline across otherwise-different runs.

### Send results into your apps (MCP connectors)

You can optionally pipe results into the apps you already use through Model Context Protocol (MCP) connectors. Authorize a connector under Apify, Settings, API & Integrations, then select it in the input. For Notion, set a parent page URL and each review is written as a page. Other connectors receive a best-effort write.

The connector receives a condensed, human-readable summary of each review (a heading plus the key fields and body text), not the full JSON. The complete record always stays in the Apify dataset. Leave the connector field empty to skip this step; it never changes the dataset output.

### Plan requirement

Runs on any Apify plan. For very large multi-country sweeps, a proxy with more exit rotation can help.

# Actor input Schema

## `mode` (type: `string`):

How to select apps. 'search' resolves app names to apps via the App Store; 'url' takes App Store app links directly.

## `queries` (type: `array`):

App names or keywords to look up (for example 'Instagram', 'Spotify'). Each term resolves to the top matching app(s) and its reviews are collected. Ignored in URL mode.

## `appsPerQuery` (type: `integer`):

How many top matching apps to take for each search term. Use 1 for the single best match. Ignored in URL mode.

## `urls` (type: `array`):

App Store app links, for example https://apps.apple.com/us/app/instagram/id389801252 . Multiple URLs supported. The app id is read from the /id{digits} part; the storefront in the link is used only if you leave Countries empty.

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

ISO 2-letter storefront codes to collect reviews from (for example us, gb, de, jp). Each country is a separate review stream. Leave empty to use the storefront from each URL (URL mode) or 'us' (search mode). Use 'all' to sweep a broad built-in set of major storefronts.

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

Order reviews are returned in per country.

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

Keep only reviews with at least this star rating (1 to 5). Leave empty for all.

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

Keep only reviews with at most this star rating (1 to 5). Leave empty for all.

## `fetchDetails` (type: `boolean`):

Also attach app metadata (developer, average rating, rating count, genre, icon, price, release notes) to each review. Adds an extra lookup per app.

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

Maximum number of reviews to collect in total. 0 means no limit (stops at Max pages per country across all countries). Predictable, keeps the first run cheap.

## `maxPages` (type: `integer`):

Optional bound on review pages walked per country (50 reviews per page). Leave empty to walk every review page; the storefront naturally stops paginating once it runs out. The run also stops at Max reviews.

## `resumeFromRunId` (type: `string`):

Optional: paste a previous run ID (or dataset ID) from this actor. Reviews already collected there are skipped, so this run only returns new ones. For repeated scheduled monitoring of the SAME search, use Incremental mode below instead. Leave empty for a normal run.

## `incrementalMode` (type: `boolean`):

For a search you run on a schedule: remember the reviews already seen and return only what changed (NEW/UPDATED/REAPPEARED, plus EXPIRED if enabled) instead of pasting a Resume from run ID every time. Off by default so a first-time run behaves exactly as before.

## `stateKey` (type: `string`):

Optional: name this monitoring campaign so you can run several tracked searches independently. Leave empty to derive the state automatically from your search settings (mode, queries/URLs, countries, sort, rating filters, enrichment) — two different filter setups never share a baseline. Ignored when Incremental mode is off.

## `emitUnchanged` (type: `boolean`):

Also return reviews that have not changed since the last run (changeType = UNCHANGED). Off by default so a recurring run returns only what changed — turning this on returns and bills for every scanned review, every run. Ignored when Incremental mode is off.

## `emitExpired` (type: `boolean`):

Also return a row for each previously-tracked review that no longer appears (changeType = EXPIRED) once a run completes a full, uncapped scan. Off by default — turning this on returns and bills an extra row per review that disappeared. An EXPIRED row carries only the review id and timestamps, not its original text (kept out of the saved state to keep it small). Ignored when Incremental mode is off.

## `proxy` (type: `object`):

Connection settings. The default works on any Apify plan.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write. Leave empty to skip; never changes the dataset output. Supported: Notion, Linear, Airtable, Apify.

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "Instagram"
  ],
  "appsPerQuery": 1,
  "urls": [
    "https://apps.apple.com/us/app/instagram/id389801252"
  ],
  "countries": [
    "us"
  ],
  "sortBy": "mostRecent",
  "fetchDetails": false,
  "maxItems": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "queries": [
        "Instagram"
    ],
    "urls": [
        "https://apps.apple.com/us/app/instagram/id389801252"
    ],
    "countries": [
        "us"
    ],
    "maxPages": 0,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/app-store-reviews-scraper").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 = {
    "mode": "search",
    "queries": ["Instagram"],
    "urls": ["https://apps.apple.com/us/app/instagram/id389801252"],
    "countries": ["us"],
    "maxPages": 0,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/app-store-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "queries": [
    "Instagram"
  ],
  "urls": [
    "https://apps.apple.com/us/app/instagram/id389801252"
  ],
  "countries": [
    "us"
  ],
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/app-store-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/app-store-reviews-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/PbtWYNiZGuhWHlEMu/builds/jaBdtfcEscedhDEpW/openapi.json
