App Store Reviews Scraper — Every Country, Not Just US
Pricing
from $0.05 / 1,000 review returneds
App Store Reviews Scraper — Every Country, Not Just US
Scrape Apple App Store reviews for any app, in any country store. Get rating, title, body, author, app version and date as JSON or CSV. Apple's feed is per-country and most tools return only US reviews — this one reports every market you ask for, including the ones with none.
Pricing
from $0.05 / 1,000 review returneds
Rating
0.0
(0)
Developer
Yaniv van der Stigchel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
19 hours ago
Last modified
Categories
Share
App Store Reviews Scraper — verify it fetched the right app
Every row names the app as Apple returns it for the id you passed — name, developer, bundle id and store URL. Ask for Spotify, see Spotify in row one. See something else and you know the run is wrong immediately, rather than after loading five thousand rows into a spreadsheet.
That sounds unremarkable until you read this category's reviews. "Keeps scraping the example app (candycrush) even if I update the app ID" (1.2 stars, 527 users). "kept spewing out the same incorrect reviews". "defaults to retrieving snapchat and facebook reviews". Five Actors across four publishers return the wrong app's reviews, and it goes unnoticed because review text never says which app it belongs to.
What it does
- Scrape App Store reviews for any app, in any country store
- Prove which app the reviews belong to, from Apple, on every row
- Group complaints by release:
appVersion,latestVersionandisLatestVersion - Filter stale feedback with
reviewAgeDays— no date arithmetic - Compare markets, and see which country stores had no reviews at all
- Export to JSON or CSV, or run it on a schedule
Three things this category gets wrong
Wrong app, confidently. Fixed by resolving identity from Apple's lookup and
stamping it on every row, success or failure. If appName is not your app, the
run is wrong, and you can see it in row one.
Silent truncation. Apple's public feed serves at most 500 reviews per app
per country store, newest first. That number is printed in the log before any
work starts, a warning names every app and country that reaches it, and
SUMMARY carries pairsTruncatedByAppleCap with the pairs listed. When nothing
was truncated the summary says so too, so a complete result is confirmed rather
than assumed.
Missing markets. There is no country allow-list and no curated dropdown.
Any two-letter ISO code goes straight to Apple — cn, hk, tw, kz, is,
whatever you need. No store can be absent because we did not think of it.
No duplicate reviews
Apple's pages shift while a run is in progress, which is how duplicates get into
other tools. Every review is deduplicated on Apple's own reviewId, which is
stable across runs — so it also works as a primary key for merging today's run
into yesterday's without double-counting.
Why per-country matters
Ask for us, gb and de for the same app and you routinely get three
different pictures: different star averages, different top complaints, and
sometimes an empty store. A single-country tool cannot show you that, and worse,
it cannot tell you it did not look.
Every row carries its country, so the split is checkable rather than trusted.
You only pay for reviews
errorCode | Meaning | Charged |
|---|---|---|
— (success: true) | A review row | Yes |
no-reviews-in-country | App exists, nobody reviewed it in that store | No |
app-not-found | No such app id, or Apple refused | No |
unparseable-app-id | Input was not an App Store id or URL | No |
unparseable-country | Country was not a two-letter code | No |
An empty country store is a real answer and one of the more useful ones. It is reported as a row and never billed.
Input
| Field | Required | Description |
|---|---|---|
apps | yes | Numeric ids (324684580) or any apps.apple.com URL |
countries | no | Two-letter codes. Default ["us"] |
maxReviewsPerApp | no | Per app and country. Default 500, which is Apple's cap |
{"apps": ["324684580", "https://apps.apple.com/us/app/duolingo/id570060128"],"countries": ["us", "gb", "de", "jp"],"maxReviewsPerApp": 200}
Output
Every row has the same fields whether it succeeded or failed, so you can select columns without branching.
| Field | Type | Description |
|---|---|---|
success | boolean | True when the row carries a review |
appId | string | App Store numeric id, as you asked for it |
appName | string | The app's name as Apple has it — your check that the run returned the right app |
developer | string | Publisher, from the store |
storeRating | number | Overall rating in this country store |
storeRatingCount | integer | How many ratings that average covers |
bundleId | string | Reverse-DNS id (com.spotify.client) — the join key to Google Play and analytics |
appUrl | string | The store page, to confirm the app by eye in one click |
country | string | Country store this review came from |
reviewId | string | Apple's stable id — deduplicates within and across runs |
rating | integer | 1 to 5 |
title | string | Review headline |
body | string | Full review text |
authorName | string | App Store nickname; Apple publishes no real name |
appVersion | string | The build the review was written against |
latestVersion | string | The build shipping now, from Apple |
isLatestVersion | boolean | Whether the review is about the current release. Null when either version is unknown |
updatedAt | string | ISO 8601 timestamp |
reviewAgeDays | integer | Whole days old at run time |
voteSum | integer | Net helpfulness votes |
voteCount | integer | Total helpfulness votes |
errorCode | string | Null on success |
errorMessage | string | Human-readable explanation |
{"success": true,"appId": "324684580","appName": "Spotify: Music and Podcasts","developer": "Spotify","storeRating": 4.77467,"storeRatingCount": 41883904,"bundleId": "com.spotify.client","appUrl": "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580?uo=4","country": "us","reviewId": "14504656715","rating": 1,"title": "Ads are getting insane","body": "It got to the point where after 2 songs, 2 one-minute ads would play.","authorName": "Amber10122012333","appVersion": "9.1.78","latestVersion": "9.1.78","isLatestVersion": true,"updatedAt": "2026-09-03T00:01:34-07:00","reviewAgeDays": 1,"voteSum": 0,"voteCount": 0,"errorCode": null,"errorMessage": null}
The run summary
SUMMARY in the key-value store answers "what did this run achieve", per app
and per country store — not just in total:
{"requestedApps": 1,"requestedCountries": 2,"pairs": 2,"delivered": 500,"emptyCountries": 1,"notFound": 0,"maxReviewsPerAppPerCountry": 500,"appleFeedCapPerAppPerCountry": 500,"pairsTruncatedByAppleCap": 1,"truncationNote": "1 app/country pair(s) reached Apple's cap of 500 reviews per country store, newest first: 324684580/us. Older reviews exist but the public feed does not serve them.","byAppAndCountry": [{"appId": "324684580","appName": "Spotify: Music and Podcasts","developer": "Spotify","country": "us","reviewsDelivered": 500,"appleCapHit": true,"storeRating": 4.77467,"sampleAverageRating": 2.31,"ratingDrift": -2.46,"ratingBreakdown": { "1": 231, "2": 47, "3": 38, "4": 41, "5": 143 },"newestReview": "2026-09-04T05:12:03-07:00","oldestReview": "2026-08-27T11:40:19-07:00","versionsCovered": ["9.1.76", "9.1.78"],"latestVersion": "9.1.78","errorCode": null}]}
ratingDrift is the sampled average minus the lifetime store rating. Negative
means the newest reviews are worse than the app's history — the reading a star
rating on its own cannot give you.
Use it from an AI agent (MCP)
This Actor is callable as a tool through the Apify MCP server, so Claude, ChatGPT, Cursor and VS Code can run it directly.
Add the server to your MCP client:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com","headers": {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}}}}
Then ask for what you want in plain language — for example “get the App Store reviews for this app in the UK and German stores” — and the agent calls cleanfeed/app-store-reviews-scraper with the right input. Every output field is described in the dataset schema, so the agent knows what it is getting back before it runs anything.
Call it from code
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("cleanfeed/app-store-reviews-scraper").call(run_input={"apps": ["324684580"],})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["success"]:print(item)
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('cleanfeed/app-store-reviews-scraper').call({apps: ["324684580"],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((i) => i.success));
cURL
curl -X POST "https://api.apify.com/v2/acts/cleanfeed~app-store-reviews-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H 'Content-Type: application/json' \-d '{"apps": ["324684580"]}'
Related actors
| If you need | Use |
|---|---|
| Comments on a product's YouTube videos | YouTube Comments Scraper |
| Contact details for the company behind an app | Verified Website Contacts |
Limitations
- Apple caps the public feed at 500 reviews per app per country, newest first — ten pages of fifty, with no way to page beyond it. The cap is printed in the run log before any work starts, warned about per app and country when it is reached, and reported in
SUMMARYaspairsTruncatedByAppleCapwith the pairs named. When it is not reached,SUMMARYsays so too, which means a full result is confirmed rather than assumed. Anything promising an app's complete review history is not using this source. - Reviews are per-country, not per-language. A German-language review left from a US account appears in the
usstore. An app with no reviews in a country returnserrorCode: no-reviews-in-country, which is a real answer and never charged. - Newest first is the only order. Apple's public feed offers no most-helpful or most-critical sort, so
maxReviewsPerAppalways trims the oldest reviews, never a random sample. - No developer responses. Apple's public feed carries the review only.
- Author names are App Store nicknames, not real names, and should not be treated as identifying a person.
- A star rating with no text is a real review and returns with a null
titleandbody. isLatestVersionis null when either version is unknown. An unknown comparison is reported as unknown rather than guessed as false.
FAQ
How do I know it fetched the right app?
Because every row carries appName, developer, bundleId and appUrl as Apple returns them for the id you passed, never echoed from your input. Ask for Spotify and see Spotify in row one. See something else and the run is wrong, visibly, in the first row. This is the category's most common failure — competing Actors have reviews reading "Keeps scraping the example app (candycrush) even if I update the app ID" — and it is invisible in a plain review dump because review text never names the app it belongs to.
Will I silently get fewer reviews than I asked for?
No. Apple's feed stops at 500 per app per country store. That number is printed in the log before the run starts, a warning names every app and country that reached it, and SUMMARY carries pairsTruncatedByAppleCap plus a plain-English truncationNote. If nothing was truncated the note says that as well, so you know the result is complete rather than hoping it is.
Are there duplicate reviews?
No. Apple's pages shift while a run is in progress, which is how duplicates get into other tools. Every review is deduplicated on Apple's own reviewId, which is stable across runs — so you can also use it as a primary key to merge today's run into yesterday's without double-counting.
Which country codes can I use?
Any two-letter ISO code Apple runs a store in — us, gb, de, fr, jp, br, in, cn, hk, tw. There is no allow-list in the code and no dropdown of curated markets, so no store can be missing because we forgot it. A code Apple does not run returns errorCode: unparseable-country or no-reviews-in-country rather than being silently dropped.
How do I group complaints by release?
Each row carries appVersion (the build the review was written against), latestVersion (what is shipping now, from Apple) and isLatestVersion. Filter to isLatestVersion: true for feedback on what you just released, or group by appVersion to see which build a complaint arrived with. SUMMARY lists versionsCovered per app and country.
What does the run summary tell me?
Per app and country: reviews delivered, the app Apple resolved, the star breakdown, the newest and oldest review in the sample, the versions covered, whether Apple's cap was hit, and ratingDrift — the sampled average minus the lifetime store rating. A negative drift means the newest reviews are worse than the app's history, which a star rating on its own cannot tell you.
How do I find an app's id?
It is the number after /id in any App Store URL. You can paste the whole URL instead and the id is extracted for you.
Why did a country return no reviews?
Because nobody has reviewed the app in that store. This is common for apps with one strong home market, and it is exactly the finding per-country reporting exists to surface. You are not charged for it.
Why not just query the US store?
Because ratings and complaints genuinely differ by market. Ask for us, gb and de for the same app and you routinely get three different pictures. A single-country tool cannot show you that, and cannot tell you it did not look.
Can I get Google Play reviews too?
Not from this Actor. Play is a separate source with a different shape, and merging them behind one schema would hide which store a row came from.
Do I need an Apple developer account?
No. This reads Apple's public customer-reviews feed, which needs no key and no login.
Notes
Only Apple's public customer-reviews feed is read — the same data any visitor sees on an app's App Store page. No developer account, no key, no login, and nothing that is not already published.