# App Store Reviews Scraper — Deep, Multi-Country, Dev Replies (`scrapersdelight/appstore-reviews-scraper`) Actor

Apple App Store reviews from Apple's own review API: rating, title, body, author, date and developer reply, one row per review. Goes past the 500-review RSS cap — verified 150,000 reviews deep on one app — and sweeps many countries in one run. $0.10 per 1,000 reviews. No login, no token.

- **URL**: https://apify.com/scrapersdelight/appstore-reviews-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Marketing, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 88.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.10 / 1,000 per review returneds

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?

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 — Deep, Multi-Country, With Developer Replies

Turn any app's Apple App Store reviews into clean rows, straight from Apple's own review
API. One row per review: **rating, title, body, userName, date, country, appId, appName,
isEdited, developerResponseBody, developerResponseDate, position** — plus **appVersion,
voteCount, voteSum** and **authorUrl** in recent mode. Filter by star rating or
developer-reply presence, sweep as many storefront countries as you want in one run.
No login. No API token. No browser.

**Scope: this Actor paginates the whole review corpus, not the first feed page** —
verified live on 2026-08-15 by walking one app (WhatsApp, US storefront) to offset
**150,000** (page 7,500) and still getting rows back. Apple's public RSS feed — which is
what many review scrapers actually read — hard-caps at **500 reviews per app per country**
(page 11 returns zero entries, measured). If the reviews you need are older or deeper than
the newest 500, the cap is the difference between a sample and the corpus.

```json
{
  "apps": ["https://apps.apple.com/us/app/whatsapp-messenger/id310633997"],
  "countries": ["us", "gb", "au"],
  "mode": "deep",
  "maxItems": 600,
  "maxReviewsPerAppCountry": 200
}
```

Click **Try for free** and hit **Start** — that block is literally the input the Actor
ships with. A run on those shipped defaults returned **600 reviews (exactly 200 per
country) in 30 requests with 0 duplicates and 0 failures**, rating/title/body/author/date
all 100% filled, for **$0.06**.

***

### The wedge: the corpus, not the sample — in every country at once

Three things this Actor does that the RSS-cap scrapers cannot:

- **Deep pagination.** Apple's review API serves the full written-review corpus, most
  helpful first. Measured: 150,000 reviews deep on one app x country and still going; a
  2,000-review contiguous walk of Slack US came back **2,000 unique, 0 duplicates, in
  1m15s**. The 500-review feed cap does not apply here.
- **Multi-country in one run.** Reviews are storefront-specific — WhatsApp's US reviews
  and its German reviews are two disjoint corpora. `countries` takes any list of
  storefront codes and every app is crossed with every country, each pair walked, counted
  and capped separately. Verified live on nine storefronts (us, gb, de, jp, fr, br, in,
  au, kr).
- **Developer replies.** Deep mode carries the publisher's public reply where one exists
  (`developerResponseBody` + `developerResponseDate`) — measured **31.75% of 2,000 Slack
  US reviews**, 2.2% on WhatsApp, 0% on Duolingo's first 100. The
  `onlyWithDeveloperResponse` filter turns any competitor's support behaviour into a
  dataset: what they answer, what they ignore, how fast.

As of 2026-08-15 the Apify Store has 12+ actors on "app store reviews" — this lane has
real demand and real competition. Judge this page on its measured numbers: the depth, the
0.00% duplicate rate, the per-mode field-fill tables below, and a price at the floor of
the lane ($0.10 per 1,000).

***

### Read this before you buy rows

Six things that would otherwise turn into a refund request.

1. **Deep mode is most-helpful order, not newest-first.** That is the order Apple's API
   serves, and its `sort` parameter is silently ignored (measured: identical pages with
   and without it). If you need newest-first, that is `mode: "recent"` — Apple's
   date-sorted feed — which caps at 500 per app x country. Deep = complete,
   helpfulness-ranked; recent = fresh, capped. Pick per job.
