# App Store Reviews Scraper · All Countries, Deduped · No Login (`thequietstack/app-store-reviews`) Actor

Apple App Store reviews scraper. Pulls reviews from every country storefront in one run instead of the 500 one country allows, deduplicated by review ID. Input by App Store URL, app ID or search term. Filter by stars, date and app version. No login required.

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

## Pricing

from $0.50 / 1,000 scraped 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 Store Reviews Scraper · All Countries, Deduped · No Login

Scrape Apple App Store reviews for any iPhone/iPad app, from **one, several or all ~175 country storefronts in a single run**. Every review becomes one clean, flat row, deduplicated by review ID. No login, no API key.

### Why this scraper

- **More than 500 reviews per app.** Apple only exposes the 500 newest reviews *per country*. This Actor merges the storefronts you pick (or all of them) into one dataset, with a `country` field on every row. Measured on 23 Sep 2026: Notion had **at least 3,285 reviews since 1 Aug 2026 across 140 storefronts**, vs. 500 in the US feed alone.
- **No silently missing pages.** Apple's review feed returns an empty page (HTTP 200, zero reviews) for between 1 in 8 and 1 in 3 pages in our tests, and keeps doing so for the same URL. Scrapers that don't notice this lose up to 30% of the data without any error. This Actor detects empty pages and re-requests them through alternate feed URLs. Measured: 6 of 6 test storefronts returned the full 500 reviews, instead of 300–350.
- **Input the way you have it.** App Store URL, numeric app ID, or just a search term (`"notion"`), resolved via Apple's official search.
- **Filters that save money.** Star rating (e.g. only 1–2★ complaints), date range, and app version. Filtered-out reviews are **never charged**. Paging stops as soon as reviews are older than `sinceDate`.
- **A hard limit you can trust.** `maxReviews` stops the run cleanly. You are never charged for more reviews than that, and never for duplicates.

### Input example

```json
{
    "apps": ["https://apps.apple.com/us/app/instagram/id389801252", "284882215"],
    "searchTerms": ["notion"],
    "countries": ["us", "gb", "de"],
    "ratings": [1, 2],
    "sinceDate": "2026-08-01",
    "maxReviews": 5000
}
```

Set `"allCountries": true` to scrape every storefront. Countries where the app has no reviews cost nothing.

### Output example

```json
{
    "reviewId": "14565234989",
    "appId": "284882215",
    "appName": "Facebook",
    "country": "gb",
    "rating": 5,
    "title": "Las Dalias Hotel Tenerife.",
    "text": "Fantastic Hotel never fails to disappoint , our Favourite place 5 star .",
    "version": "579.0.0",
    "author": "Ourliam",
    "authorUrl": "https://itunes.apple.com/gb/reviews/id426371395",
    "date": "2026-09-18T18:09:30.000Z",
    "voteCount": 0,
    "voteSum": 0
}
```

| Field | Meaning |
|---|---|
| `rating` | Stars, 1–5 |
| `title`, `text` | Review headline and body |
| `version` | App version the reviewer was using |
| `date` | Review date, ISO 8601 UTC |
| `country` | Storefront the review was posted in |
| `voteCount` / `voteSum` | Total votes on the review / "helpful" votes, as Apple reports them |

A run summary (`SUMMARY` in the key-value store) lists reviews per country, duplicates removed, how many reviews each filter dropped, countries without reviews, and any country that failed.

### Honest limits

- **Apple exposes at most the 500 newest reviews per country** (10 pages × 50). No scraper can get older reviews from this feed. More countries = more reviews; a single country never goes past 500.
- `sortBy: mostHelpful` returns a different set of (up to) 500 reviews per country, not additional history.
- Star-only ratings without text are not in the feed. Apple's rating count (e.g. 25M ratings) is much larger than the number of written reviews.
- Apple rate-limits single IP addresses on large runs. The default Apify proxy rotates the IP on each retry; countries that still fail are listed in the summary, not hidden.
- Apple App Store only. For Android, use the twin actor [Google Play Reviews Scraper](https://apify.com/thequietstack/google-play-reviews) — similar flat output, all 49 review languages in one run, developer replies included.

### Pricing (pay per event)

- Actor start: small flat fee per run
- Per scraped review: charged only for reviews written to the dataset — no charge for duplicates, filtered-out reviews, or empty countries

# Actor input Schema

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

App Store URLs (apps.apple.com/.../id389801252) or bare numeric app IDs.

## `searchTerms` (type: `array`):

App names or keywords. Resolved with Apple's official search in the first selected country.

## `appsPerSearchTerm` (type: `integer`):

How many top search results to scrape for each search term.

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

Two-letter country codes (us, gb, de, fr, jp ...). Apple returns at most 500 reviews per country, so more countries means more reviews.

## `allCountries` (type: `boolean`):

Ignore the list above and scrape every App Store storefront. Countries without reviews cost nothing.

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

Most recent is required for an efficient date filter.

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

e.g. \[1, 2] for complaints only. Empty = all ratings. Filtered-out reviews are not charged.

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

Paging stops as soon as reviews get older than this date.

## `untilDate` (type: `string`):

Optional upper bound for the review date.

## `versions` (type: `array`):

Exact version ("448.0.0") or a prefix ("448" matches 448.x). Empty = all versions.

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

Stop an app after this many reviews. Empty = no per-app limit.

## `maxReviews` (type: `integer`):

The run stops cleanly at this number. You are never charged for more reviews than this.

## `concurrency` (type: `integer`):

How many countries are fetched at the same time.

## `proxyConfiguration` (type: `object`):

Apple throttles single IPs on large runs (HTTP 403). The default Apify datacenter proxy rotates the IP on every retry.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/instagram/id389801252",
    "284882215"
  ],
  "searchTerms": [
    "notion",
    "duolingo"
  ],
  "appsPerSearchTerm": 1,
  "countries": [
    "us",
    "gb",
    "ca",
    "au"
  ],
  "allCountries": false,
  "sortBy": "mostRecent",
  "ratings": [
    1,
    2
  ],
  "versions": [
    "448"
  ],
  "maxReviews": 5000,
  "concurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `reviews` (type: `string`):

One flat row per review: rating, title, text, version, author, ISO date, country, app.

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

Reviews per country, duplicates removed, filtered-out counts, countries without reviews, recovered empty pages.

# 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/instagram/id389801252"
    ],
    "countries": [
        "us",
        "gb",
        "ca",
        "au"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("thequietstack/app-store-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/instagram/id389801252"],
    "countries": [
        "us",
        "gb",
        "ca",
        "au",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("thequietstack/app-store-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/instagram/id389801252"
  ],
  "countries": [
    "us",
    "gb",
    "ca",
    "au"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call thequietstack/app-store-reviews --silent --output-dataset

```

## MCP server setup

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