App Store Reviews API: Apple App Store Data as JSON
To get App Store reviews for any app as JSON, call GET /v1/apple-app-store/product-reviews with the app's product_id and store=apps. The response lists each review with its rating, title, text and date, plus a pagination block for the next page. A successful call costs 1 credit, and a failed call costs nothing.
Why can't App Store Connect return reviews for other people's apps?
Apple's App Store Connect API is the tool developers use to manage their own apps. It has a Customer Reviews resource (Apple Developer Documentation, Customer Reviews), but it's part of an API you sign in to as a team that publishes apps. We wouldn't plan on it for a competitor's reviews, and you should check Apple's documentation for what your own account can read.
That's the right tool when you're reading and answering feedback on your own app. If you're doing market research, competitor tracking or building a review dataset across many apps, you need a source that works from a public app ID. That's the gap this endpoint fills: it works from a public app ID, so you only need the ID. Apple's own API is the place for replies: its Customer Review Responses resource (Apple Developer Documentation, Customer Review Responses) is the place to manage replies to reviews. This API doesn't publish replies.
How do you find an app's product ID?
The product_id is the numeric ID in the app's App Store URL (the digits after id). If you only know the name, use the search endpoint. It takes one required parameter, query, and returns matching apps for the store and country you choose.
curl "https://api.perceptlayer.com/v1/apple-app-store/search?query=facebook&store=apps&country=us" \
-H "X-API-Key: $API_KEY"
We don't have a recorded search response to show, so this article doesn't name the field that holds the ID. Run the call in the playground on the Apple App Store API page and read the ID from the output. The numeric ID in the App Store URL is the more reliable route. The search call costs 1 credit. If you already have the ID from a URL, skip this step.
The store parameter accepts one of apps, mac, books, audiobooks, movies, tv, music or podcasts. For iPhone and iPad apps, use apps. The country parameter is a code of up to two characters, such as us.
How do you request an app's reviews?
Send a GET request to /v1/apple-app-store/product-reviews with the product_id, store and, optionally, num to set how many reviews you want. No parameter is strictly required by the catalogue, but without an app ID there is nothing to look up.
curl "https://api.perceptlayer.com/v1/apple-app-store/product-reviews?product_id=324684580&store=apps&num=20" \
-H "X-API-Key: $API_KEY"
Send the key in the X-API-Key header, as above. Where headers aren't possible, the api_key query parameter works too. See authentication in the docs for both forms. Keys start with sk_live_, and you can get a free API key in a minute.
Which parameters does the endpoint take?
All parameters are optional in the catalogue. These are the ones you'll use for reviews.
| Parameter | Type | What it does |
|---|---|---|
product_id |
string, up to 255 characters | The app's ID from its store URL |
id |
string, up to 255 characters | Listed in the catalogue alongside product_id |
store |
string | One of apps, mac, books, audiobooks, movies, tv, music, podcasts |
country |
string, up to 2 characters | The storefront country, such as us |
num |
integer | How many reviews to request |
offset |
integer | Where to start in the list of reviews |
next_page_token |
string, up to 100 characters | The token from the previous page, for the next one |
season_id |
string, up to 255 characters | Listed in the catalogue; not used in the request above |
The catalogue documents types and limits for these parameters and nothing more. It doesn't state defaults or page sizes, so set num explicitly if you depend on a number.
What does the response look like?
The response has three parts: search_parameters echoing your request, a reviews array and a reviews_pagination block. This is a real, trimmed response for the request above. We've left out reviewer usernames and cut the text where it says ….
{
"search_parameters": {
"product_id": "324684580",
"store": "apps",
"num": 20,
"country": "us",
"engine": "apple_app_store_product_reviews"
},
"reviews": [
{
"id": "11994640656",
"title": "Favorite app ever",
"rating": 5,
"text": "I personally love this app. I have had it for over 4 years, and it’s been an app I use daily. If you are thinking about getting it, for sure get the Premium pla…",
"date": "2024-11-26T17:10:59Z",
"is_edited": false
},
…
],
"reviews_pagination": {
"offset": 0,
"limit": 20,
"next_offset": 20,
"next_page_token": "20",
"end_of_history": false
}
}
Each review has an id, a title, a numeric rating, the review text, an ISO 8601 date and an is_edited flag. The raw response also carries an author field, which we removed from the excerpt. If you store reviews, decide whether you need it at all; ratings, text and dates are enough for most analysis.
The … at the end of each text value in the sample marks where we trimmed the excerpt.
How do you page through all the reviews?
Use reviews_pagination. It gives you next_offset, next_page_token and an end_of_history flag. Pass the token back as next_page_token (or the offset as offset) to fetch the next page, and stop when end_of_history is true. Every page is a separate call, so every page costs 1 credit.
import os
import requests
BASE = "https://api.perceptlayer.com/v1/apple-app-store/product-reviews"
HEADERS = {"X-API-Key": os.environ["API_KEY"]}
params = {"product_id": "324684580", "store": "apps", "num": 20, "country": "us"}
reviews = []
for _ in range(5): # hard cap: at most 5 calls, 5 credits
resp = requests.get(BASE, headers=HEADERS, params=params, timeout=30)
resp.raise_for_status()
body = resp.json()
reviews.extend(body["reviews"])
page = body["reviews_pagination"]
if page["end_of_history"]:
break
params["next_page_token"] = page["next_page_token"]
print(len(reviews), "reviews")
The loop has a fixed cap so a large app can't run up your balance by accident.
How do you turn the pages into a result?
Once reviews is filled, you can work with the rating and date fields from the recorded response without any more calls. This adds a count per star rating and the share of low ratings, then lists the one-star and two-star reviews from the last 90 days. date is an ISO 8601 string, so comparing it as text works.
from collections import Counter
from datetime import datetime, timedelta, timezone
by_rating = Counter(r["rating"] for r in reviews)
for stars in sorted(by_rating, reverse=True):
print(stars, "stars:", by_rating[stars])
low = sum(n for stars, n in by_rating.items() if stars <= 2)
print(f"{low / len(reviews):.0%} of {len(reviews)} reviews are 1 or 2 stars")
cutoff = (datetime.now(timezone.utc) - timedelta(days=90)).strftime("%Y-%m-%dT%H:%M:%SZ")
recent_low = [r for r in reviews if r["rating"] <= 2 and r["date"] >= cutoff]
for r in recent_low:
print(r["date"][:10], r["rating"], r["title"])
The output is a rating breakdown and a list of recent complaints you can read, which is a usable first pass at what's going wrong in an app. The numbers describe only the pages you fetched, not every review the app has. If reviews is empty, the division fails, so check end_of_history and your parameters first. For retries, backoff and the lost-reply case, read paginate a web data API in Python. Requests have no idempotency key, so a call whose reply was lost may have succeeded and been billed. Retrying without paying twice covers it.
What do the Details and Charts endpoints add?
The same API also has GET /v1/apple-app-store/details, which takes the same parameters as reviews, and GET /v1/apple-app-store/charts, which takes store, chart (top-free, top-paid, top-grossing, most-played, top-subscriber or top-episodes), genre, country and limit. We don't have a recorded response for either, so this article doesn't show their fields. Try them in the playgrounds on the Charts and Details pages. Related endpoints include Developer (by artist_id) and Apps Product.
What does it cost?
Every endpoint on the Apple App Store API costs 1 credit per successful call. Failed calls cost nothing and return X-Credits-Used: 0. Successful responses carry X-Credits-Used and X-Credits-Remaining, so you can watch your balance in code. The credits docs explain the accounting.
A worked example: one chart call, then reviews for each of its 10 apps with 5 pages each.
| Step | Calls | Credits |
|---|---|---|
Charts, limit=10 |
1 | 1 |
| Reviews, 10 apps × 5 pages | 50 | 50 |
| Total | 51 | 51 |
51 credits sits inside the 1,000 free credits you get each month. At scale, credits come in prepaid packs:
| Pack | Credits | Requests a second included |
|---|---|---|
| Starter | 10,000 | 5 |
| Build | 50,000 | 5 |
| Growth | 200,000 | 5 |
| Pro | 500,000 | 5 |
| Scale | 1,000,000 | 10 |
| Volume | 5,000,000 | 20 |
To pull 5 pages for each of 2,000 apps, that's 2,000 × 5 = 10,000 calls × 1 credit = 10,000 credits, which is exactly the Starter pack. Reviews for 10,000 apps at 5 pages each is 10,000 × 5 = 50,000 calls × 1 credit = 50,000 credits, the Build pack. Purchased credits last 12 months, and there's no subscription. See pricing for what credits cost and for faster request rates.
What can go wrong?
Errors return an HTTP status and a JSON body with error.code, error.message and error.request_id. The ones you'll meet most are invalid_api_key (401), insufficient_credits (402), invalid_parameters (422), rate_limited (429) and temporarily_unavailable (503). The service is prepaid. When the balance runs out you get a 402 and nothing further is debited, with no automatic top-up.
A 429 or 503 carries a Retry-After header with the seconds to wait, so honour it instead of retrying in a tight loop. The errors docs list every code, and rate limits explain the 429 behaviour.
Data is collected from publicly available sources and returned as found at the time of the call. Reviews can change after you fetch them, so store the date and the time you fetched them.
If you're building an agent rather than a pipeline, the same key works over MCP. See AI agents (MCP) and MCP server for web scraping. To compare iOS with Android, the Google Play Store API guide covers the same job on the other store. For a comparable reviews walkthrough on another site, see the Trustpilot reviews API guide or the Google Maps reviews API guide. More pull-by-source guides are in the API guides category.
Next step: get a free API key, then run the product-reviews call above in the playground on the Apple App Store API page.
APIs in this post
Frequently asked questions
Can I get reviews for an app I don't own?
Yes, this endpoint takes a public product_id, so ownership isn't required. App Store Connect also has a Customer Reviews resource, but it sits inside an API for managing your own apps, so we'd use it for your own apps only. You can't post replies through Perceptlayer, though. Apple's Customer Review Responses resource is the place for that.
How much does it cost to pull reviews for one app?
Each successful call costs 1 credit and returns one page of reviews, so cost equals the number of pages you fetch. Five pages is 5 credits. Calls that fail cost nothing and return X-Credits-Used: 0. The free tier of 1,000 credits per month covers 1,000 pages with no credit card.
Can I get reviews from a specific country?
Use the country parameter, a code of up to two characters such as us. The recorded request above returned "country": "us" in search_parameters. Run the call once per country you care about; each is a separate call and costs 1 credit.
How do I get the next page of reviews?
Read reviews_pagination in the response. Pass its next_page_token as next_page_token (or next_offset as offset) in your next request. Keep going until end_of_history is true. Each page is billed as its own call, so cap your loop to control spend.
Does this return personal data?
Reviews can include author details and free text written by individuals. Treat them as personal data in your own system: keep only the fields you need, and don't use scraped reviews to contact people. Your lawful basis and retention rules are your responsibility.