App Store Scraper — App Details & Reviews API
Pricing
from $2.00 / 1,000 apps
App Store Scraper — App Details & Reviews API
Search or look up any app on the iOS/macOS App Store and get its full details (price, rating, version, description, screenshots) plus its most recent customer reviews, straight from Apple's own public APIs. You only pay per app and per review delivered.
Pricing
from $2.00 / 1,000 apps
Rating
0.0
(0)
Developer
Plainfetch Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Search or look up any app on the iOS/macOS App Store and get back its full details — price, rating, version history, description, screenshots — plus its most recent customer reviews, without touching a browser. App Store Scraper is an App Store API alternative built directly on Apple's own official, public APIs: the iTunes Search API, the Lookup API, and the customer-reviews feed. Give it search terms or app ids and get one dataset item per app, plus one item per review, ready to filter, sort, or feed into another tool.
What can you do with it?
- ASO and competitor research. Pull a competitor's rating, price, category, description and screenshot set, or run a search term the way a shopper would and see exactly which apps rank for it.
- Monitor app ratings and releases. Track
rating,ratingCountandversion/updatedAtfor a list of apps over time — run it on a schedule and diff the results to catch a new release or a rating drop. - Review mining for product feedback. Fetch an app's most recent reviews to spot recurring complaints,
feature requests, or praise —
rating,titleandtextfor every review, in the language reviewers actually wrote it in. - Feed AI agents and downstream tools. Call this Actor from the Apify API, an integration, or an MCP-connected AI agent so it can look up an app's details or read its reviews as one step in a larger workflow.
What data do you get?
Each run produces two kinds of dataset item, distinguished by type.
One app item per app:
| Field | Type | Description |
|---|---|---|
name / title | string | The app's name (title is a duplicate of name, kept for consistency with review items). |
developer | string | null | The developer/seller name, e.g. "Duolingo". |
price / currency | number | null | 0 for a free app; isFree is true/false derived from it. |
rating / ratingCount | number | null | The app's overall App Store rating and how many ratings it has. |
version / updatedAt | string | null | The current version and when it was released. |
description | string | null | The full App Store description. |
genres / primaryGenre | string[] / string | null | The App Store category (or categories) the app is listed under. |
screenshots | string[] | Screenshot image URLs, straight from Apple. |
url | string | null | The app's App Store page. |
reviewsComplete | boolean | null | Whether this app's reviews (if requested) were fetched in full — null if reviews weren't requested. See below. |
reviewsIncompleteReason / reviewsError | string | null / object | null | Why reviewsComplete is false — see below. |
error | object | null | Set when a requested search term or app id could not be delivered — see Limitations. |
Every requested search term or app id leaves exactly one row — never silently dropped. A term or
id skipped entirely, before it was ever attempted, still gets its own app row with every other field
blank, and is never charged. The two reasons it can be skipped read differently, on purpose:
- Your spending cap was already reached — your own limit doing exactly what you set it to do, not
a failure of this Actor. That row carries no
errorat all: it saysreviewsIncompleteReason: "spending-cap-reached"(withreviewsComplete: false, since nothing was read for it, its reviews included) and counts as delivered in the run summary. - This Actor's circuit breaker had already tripped (three blocked or rate-limited answers in a
row) — the source would not have answered any further, so that row carries a clear
errorand counts as failed.
One review item per review:
| Field | Type | Description |
|---|---|---|
rating | number | null | 1 to 5. |
title | string | The review's title. |
text | string | null | The review's body text. |
version | string | null | The app version the reviewer was using. |
date | string | null | When the review was posted. |
appId | string | Which app this review is for. |
This Actor never emits who wrote a review. No reviewer nickname, no reviewer profile URL, no author field of any kind — only what they rated and wrote. See Limitations.
An app's reviews can end up incomplete, and the app row says so honestly. With includeReviews: true,
reviewsComplete: true means one thing: every review there was to get was got — the feed ran out, or
Apple's own 10-page ceiling was reached, which is all Apple publishes. reviewsComplete: false means
reviews were left unread, and reviewsIncompleteReason says why: "max-reviews-reached" (your own
maxReviewsPerApp stopped the read — not a failure, and everything fetched is still charged),
"interrupted" (a network or blocked error, with the detail in reviewsError), or
"spending-cap-reached". The app row itself is always delivered either way; only its reviews may be
incomplete. Every review already fetched before the cutoff is still in the dataset and still charged.
What real output looks like
You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. Here is real output from a run with
{"searchTerms": ["duolingo"], "maxAppsPerSearch": 1, "includeReviews": true, "maxReviewsPerApp": 2}description,
releaseNotes, screenshots and languages are also shortened here (the real item carries the app's full
description and release notes, its complete screenshot set, and all 32 supported languages). One more
field is not a literal capture either: the app row's reviewsComplete: false and
reviewsIncompleteReason: "max-reviews-reached" are derived from this Actor's own logic for
maxReviewsPerApp: 2 against an app with far more reviews than that — the same values the code produces
for real, not copied verbatim from one specific run and not hand-edited. Reviews are fetched (and appear in
the dataset) before the app row they belong to — that is what lets the app row report reviewsComplete
honestly instead of guessing:
[{"type": "review","appId": "570060128","reviewId": "14566250893","rating": 2,"title": "Loot chest are terrible","text": "Stop forcing me to tap repeatedly for a chest. I don’t care about what is in it. It is just annoying trash that gets in the way of learning","version": "7.140.0","date": "2026-09-18T17:24:18-07:00","country": "us"},{"type": "review","appId": "570060128","reviewId": "14566216737","rating": 5,"title": "Good app","text": "I love Duolingo but I wish they did gettar","version": "7.140.0","date": "2026-09-18T17:11:09-07:00","country": "us"},{"type": "app","appId": "570060128","bundleId": "com.duolingo.DuolingoMobile","name": "Duolingo: Language Lessons","title": "Duolingo: Language Lessons","developer": "Duolingo","developerId": "570060151","url": "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128?uo=4","iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/c7/ee/1d/c7ee1d8c-9928-98bf-318a-0713e0351998/AppIcon-0-0-1x_U007epad-0-1-85-220.png/512x512bb.jpg","price": 0,"currency": "USD","isFree": true,"rating": 4.7232,"ratingCount": 5456627,"ratingCurrentVersion": 4.7232,"version": "7.140.0","releasedAt": "2012-11-13T08:00:00Z","updatedAt": "2026-09-14T15:53:43Z","releaseNotes": "Our mascot owl, Duo, is trading in his usual diet of mice for bugs this week...","description": "Learn a new language, chess & more with the world's most downloaded education app!...","genres": ["Education", "Social Networking"],"primaryGenre": "Education","contentRating": "4+","sizeBytes": 515881984,"minimumOsVersion": "17.0","languages": ["AR", "BN", "CS", "NL", "EN", "FR", "DE"],"screenshots": ["https://is1-ssl.mzstatic.com/image/thumb/PurpleSource211/v4/6e/ef/30/6eef3023-de04-3b29-21f8-a993245b9abd/iPhone6.5_-_Default_-_first_screen_adapt_Var_2_01.jpg/320x480bb.jpg"],"country": "us","searchTerm": "duolingo","reviewsComplete": false,"reviewsIncompleteReason": "max-reviews-reached","reviewsError": null,"error": null}]
reviewsComplete: false here because the read stopped at maxReviewsPerApp: 2 while this app still had
millions of reviews available — nothing went wrong, and both reviews are delivered and charged;
incompleteReason just tells you it is not the whole feed.
How much does it cost?
You pay $0.002 per app delivered and $0.0005 per review delivered. A search term or app id that fails — not found, blocked, or a network error — is never charged. No start fee.
| Run | Cost |
|---|---|
| 5 apps, no reviews | 5 × $0.002 = $0.01 |
| 5 apps, 50 reviews each | 5 × $0.002 + 250 × $0.0005 = $0.135 |
| 20 apps, 100 reviews each | 20 × $0.002 + 2,000 × $0.0005 = $1.04 |
Real example: the run above (
{"searchTerms": ["duolingo"], "maxAppsPerSearch": 1, "includeReviews": true, "maxReviewsPerApp": 2}Input
See the Input tab for the full configuration. The minimal input is one search term or one app id:
{"searchTerms": ["duolingo"]}
searchTerms/appIds— provide at least one of either.appIdsaccepts a numeric App Store id ("570060128"or570060128) or an App Store URL containing one ("https://apps.apple.com/us/app/duolingo/id570060128"). Duplicate entries are processed once: the same id listed twice is looked up once, and an app found by both a search term and an explicit id is delivered — and charged — once.country(optional, default"us") — two-letter App Store storefront code, e.g."gb","de". Controls which country's catalogue, pricing and reviews are read.maxAppsPerSearch(optional, default50, 1 to 200) — how many apps to fetch per search term.includeReviews(optional, defaultfalse) — also fetch each app's most recent reviews.maxReviewsPerApp(optional, default100, 0 to 500) — how many of the most recent reviews to fetch per app. Ignored whenincludeReviewsis off.requestDelayMs(optional, default250) — courtesy pause before each request to Apple's APIs.
Limitations — read this first
This Actor never returns reviewer names. Apple's customer-reviews feed includes a reviewer nickname
and profile URL for every review; this Actor drops both before anything else touches the response. No
review item ever carries an author, nickname, or profile URI, no matter what Apple's feed includes.
Apple exposes at most 500 of the most recent reviews per app per country (10 pages of 50) — this is a
hard ceiling of Apple's own feed, not a limit this Actor imposes; maxReviewsPerApp cannot get you more.
The Search API returns at most 200 apps per search term. Apple rate-limits the Search API to roughly
20 calls per minute per IP address by its own documentation — the default requestDelayMs (250ms) and
the built-in concurrency cap (5) are deliberately gentle to respect that; raising them risks a rate-limited
or blocked response, which this Actor never attempts to get around (no proxy rotation, no fingerprint
spoofing, no CAPTCHA solving). Three consecutive blocked or rate-limited responses stop the run entirely,
with a clear message in the log — and a search term or app id skipped that way, or by your own spending
cap, still leaves its own uncharged app row instead of vanishing.
iOS/macOS App Store only — not Google Play. This Actor reads Apple's App Store APIs exclusively; it does not cover Android or the Google Play Store.
FAQ
Is this legal? We can't give you legal advice, but here is exactly what the Actor does: it calls Apple's own public, documented APIs — the iTunes Search API, the Lookup API, and the customer-reviews feed — the same data these APIs return to any client that asks. It does not log in, does not get around any block, and never emits a reviewer's name or profile. You are responsible for the search terms and app ids you submit and for how you use the results.
Where does the data come from? Directly from itunes.apple.com's public Search, Lookup and
customer-reviews endpoints — no third-party database, no cache.
How do I use this from the API or an AI agent (MCP)? Call it like any other Apify Actor: via the
Apify API, the JavaScript or Python client, an integration, or an
MCP-connected AI agent that has access to your Apify Actors. Pass the same searchTerms/appIds input
and read the results back from the default dataset.
What happens if I hit my spending cap? The run stops cleanly: it finishes the app or review already
being processed, writes everything it has, and reports chargeLimitReached: true in the run summary.
Nothing already produced is lost, and nothing beyond the cap is charged.
Changelog
23 September 2026 — A search term or app id skipped because your spending cap was already
reached no longer carries an error. That row used to be delivered with
error: {category: "other", message: "Skipped: the spending limit for this run was reached."}; it now
carries error: null, says why in reviewsIncompleteReason: "spending-cap-reached" (with
reviewsComplete: false), and still counts as delivered and is still never charged. Your own limit is
not a failure of this Actor, and error now means one thing only: this row could not be delivered. A
row skipped because the Actor's circuit breaker tripped is unchanged — it still carries an error. If
you filter rows on error !== null to spot cap-skipped units, filter on
reviewsIncompleteReason === "spending-cap-reached" instead.
0.1 — Initial release.