DEV Community

Olly
Olly

Posted on

Your App Store reviews aren't just in the US: pull them from all 155 countries with Python

If you ship an iOS app, you probably read your reviews in App Store Connect or on the public store page for one country. That hides a lot. A crash that only shows up on a German locale, a pricing complaint from Brazil, a translation bug in Japan: those reviews live in their own storefronts and never show up in the US list.

This post shows two ways to get them:

  1. The free, do-it-yourself way using Apple's public review RSS feed (fine for one or two countries).
  2. Sweeping every storefront in one call with an Apify Actor I built, then filtering to the reviews that matter (1–2 stars, recent, mentioning "crash" or "subscription").

How Apple exposes reviews

Apple publishes a public JSON feed of customer reviews per app, per country:

https://itunes.apple.com/{country}/rss/customerreviews/page={1-10}/id={appId}/sortby=mostrecent/json
Enter fullscreen mode Exit fullscreen mode

Each page holds up to 50 reviews and there are 10 pages, so you get at most 500 reviews per country per sort order. No key, no login.

A minimal DIY version for one country:

import requests

APP_ID = "570060128"  # Duolingo, the number after "id" in the App Store URL
COUNTRY = "de"

reviews = []
for page in range(1, 11):
    url = f"https://itunes.apple.com/{COUNTRY}/rss/customerreviews/page={page}/id={APP_ID}/sortby=mostrecent/json"
    entries = requests.get(url, timeout=20).json().get("feed", {}).get("entry", [])
    if isinstance(entries, dict):  # a single entry comes back as an object, not a list
        entries = [entries]
    if not entries:
        break
    for e in entries:
        if "im:rating" not in e:   # older feeds put the app itself in the first entry
            continue
        reviews.append({
            "rating": int(e["im:rating"]["label"]),
            "title": e["title"]["label"],
            "text": e["content"]["label"],
            "version": e.get("im:version", {}).get("label"),
        })

print(len(reviews), "reviews from", COUNTRY.upper())
Enter fullscreen mode Exit fullscreen mode

That works. The pain starts when you want every country: 155 storefronts × 10 pages is 1,550 requests, with throttling, empty storefronts, the dict-vs-list quirk, retries, and de-duplication to handle. That part is what I packaged up.

Sweep all 155 storefronts in one call

App Store Reviews Scraper – All Countries runs the loop above across every storefront in parallel and gives you one clean dataset. Install the client:

pip install apify-client
export APIFY_TOKEN=...   # from Apify Console > Settings > API & Integrations
Enter fullscreen mode Exit fullscreen mode

Then ask for the negative, recent reviews from everywhere:

import os
from collections import Counter
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("olsols4/app-store-reviews-all-countries").call(run_input={
    "apps": ["https://apps.apple.com/us/app/duolingo/id570060128"],
    "countries": ["ALL"],            # or a list like ["us", "gb", "de", "br", "jp"]
    "maxReviewsPerCountry": 500,
    "sortBy": "mostrecent",
    "minRating": 1,
    "maxRating": 2,                  # only 1- and 2-star reviews
    "reviewsSince": "2026-09-01",    # e.g. the date of your last release
    "keywords": ["crash", "subscription", "refund", "login"],
    "includeAppDetails": True,
})

items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
reviews = [i for i in items if i["type"] == "review"]
apps = [i for i in items if i["type"] == "app"]

print(apps[0]["name"], apps[0]["averageRating"], "stars")
print(len(reviews), "matching reviews")

# Which countries are complaining the most?
for country, n in Counter(r["country"] for r in reviews).most_common(10):
    print(country, n)

# Which app version are the complaints about?
for version, n in Counter(r["appVersion"] for r in reviews).most_common(5):
    print(version, n)
Enter fullscreen mode Exit fullscreen mode

You can also pass app names instead of URLs with "searchTerms": ["duolingo"] (it takes the top store match), which is handy for competitor research.

What a row looks like

One row per review, plus one type: "app" summary row per app. Here is a real one from a test run today (author removed):

{
  "type": "review",
  "appId": "570060128",
  "country": "DE",
  "reviewId": "14665011333",
  "rating": 1,
  "title": "Nicht mehr was es war",
  "text": "Leider richtig schade, das war mal eine gute App aber mittlerweile ...",
  "appVersion": "7.144.0",
  "voteCount": 0,
  "voteSum": 0
}
Enter fullscreen mode Exit fullscreen mode

Note the keyword filter matches words as they appear, so for non-English storefronts add the local words too ("absturz" for German crashes, "assinatura" for Portuguese subscriptions), or drop keywords and filter afterwards with a translation step or an LLM.

Turn it into a weekly report

The useful version of this is a recurring job, not a one-off:

  1. In Apify Console, open the Actor, set the input above, and Save as a task.
  2. Add a Schedule (e.g. every Monday 08:00) for that task.
  3. Pick the latest results up from your own code:
run = client.actor("olsols4/app-store-reviews-all-countries").last_run(status="SUCCEEDED")
items = run.dataset().list_items().items
Enter fullscreen mode Exit fullscreen mode

Or skip code entirely and use Apify's built-in integrations to push each run to Google Sheets, Slack, a webhook, n8n, Make or Zapier.

Set reviewsSince to roughly a week back each time (or just de-duplicate on reviewId on your side) so you only look at what's new.

Ideas for what to do with the data

  • Release QA: after every release, run with maxRating: 2 and reviewsSince = release day. A cluster of 1-star reviews from one country on the new version is usually a localisation or regional-payment bug.
  • Competitor research: run the same query on two or three rival apps and compare what users in each market dislike.
  • ASO copy: pull 4–5 star reviews (minRating: 4) and mine the phrases real users use for your store description.
  • Feed an LLM: the text field is clean, so it drops straight into a sentiment or topic-clustering prompt.

Cost and limits (being upfront)

  • It's pay-per-result: $0.25 per 1,000 reviews, plus $0.002 per app summary row. Countries with no reviews cost nothing. One app, all countries, newest 50 per country is typically a few thousand reviews, so around a dollar or two.
  • The 500-per-country ceiling is Apple's, not the Actor's. If you need a full historical archive of a huge app, this won't give it to you; for "what are people saying right now, everywhere", it's plenty.
  • It's App Store only, no Google Play.
  • Reviews include public nicknames; handle them in line with GDPR and your local rules.

If you try it and a field is missing or something breaks, open an issue on the Actor's Issues tab and I'll take a look.

👉 App Store Reviews Scraper – All Countries on Apify

Top comments (0)