2. **`appVersion`, `voteCount`, `voteSum` and `authorUrl` are `null` in deep mode —
   always.** Apple's review API simply does not return them; only the RSS route (recent
   mode) carries them. Measured fill: 0.0% in deep mode, 100% in recent mode. If you need
   "which version was this complaint written against", use recent mode.
3. **Reviews are not ratings.** WhatsApp shows 18.4M US *ratings* (star taps); its
   written-review corpus in the US storefront ended between offset 150,000 and 200,000
   when measured. Expect written reviews to be a low single-digit percentage of the
   rating count Apple advertises on the store page.
4. **No reviewer contact data exists — at all.** `userName` is a public nickname
   ("Can't Share Album"), never a real name, and Apple publishes no email, no phone, no
   location for reviewers. If a lead list is what you need, this is the wrong source.
5. **`recent` mode runs out at 500.** That is Apple's feed cap, not a bug and not this
   Actor's limit — the run logs "feed exhausted" and moves on rather than pretending
   there is more.
6. **A country with no reviews is a warning, not an error.** Apps are not sold in every
   storefront and small storefronts often have zero written reviews. The run reports
   "no reviews in this storefront" per app x country and keeps going; you are never
   billed for an empty pair.

***

### What you get

One record shape in both modes — fields a mode cannot know are `null`, so your CSV
columns never move. Dates are ISO-8601 UTC. `rating` is an integer 1–5.

| Group | Fields | Example |
|---|---|---|
| **Identity** | `reviewId`, `appId`, `appName`, `country` | `11781634604` · `618783545` · `Slack` · `us` |
| **Review** | `rating`, `title`, `body`, `date`, `isEdited` | `3` · "New Users Say it is difficult" · `2024-09-30T14:31:24.000Z` |
| **Author** | `userName`, `authorUrl` (recent mode) | `Can't Share Album` · `https://itunes.apple.com/us/reviews/id…` |
| **Developer reply** (deep mode) | `developerResponseBody`, `developerResponseDate` | "Hi there, We'd be interested in hearing more…" |
| **Version & votes** (recent mode) | `appVersion`, `voteCount`, `voteSum` | `26.08.10` · `4` · `3` |
| **Provenance** | `position`, `order`, `source`, `appUrl`, `scrapedAt` | `354` · `mostHelpful` · `app-store-api` |

`position` is the review's 1-based rank within its app x country walk — in deep mode that
is Apple's helpfulness rank, a signal in its own right.

***

### Field fill — measured, per mode

**Deep mode, 2,000-review contiguous walk of Slack US (2026-08-15):**

| Field | Fill | Notes |
|---|---|---|
| `reviewId` / `appId` / `appName` / `country` | 100% | |
| `rating` / `title` / `body` / `userName` / `date` | 100% | |
| `isEdited` | 100% | `true` on 3.0% of rows |
| `position` / `order` / `source` / `appUrl` / `scrapedAt` | 100% | |
| **`developerResponseBody` / `developerResponseDate`** | **31.75%** | publisher-dependent: 2.2% on WhatsApp, 0% on Duolingo's first 100 |
| **`appVersion` / `voteCount` / `voteSum` / `authorUrl`** | **0%** | Apple's API omits them — recent mode only |

**Recent mode, 300 reviews (Slack, US + DE, 2026-08-15):**

| Field | Fill | Notes |
|---|---|---|
| `reviewId` … `date` (all core fields) | 100% | |
| **`appVersion`** | **100%** | the version the review was written against |
| **`voteCount` / `voteSum`** | **100%** | helpful votes; `0` is a real zero |
| **`authorUrl`** | **100%** | the reviewer's public iTunes profile URL |
| **`isEdited` / `developerResponseBody` / `developerResponseDate`** | **0%** | the feed omits them — deep mode only |

The ratings in a deep walk are the real distribution, not a happy-path sample — the 2,000
Slack rows split 1★ 319 · 2★ 263 · 3★ 312 · 4★ 294 · 5★ 812. **Every run prints its own
measured fill in the log.**

***

### How to run it

#### 1. The default — one app, three countries

The shipped input above. 600 reviews across us/gb/au for $0.06.

