Most people who export App Store reviews only get the US ones, and that's a big blind spot. Every App Store country has its own reviews, so the complaints of your German, Brazilian or Japanese users never show up in a US-only export. Apple has a free public feed that gives you all of them as JSON, with no account and no API key. Here is how it works, the four traps that quietly break most scripts, and a ready-made option if you'd rather not maintain one.
1. The feed
Apple publishes customer reviews as an RSS feed that also comes in JSON:
https://itunes.apple.com/{country}/rss/customerreviews/page={page}/id={app_id}/sortby=mostrecent/json -
country: a two-letter store code (us,gb,de,jp...). -
app_id: the number at the end of the App Store URL. Forapps.apple.com/us/app/duolingo-language-chess/id570060128it's570060128. -
sortby:mostrecentormosthelpful.
Each page has up to 50 reviews:
import httpx url = "https://itunes.apple.com/us/rss/customerreviews/page=1/id=570060128/sortby=mostrecent/json" entries = httpx.get(url).json()["feed"].get("entry", []) for e in entries[:3]: print(e["im:rating"]["label"], e["title"]["label"], e["updated"]["label"]) Every review comes with its stars, title, text, app version, author, date and helpful votes.
2. The four traps
Trap 1: the 10-page limit. Ask for page 11 and Apple answers 400 CustomerReviews RSS page depth is limited to 10. That means 500 reviews per sort and per country. The mostrecent and mosthelpful lists barely overlap, though, so if you fetch both and deduplicate by review id you get close to 1,000 per country.
Trap 2: one country at a time. A US export of a global app can miss most of its reviews. In a test with Duolingo, the US store gave 500 recent reviews, while looping over all storefronts gave 7,429 recent reviews from 173 countries. Countries with no reviews just return an empty feed.
Trap 3: going too fast. Fire around six requests a second and Apple starts answering 403 for a while. Stay at two or three requests a second, and when you see a 403 or 429, pause everything for a minute instead of retrying in a tight loop.
Trap 4: entries that aren't reviews, and fuzzy search. Older feeds start with an entry that describes the app, so skip anything without im:rating. And if you look up apps by name with the iTunes Search API (itunes.apple.com/search?term=...&entity=software), keep in mind that it always returns something, even for nonsense. Check that the result's name actually contains the words you typed.
3. A small script for every country
import time import httpx FEED = "https://itunes.apple.com/{cc}/rss/customerreviews/page={p}/id={app}/sortby={sort}/json" COUNTRIES = ["us", "gb", "de", "fr", "es", "it", "br", "jp", "in", "au"] # add the ones you need def reviews(app_id, countries, sorts=("mostrecent", "mosthelpful"), per_second=2): client = httpx.Client(timeout=30) for cc in countries: seen = set() for sort in sorts: for page in range(1, 11): # Apple stops at page 10 time.sleep(1 / per_second) r = client.get(FEED.format(cc=cc, p=page, app=app_id, sort=sort)) if r.status_code in (403, 429): # rate limited: back off, then try again time.sleep(60) r = client.get(FEED.format(cc=cc, p=page, app=app_id, sort=sort)) if r.status_code != 200: break entries = r.json()["feed"].get("entry", []) if isinstance(entries, dict): # a single entry comes as an object entries = [entries] for e in entries: if "im:rating" not in e or e["id"]["label"] in seen: continue seen.add(e["id"]["label"]) yield { "country": cc, "rating": int(e["im:rating"]["label"]), "title": e["title"]["label"], "text": e["content"]["label"], "version": e["im:version"]["label"], "date": e["updated"]["label"], } if len(entries) < 50: break for rev in reviews("570060128", COUNTRIES): if rev["rating"] <= 2: print(rev["country"], rev["rating"], rev["title"]) That's enough for a few apps and a few countries. Once you want all ~175 storefronts, several apps, date filters and a daily schedule, the slow part isn't the code but running it, retrying it and storing the output.
4. Skip the maintenance: the ready-made Actor
I packaged all of this as an Apify Actor: App Store Reviews Scraper.
- Paste apps however you have them: App Store URL, id, bundle id (
com.spotify.client) or just the name. - One, several or all ~175 countries in one run, with both sorts merged and deduplicated.
- Filters for stars (only 1-2 star complaints, say), dates (
30 days), keywords (crash,login,refund) and minimum text length. Filtered-out reviews are free. - Export to JSON, CSV or Excel, call it from Python, schedule it daily or use it from Claude and other AI agents through the Apify MCP server.
- $0.10 per 1,000 reviews, no start fee, and apps that can't be found cost nothing. Apify's free monthly credit covers tens of thousands of reviews.
from apify_client import ApifyClient client = ApifyClient("<YOUR_APIFY_TOKEN>") run = client.actor("Yakk000/app-store-reviews-scraper").call(run_input={ "apps": ["Duolingo"], "allCountries": True, "stars": ["1", "2"], "since": "30 days"}) for r in client.dataset(run["defaultDatasetId"]).iterate_items(): print(r["country"], r["rating"], r["title"]) 👉 Try it free on Apify. If it saves you time, a quick review on the Actor page helps a lot. If something is missing, open an issue and I'll add it.