# App Store Reviews Scraper (Multi-Country) (`cleanfeed-data/app-store-reviews`) Actor

Scrape Apple App Store reviews for any app across many countries in one run. Deduplicated JSON with rating, app version, sentiment and topic tags, star/date/keyword filters, app metadata and new-reviews-only alerts. Official Apple feeds only.

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

## Pricing

from $0.30 / 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 Store Reviews Scraper (Multi-Country)

**Scrape and export Apple App Store reviews for any app, across many countries in one run, as clean, deduplicated JSON, with app metadata, filters, sentiment and topic tags, and a new-reviews-only monitoring mode.**

Give it apps (App Store URLs, numeric IDs, bundle IDs, or just names) and a list of countries. The Actor finds each app and reads its reviews from **Apple's official public customer-review feed** in every storefront you choose. It merges everything into one consistent dataset. Data comes only from Apple's public feeds and the iTunes Search/Lookup API. No login, no proxies, no browser, no HTML scraping.

👉 **Try it in one click:** the input is prefilled with Duolingo and Spotify in the US and UK and capped at 100 reviews, so a first run takes under a minute and costs about $0.04.

### What does this App Store reviews scraper do?

| | This Actor |
|---|---|
| Many countries in one run | ✅ `["us","gb","de","jp"]`, `major` (~48 markets) or `all` (~175 storefronts) |
| Find apps by URL, ID, bundle ID **or name** | ✅ |
| Most recent **and** most helpful, deduplicated | ✅ up to ~1,000 reviews per app per country |
| Star, date and keyword filters | ✅ applied before you pay |
| New-reviews-only monitoring mode | ✅ for scheduled runs and alerts |
| Sentiment + topic tags | ✅ bug-crash, pricing-subscription, ads, login, performance, ui-ux, and more |
| App metadata (rating, count, price, version, release notes…) per country | ✅ optional |
| Privacy option | ✅ anonymize reviewer names and IDs |
| Fair `maxResults` split across apps and countries | ✅ |

### Use cases

- **Product and UX teams:** watch what users say after each release (`appVersion`, `update-regression` and `bug-crash` tags) and send 1–2★ reviews to Slack every morning.
- **Competitor research:** compare the complaints and praise of several competing apps across markets.
- **App marketing and ASO:** track rating averages and counts by country, and find the keywords users actually use.
- **Customer support:** catch billing, login and data-loss complaints quickly.
- **Data science and LLM pipelines:** clean, deduplicated, labelled review text in many languages.

### How to scrape App Store reviews

1. Click **Try for free** (or **Start**) and open the **Input** tab.
2. In **Apps**, paste App Store links (`https://apps.apple.com/us/app/…/id570060128`), numeric IDs, bundle IDs, or app names.
3. In **Countries**, list the storefronts you want (`us`, `gb`, `de`…), or `major` / `all`.
4. Optionally filter by stars (e.g. max 2 for complaints only), dates or keywords.
5. Set **Max results** to 0 for everything, or keep a cap while you test.
6. Click **Start**, then download the reviews as JSON, CSV or Excel, or use the API.
7. For alerts, turn on **Only new reviews** and add a schedule; connect Slack or email with Apify integrations.

### Input

| Field | Description |
|---|---|
| `apps` | **Required.** One per line: `https://apps.apple.com/us/app/…/id570060128`, `570060128`, `com.spotify.client`, or a name such as `Headspace`. Optional prefixes: `id:`, `bundle:`, `search:`. |
| `countries` | Storefront codes such as `us`, `gb`, `de`, `jp`, `br`, or `major` / `all`. Default `["us"]`. |
| `sortBy` | `mostrecent` (default), `mosthelpful`, or `both` (merged and deduplicated). |
| `maxReviewsPerAppCountry` | Up to 500 per sort order (1,000 with `both`). |
| `maxResults` | Total cap (0 = unlimited), split fairly across app/country pairs. |
| `minRating` / `maxRating` | Star range, e.g. 1–2 for complaints only. |
| `dateFrom` / `dateTo` / `reviewedWithinDays` | Date window. With `mostrecent`, paging stops once reviews are older than the window. |
| `keywords`, `onlyMatchingKeywords` | Tag reviews with your keywords, or keep only reviews that match one. |
| `enrich` | Sentiment + topic tags (default on). |
| `includeAppDetails` | One extra item per app and country with full app metadata. |
| `anonymizeUsers` | Drop reviewer names and URLs, and hash reviewer IDs. |
| `onlyNew`, `stateStoreName` | Monitoring mode (see below). |