#### 2. A competitor's whole review corpus

```json
{
  "apps": ["https://apps.apple.com/us/app/slack/id618783545"],
  "countries": ["us"],
  "mode": "deep",
  "maxItems": 100000,
  "maxReviewsPerAppCountry": 0
}
```

Walks until Apple's corpus ends (the log says "corpus exhausted" with the true count) or
`maxItems` is hit, whichever comes first. `maxItems` is your hard cost cap: 100,000 = $10.

#### 3. Complaint mining — 1–2★ only

```json
{
  "apps": ["310633997"],
  "countries": ["us", "gb", "ca", "au"],
  "mode": "deep",
  "minRating": 1,
  "maxRating": 2,
  "maxItems": 5000
}
```

Filtered-out reviews are fetched but **never billed** — you pay for the 1–2★ rows you
receive, not the 5★ rows discarded on the way.

#### 4. Support-behaviour audit — only reviews the developer answered

```json
{
  "apps": ["618783545"],
  "countries": ["us"],
  "mode": "deep",
  "onlyWithDeveloperResponse": true,
  "maxItems": 2000
}
```

#### 5. Version-tagged fresh reviews, on a schedule

```json
{
  "apps": ["310633997"],
  "countries": ["us", "gb", "de", "fr", "jp", "br", "in"],
  "mode": "recent",
  "maxItems": 3500
}
```

Newest-first with `appVersion` and vote counts on every row — the release-quality
monitor. Save it as a **Task** (Console → *Save as Task*), attach a **Schedule** (`0 7 * * *`
for a daily pull), and diff on `reviewId` downstream. The dataset is available over the
REST API and the standard integrations (Zapier, Make, n8n, webhooks, MCP). From the API:

```bash
curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~appstore-reviews-scraper/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"apps":["310633997"],"countries":["us"],"mode":"recent","maxItems":500}'
```

#### Countries

Any 2-letter storefront code Apple runs (~175 of them): `us gb ca au de fr it es nl jp kr
cn hk tw sg in br mx ar cl co pe ru tr sa ae il za ng eg pl cz se no dk fi pt gr…` —
nine of them verified live in testing (us, gb, de, jp, fr, br, in, au, kr). An unknown
code is reported per app x country ("Unknown storefront 'zz'") and costs you nothing.

***

### Sample row

A real row from an actual run, captured 2026-08-15 (deep mode, Slack US):

```json
{
  "reviewId": "11781634604",
  "appId": "618783545",
  "appName": "Slack",
  "country": "us",
  "rating": 3,
  "title": "New Users Say it is difficult",
  "body": "I have used many business communication tools. What I hear often from new users and trying to get people to integrate into and use slack as opposed to calling or Texting on a phone, is how difficult it is to use and it is not intuitive. Since it is under the control of an administrator, there is little ability for people to organize in a way that they can get the information they need quickly.",
  "userName": "Can't Share Album",
  "authorUrl": null,
  "date": "2024-09-30T14:31:24.000Z",
  "isEdited": false,
  "appVersion": null,
  "voteCount": null,
  "voteSum": null,
  "developerResponseBody": "Hi there, \nWe'd be interested in hearing more about any frictions new users are having when using Slack. When you have a moment, please navigate to your app's \"You\" tab - \"Preferences\" - \"Send Feedback\" to flag this with our support team and provide some logs we'll need to investigate. We'll be happy to help!",
  "developerResponseDate": "2024-10-02T09:28:51.000Z",
  "position": 354,
  "order": "mostHelpful",
  "source": "app-store-api",
  "appUrl": "https://apps.apple.com/us/app/id618783545",
  "scrapedAt": "2026-08-15T04:19:21.260Z"
}
```

Fields people misread:

- `position: 354` — Apple's helpfulness rank, not a date rank. In recent mode it is the
  recency rank instead, and `order` says which one you got.
- `voteCount`/`voteSum` are `null` here because this is a deep-mode row, not because
  nobody voted. In recent mode they are filled, and `0` there is a real zero.
- `isEdited: false` means the reviewer never revised the review; `null` (recent mode)
  means Apple's feed doesn't say.

***

### Input

Fields in the order the Console shows them.

| Field | Type | Default | What it does |
|---|---|---|---|
| **🎯 What to scrape** | | | |
| `apps` | string list | WhatsApp URL | App Store URLs or bare numeric ids. A wrong id is reported and skipped before anything is billed. Left empty, the WhatsApp sample runs. |
| `countries` | string list | `["us","gb","au"]` | Storefront codes, one per entry. Every app is crossed with every country. |
| `mode` | select | `deep` | `deep` = whole corpus, most-helpful order, developer replies. `recent` = newest 500 per pair, appVersion + votes. |
| **🎚️ Filters** (applied before billing) | | | |
| `minRating` / `maxRating` | integer | `0` (off) | Star-rating window, 1–5. `minRating: 1, maxRating: 2` = complaints only. |
| `onlyWithDeveloperResponse` | boolean | `false` | Deep mode only — recent mode has no replies and the run says so rather than returning nothing. |
| **📊 Output & cost** | | | |
| `maxItems` | integer | `600` | **Your hard cost cap.** 600 = $0.06, 10,000 = $1, 100,000 = $10. Counts unique delivered reviews only. |
| `maxReviewsPerAppCountry` | integer | `200` shipped / `0` = off | Per-corpus cap so one deep storefront cannot eat the whole budget before the others get a turn. |
| **⚙️ Advanced** | | | |
| `maxConcurrency` | integer | `3` | App x country pairs walked in parallel, max 5. Pages within a pair are sequential by nature. |
| `proxyConfiguration` | proxy | Apify datacenter | On by default — see the measured ladder below. Residential is not needed. |

***

### Pricing

**$0.0001 per review returned — $0.10 per 1,000.** Charged on the `review-scraped`
event. No actor-start fee, no monthly fee from this Actor. That is the floor of this
lane, deliberately: reviews are high-volume rows and are priced like it.

| Run | Reviews | Cost |
|---|---|---|
| The shipped default (WhatsApp, 3 countries) | 600 | **$0.06** |
| A full recent-mode sweep, 7 countries | 3,500 | **$0.35** |
| A mid-size app's whole US corpus | 20,000 | **$2.00** |
| A whale's whole US corpus | 100,000 | **$10.00** |

- **You are charged for rows delivered.** Duplicates are dropped before billing (measured
  duplicate rate: 0.00% — and the run *verifies* it, see below). Reviews removed by your
  rating/reply filters are fetched but never charged.
- **Rows are charged as they are pushed**, so a budget cap gives you whole rows and a
  clean stop, never a half-billed dataset.
- Every run ends with a **billing integrity check**: the dataset row count must equal the
  charged-review count exactly, or the run errors instead of over- or under-billing.

***

### Honest limits

- **No reviewer emails, phones, or real names — ever.** Apple publishes nicknames only.
  This is a voice-of-customer source, not a lead-gen source.
- **Deep mode cannot give you `appVersion` or helpful votes** (0.0% measured — Apple's
  API omits them), and **recent mode cannot go past 500 per app x country or carry
  developer replies** (Apple's feed, Apple's rules). The two modes exist because neither
  Apple route carries everything; the record shape is identical in both so you can union
  the datasets downstream.
- **Deep order is Apple's helpfulness ranking and cannot be changed** — the API ignores
  its own sort parameter (measured). There is no "oldest first" on any public Apple
  route.
- **No date-range filter.** Deep mode is not date-ordered, so a server-side date slice
  does not exist; filter on `date` downstream. In recent mode, newest-first plus
  `maxItems` is the practical date window.
- **Written reviews are a small fraction of the advertised rating count** (see gotcha 3).
  An app showing "50K ratings" may have only a few thousand written reviews. The log
  prints each app's worldwide rating count at start so you can calibrate expectations.
- **Rate limits are real.** Apple 429-throttles busy IPs. The Actor absorbs this
  (measured 100% page success through the default proxy with fresh-session retries), but
  a 100k-review corpus still takes on the order of an hour, not minutes: measured
  throughput ~27 rows/s single pair, 45.7 rows/s across pairs.