#### Example input

```json
{
  "apps": ["https://apps.apple.com/us/app/duolingo-language-lessons/id570060128", "com.spotify.client", "Headspace"],
  "countries": ["us", "gb", "de", "fr", "jp"],
  "sortBy": "mostrecent",
  "maxRating": 2,
  "reviewedWithinDays": 30,
  "keywords": ["subscription", "crash", "ads"],
  "onlyNew": true
}
```

### Output

Each review is one dataset item:

```json
{
  "itemType": "review",
  "appId": "570060128",
  "appName": "Duolingo: Learn Languages",
  "bundleId": "com.duolingo.DuolingoMobile",
  "developer": "Duolingo",
  "country": "us",
  "reviewId": "14726701682",
  "title": "Keeps crashing since the update",
  "text": "Since the latest update the app crashes every time I open a lesson…",
  "rating": 2,
  "appVersion": "7.146.1",
  "userName": "iKlava",
  "userId": "352431021",
  "userUrl": "https://itunes.apple.com/us/reviews/id352431021",
  "date": "2026-10-29T18:34:33.000Z",
  "sortSource": "mostrecent",
  "helpfulVotes": 0,
  "totalVotes": 0,
  "appUrl": "https://apps.apple.com/us/app/id570060128",
  "sentiment": "negative",
  "sentimentScore": -0.5,
  "topics": ["bug-crash", "update-regression"],
  "matchedKeywords": ["crash"],
  "isNew": true,
  "firstSeenAt": "2026-10-31T04:49:27.183Z",
  "scrapedAt": "2026-10-31T04:49:27.183Z"
}
```

With `includeAppDetails`, you also get one `itemType: "app"` item per app and country. It includes `averageRating`, `ratingCount`, the same two for the current version, `price`, `currency`, `version`, `currentVersionReleaseDate`, `releaseNotes`, `description`, `genres`, `developer`, `contentRating`, `languages`, `fileSizeBytes`, `iconUrl` and screenshot URLs.

The Output tab has a **Reviews** view and an **App details** view. Each run also stores `APPS` (how every input was resolved) and `RUN_SUMMARY` (counts and errors per app and country) in the run's key-value store.

### Monitoring new reviews (`onlyNew`)

Schedule the Actor (e.g. hourly or daily) with `onlyNew: true`. The first run returns everything that matches (the baseline). After that, each run returns only reviews it hasn't returned before. With `mostrecent`, it stops paging as soon as it reaches reviews it has already seen, so monitoring runs are quick. Give each separately monitored task its own `stateStoreName`.

### Limits you should know

- Apple's public feed exposes at most **500 reviews per app, per country, per sort order** (10 pages of 50). Using `both` and many countries goes well beyond that per app. Reviews older than that window can't be reached through Apple's public feed.
- The feed includes written reviews only, not star-only ratings. For the total rating count and average, use `includeAppDetails`.
- Sentiment is a heuristic: the star rating blended with a multilingual-light word lexicon. Topic tags are English keyword rules.

### How much does it cost to scrape App Store reviews?

| Event | Price |
|---|---|
| Actor start (once per run at 512 MB) | **$0.005** |
| Review returned | **$0.0003** ($0.30 per 1,000 reviews) |
| App details item (only with `includeAppDetails`) | **$0.001** |