***

### How it works, and what it cost to make reliable

Deep mode reads Apple's own review API (`amp-api`) through the same-origin route the App
Store web front-end itself uses — the route that needs no token (the dedicated API hosts
demand a bearer JWT that Apple no longer embeds in the page; this Actor needs no JWT at
all). Recent mode reads Apple's public customer-reviews feed. App ids are validated
against Apple's lookup API before anything is walked, so a typo is caught in second one
with a clear message, not after a paid crawl.

**Transport ladder, measured 2026-08-15, retries off:**

| Rung | Result |
|---|---|
| Direct, one IP, conc 3 @ 16.5 req/s | 13/20 = 65% (7x HTTP 429 — rate limit, no bot wall) |
| Apify datacenter, one pinned session | 18/20 = 90% |
| Apify datacenter, fresh session per request | 24/30 = 80% |
| Apify RESIDENTIAL | 18/20 = 90% |

There is **no Cloudflare, no CAPTCHA, no challenge page** — only a per-IP token-bucket
rate limit. So the shipped transport is the cheap datacenter rung plus the thing the
measurements actually justify: a per-worker pinned session for rate-budget locality, and
**every retry on a fresh exit IP** with exponential backoff. Sustained load, as shipped:

| Test | Result |
|---|---|
| 50 contiguous pages, conc 3, through Apify datacenter | **50/50 = 100%**, 1,000 rows, 45.7 rows/s |
| 2,000-review contiguous walk (100 pages), in-actor | **100/100 pages**, 0 failures, 1m15s |
| Shipped-default run (30 requests, 3 countries) | **30/30**, 0 failures |

Concurrency is capped at 5 on purpose: Apple rate-limits per IP, and past ~3 workers more
concurrency mostly buys more 429-retries, not more rows.

***

### Duplicates — measured, then verified every run

| Walk | Rows | Duplicates |
|---|---|---|
| 20 contiguous pages, direct (x2 runs) | 800 | **0** |
| 50 contiguous pages through Apify proxy | 1,000 | **0** |
| 2,000-review in-actor walk, Slack US | 2,000 | **0** |
| Shipped default, 3 countries | 600 | **0** |

Apple's offset pagination is stable — 0.00% overlap on every contiguous walk measured.
The Actor still dedupes run-wide on `appId:country:reviewId` before billing, logs the
count it dropped, and then **asserts that the charged-event count equals the delivered
row count exactly** — and cross-checks the dataset row count — before the run may
succeed. That assertion is not decorative: during development it caught a concurrency
bug that pushed 1,620 rows for 600 counted ones, and it now guards every run you pay
for.

Across runs, dedupe on `reviewId` downstream — it is Apple's own stable id for the
review.

***

### When something goes wrong

Every diagnosis lands in the run's **status message** and the log, and a run that
returns nothing bills **$0**. A misconfigured input ends the run *Succeeded with the
explanation attached* rather than red-failing — you get the same sentence either way, and
your account keeps a clean run history.

- **A wrong app id** → validated against Apple's lookup API before anything is walked:
  *"App id 999999999999 does not exist on the App Store (Apple's lookup API returned 0
  results) — skipping it."* The other apps in the list still run; if **none** of the ids
  exist, the run stops there and bills nothing.
- **A wrong country code** → shape errors (`"usa,gb"` in one field) are dropped with the
  fix spelled out; a well-formed but unreal code (`zz`) is reported per pair while the
  real countries deliver.
- **0 rows overall** → the status message lists every app x country pair with its
  individual outcome, plus how many reviews your filters discarded, so a filter problem
  never masquerades as an Apple problem. Nothing is billed.
- **Repeated request failures mid-corpus** → the pair stops, the log says exactly how
  many reviews were delivered up to the stop point, and the run warns. Delivered rows
  are complete up to that point — re-run to continue. Apple's wall is a per-IP **rate
  limit**, not a block, so a re-run a few minutes later clears it.
- **The run time limit** → the Actor stops fetching before the platform would kill it,
  pushes everything already collected, and says so: *"Stopped early to stay inside this
  run's time limit — N reviews were delivered and charged."* A deep multi-country sweep
  is never lost to a timeout.
- **No input at all** (a bare `{}` from the API, MCP or an agent) → the Actor runs its
  documented WhatsApp sample so you always get real rows back, and warns that it did.
- **Billing mismatch** → the integrity check errors the run rather than billing wrong.
  This is the one condition that still fails loudly, deliberately.
- A storefront with no reviews, or a recent-mode feed ending at 500, is a **warning /
  info, not an error** — the log says which and why.

***

### Who buys this

- **App developers watching competitors** — the full corpus, their developer-reply
  behaviour included, not the newest 500.
- **ASO agencies** — complaint mining by star window across every storefront a client
  sells in, in one run.
- **VoC / review-mining and NLP pipelines** — clean UTF-8 JSON with stable ids,
  ISO dates and a fixed schema in both modes; `maxItems` makes corpus cost exactly
  predictable ($0.10 per 1,000).
- **Churn and release analysts** — recent mode's `appVersion` on 100% of rows ties
  sentiment to releases; a scheduled Task turns it into a time series.
- **Support teams** — `onlyWithDeveloperResponse` benchmarks reply rates and reply
  latency (`developerResponseDate` minus `date`) against anyone in the store.

***

### FAQ

**Does this need an account, login, cookies or an API key?**
No. Both routes are public Apple endpoints; the Actor holds no credentials at all.

**Can I get every review an app has ever received?**
Deep mode walks until Apple's corpus ends and tells you when that happens ("corpus
exhausted at N reviews"). Verified 150,000 deep on one app x country. Note Apple serves
*written reviews* — a fraction of the advertised star-rating count.

**Why are `appVersion` and votes empty in my deep-mode run?**
Because Apple's review API does not return them on any parameter combination we could
measure. They exist only on the RSS route = recent mode. This is the single most
misunderstood thing about App Store review data — see gotcha 2.

**Can I sort deep mode by date?**
No — Apple ignores the sort parameter on its review API (measured). Deep mode is
most-helpful order; recent mode is newest-first. `order` on every row says which you got.

**Which countries work?**
Any storefront Apple runs (~175). Nine verified live in testing. Unknown codes are
reported per pair and never billed.

**Two runs — will I get duplicates?**
Within a run, never — deduped before billing and verified by the end-of-run integrity
check. Across runs, dedupe on `reviewId` downstream.

**Do I get charged for filtered-out reviews?**
No. Rating and reply filters run after fetching but before billing.

**How fast is it?**
Measured: 45.7 rows/s across pairs, ~27 rows/s within one pair. The shipped default
(600 rows) takes about a minute; 100,000 rows is an hour-plus, rate-limited by Apple.

**Something looks wrong — how do I debug it?**
Read the run log: it prints per-app rating counts, per-pair outcomes, duplicate and
filter counts, and measured field fill for every run. That usually separates a filter
problem from an Apple-side change in one glance.

***

### Legal & fair use

This Actor reads public review data from Apple's endpoints without any login,
authentication bypass, or personal-data collection beyond the public nickname Apple
itself displays on every review. For transparency: Apple's `robots.txt` on
`apps.apple.com` includes `Disallow: /api/*` and on `itunes.apple.com` includes
`Disallow: /*/rss/*` (fetched 2026-08-15) — robots directives aimed at search-engine
crawling of those paths; the dedicated API hosts behind the store app publish no
robots.txt. This Actor makes targeted, rate-limited reads of reviews for apps you
explicitly specify. **You are responsible for complying with Apple's terms of use and
with how you use the data.**

Apple®, App Store® and iTunes® are trademarks of Apple Inc. This Actor is not
affiliated with, endorsed by, or connected to Apple.

***

### Feedback

Found a missing field or want a new filter? Open an issue on the **Issues** tab, and if
the Actor earns it, a review on the **Reviews** tab helps other buyers find it.