Filters are applied **before** charging, so you never pay for reviews you filtered out. Examples: 1,000 reviews costs $0.305. A daily monitor of 5 apps in 10 countries with about 40 new reviews a day costs about **$0.017/day**. Set `maxResults` or a maximum cost per run to cap spend.

### FAQ

**Is this legal?** The Actor only reads data that Apple publishes openly for anyone: the public customer-reviews RSS/JSON feed (`itunes.apple.com/{country}/rss/customerreviews/…`) and the documented iTunes Search/Lookup API. It doesn't scrape apps.apple.com pages, log in, or get around any protection, and it spaces its requests (Search/Lookup at ~20 calls per minute, per Apple's guidance). Reviews contain public nicknames written by users. If you store or process them, you are the data controller. Use `anonymizeUsers` if you don't need reviewer identities, and follow GDPR/CCPA and Apple's terms when you republish content.

**Why do some countries return fewer reviews?** Reviews are written per storefront. Small markets often have fewer than 500.

**An app name resolved to the wrong app.** Use the App Store URL or numeric ID for an exact match. Name search returns Apple's top result, and obvious mismatches are rejected.

**Can I get more than 500 reviews per app?** Apple's public feed stops at 500 per app, country and sort order. Use `sortBy: both` and several countries to collect more distinct reviews per app.

**Can I export App Store reviews to CSV or Google Sheets?** Yes. Every dataset can be downloaded as CSV, Excel or JSON, or sent to Google Sheets with Apify's integration.

**How many reviews can I get on the free plan?** Apify's free plan includes $5 of monthly usage, which covers more than 15,000 reviews with this Actor.

**Can I get Google Play reviews?** Not with this Actor. It covers the Apple App Store only.

### More scrapers from cleanfeed-data

All of our Actors use only official, documented public APIs and feeds, charge only for results, and have a new-items-only monitoring mode.