# Actor input Schema

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

One or more apps, as full App Store URLs (https://apps.apple.com/us/app/whatsapp-messenger/id310633997) or bare numeric ids (310633997). The id is the number after "id" in any App Store URL. Every app is crossed with every country below. An id that does not exist is reported and skipped rather than silently returning nothing. Left empty, the Actor runs its WhatsApp sample so an API or agent call with no input still gets real rows.

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

2-letter App Store storefront codes, one per entry: us, gb, de, fr, jp, br, in, au, kr, ca, mx, es, it, nl… (Apple runs ~175 storefronts; the README lists them). Reviews are storefront-specific, so every country is a separate corpus — this is how you sweep a competitor's reviews worldwide in one run. An unknown code is reported per app x country and never billed.

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

"deep" (default) reads Apple's review API with unlimited pagination — verified 150,000 reviews deep on one app — in Apple's most-helpful order, and includes developer replies and the isEdited flag. "recent" reads Apple's newest-first feed instead: max 500 reviews per app x country (Apple's cap, not ours), and it is the ONLY mode that carries appVersion, helpful-vote counts and the author profile URL — Apple's API omits those in deep mode.

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

Keep only reviews rated at least this many stars (1-5). 0 = off. Set minRating 1 and maxRating 2 to mine complaints; 4-5 for testimonial material.

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

Keep only reviews rated at most this many stars (1-5). 0 = off.

## `onlyWithDeveloperResponse` (type: `boolean`):

Keep only reviews carrying a developer reply. Deep mode only — Apple's newest-first feed (recent mode) does not carry replies at all, and the run stops with an error rather than silently returning nothing. Expect a minority of rows even for responsive publishers: measured 47% on Slack's first 100, 0% on Duolingo's.

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

Hard cap on reviews returned across the whole run, and therefore your cost ceiling: 600 = $0.06, 10,000 = $1, 100,000 = $10. Counts unique, delivered reviews only.

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

Per-corpus cap, so one deep storefront cannot eat the whole run budget before the other countries get a turn. 0 = no per-corpus cap (each corpus is walked to exhaustion or until Max reviews is hit). The shipped default of 200 is what splits the default 600-review run evenly across us/gb/au.

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

How many app x country corpora are walked in parallel (pages within one corpus are sequential — that is how offset pagination works). Max 5, and deliberately modest: Apple rate-limits per IP and the retry logic already absorbs that; more workers mostly buys more 429s.

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

On by default (Apify datacenter) and that is deliberate: Apple 429-throttles a busy single IP — measured 65% raw success direct at concurrency 3 versus 100% (50/50 contiguous pages) through datacenter with this Actor's fresh-session retry. Residential is not needed; there is no bot wall, only a rate limit.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997"
  ],
  "countries": [
    "us",
    "gb",
    "au"
  ],
  "mode": "deep",
  "minRating": 0,
  "maxRating": 0,
  "onlyWithDeveloperResponse": false,
  "maxItems": 600,
  "maxReviewsPerAppCountry": 200,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per review: rating, title, body, author, date, country, app and developer reply where present.

# 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/whatsapp-messenger/id310633997"
    ],
    "countries": [
        "us",
        "gb",
        "au"
    ],
    "maxItems": 600,
    "maxReviewsPerAppCountry": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/appstore-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 = {
    "apps": ["https://apps.apple.com/us/app/whatsapp-messenger/id310633997"],
    "countries": [
        "us",
        "gb",
        "au",
    ],
    "maxItems": 600,
    "maxReviewsPerAppCountry": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/appstore-reviews-scraper").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/whatsapp-messenger/id310633997"
  ],
  "countries": [
    "us",
    "gb",
    "au"
  ],
  "maxItems": 600,
  "maxReviewsPerAppCountry": 200
}' |
apify call scrapersdelight/appstore-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/appstore-reviews-scraper"
        }
    }
}

```

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/00ZZ3SWNSA1BI0uTx/builds/0JOzC3uNLnXvmtyrR/openapi.json