| Actor | What it does |
|---|---|
| [Greenhouse, Lever & Ashby Jobs Scraper](https://apify.com/cleanfeed-data/multi-ats-jobs) | Every open job from company career pages on 8 ATSs, with new-job alerts |
| [Bluesky Scraper](https://apify.com/cleanfeed-data/bluesky-data) | Bluesky posts, profiles, reply threads and followers via the official public API |

### Legal note

This Actor uses only Apple's publicly available customer-review feeds and the public iTunes Search/Lookup API. It isn't affiliated with or endorsed by Apple Inc. App Store and Apple are trademarks of Apple Inc. You are responsible for how you use the data you collect.

# Actor input Schema

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

One app per line: an App Store URL (https://apps.apple.com/us/app/…/id570060128), a numeric app ID (570060128), a bundle ID (com.spotify.client), or an app name to search for ("Duolingo"). Prefixes <code>id:</code>, <code>bundle:</code> and <code>search:</code> force the interpretation.

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

Two-letter App Store storefront codes, e.g. <code>us</code>, <code>gb</code>, <code>de</code>, <code>jp</code>, <code>br</code>. Reviews are separate per country. Use <code>major</code> for ~48 large markets or <code>all</code> for ~175 storefronts.

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

Which Apple feed to read. Apple exposes up to 500 reviews per storefront per sort order. <b>Both</b> combines the 500 most recent and the 500 most helpful, deduplicated (up to ~1,000 per storefront).

## `maxReviewsPerAppCountry` (type: `integer`):

Up to 500 per sort order (1,000 with "Both"). Lower values fetch fewer pages.

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

Total cap across all apps and countries (0 = unlimited). Prefilled to 100 so your first test run is fast and cheap; set 0 for everything. Split fairly: each app/country gets an equal share and unused share from small ones goes to the others. Also caps your cost ($0.30 per 1,000 reviews).

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

Keep reviews with at least this many stars.

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

Keep reviews with at most this many stars (e.g. 2 for complaints only).

## `dateFrom` (type: `string`):

Only reviews on or after this date (YYYY-MM-DD). With "Most recent" the Actor stops paging once it passes this date.

## `dateTo` (type: `string`):

Only reviews on or before this date (YYYY-MM-DD).

## `reviewedWithinDays` (type: `integer`):

Only reviews from the last N days (0 = off). Combined with "Reviews from" the later date wins.

## `keywords` (type: `array`):

Case-insensitive words/phrases to look for in the title and text. Matches are listed in <code>matchedKeywords</code>.

## `onlyMatchingKeywords` (type: `boolean`):

Drop reviews that match none of the keywords above.

## `enrich` (type: `boolean`):

Adds <code>sentiment</code> (positive/neutral/negative), <code>sentimentScore</code> (-1..1, star rating blended with a word lexicon) and <code>topics</code> (bug-crash, performance, login-account, ads, pricing-subscription, ui-ux, customer-support, feature-request, update-regression, privacy, notifications, sync-data-loss). Heuristic and English-centric.

## `includeAppDetails` (type: `boolean`):

Adds one dataset item per app and country (itemType "app") with rating average and count, price, version, release notes, genres, developer, screenshots and more. Charged as a separate app-result event.

## `anonymizeUsers` (type: `boolean`):

Remove reviewer nicknames and profile links, and replace the reviewer ID with a one-way hash (still lets you count repeat reviewers). Recommended if you store data about EU/UK users.

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

Monitoring mode for scheduled runs: output only reviews not returned by a previous run with the same state store. The first run returns everything matching (baseline). Every item has <code>isNew</code> either way.

## `stateStoreName` (type: `string`):

Named key-value store that remembers seen reviews between runs. Use a different name per saved task you monitor separately.

## `searchResultsPerTerm` (type: `integer`):

When an app is given by name, how many top search results to include (1 = best match only).

## `maxConcurrency` (type: `integer`):

How many app/storefront pairs are processed at once. Requests are still spaced by the delays below.

## `requestDelayMs` (type: `integer`):

Polite spacing for Apple's review RSS feed.

## `searchDelayMs` (type: `integer`):

Apple documents roughly 20 Search API calls per minute; the default 3000 ms respects that.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128",
    "com.spotify.client",
    "324684580",
    "Headspace"
  ],
  "countries": [
    "us",
    "gb"
  ],
  "sortBy": "mostrecent",
  "maxReviewsPerAppCountry": 500,
  "maxResults": 100,
  "minRating": 1,
  "maxRating": 5,
  "reviewedWithinDays": 0,
  "onlyMatchingKeywords": false,
  "enrich": true,
  "includeAppDetails": false,
  "anonymizeUsers": false,
  "onlyNew": false,
  "stateStoreName": "app-store-reviews-state",
  "searchResultsPerTerm": 1,
  "maxConcurrency": 4,
  "requestDelayMs": 250,
  "searchDelayMs": 3000
}
```

# Actor output Schema

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

All items from this run, opened in the Reviews view.

## `appDetails` (type: `string`):

App metadata rows (itemType = app), present when includeAppDetails is on.

## `apps` (type: `string`):

JSON record: how each input resolved (app ID, name, bundle ID, developer, rating) plus unresolved inputs.

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

JSON record with per app/storefront fetched, unique, matched, new, quota and output counts, errors, and HTTP stats.

# 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/duolingo-language-lessons/id570060128",
        "com.spotify.client"
    ],
    "countries": [
        "us",
        "gb"
    ],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanfeed-data/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/duolingo-language-lessons/id570060128",
        "com.spotify.client",
    ],
    "countries": [
        "us",
        "gb",
    ],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("cleanfeed-data/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/duolingo-language-lessons/id570060128",
    "com.spotify.client"
  ],
  "countries": [
    "us",
    "gb"
  ],
  "maxResults": 100
}' |
apify call cleanfeed-data/app-store-reviews --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cleanfeed-data/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/cKOTy69RyHCqif9Ox/builds/cVQ9F5FhxsRVN2A9K/openapi.json
