Apple App Store Review Scraper With Ratings By App Version avatar

Apple App Store Review Scraper With Ratings By App Version

Pricing

from $4.99 / 1,000 results

Go to Apify Store
Apple App Store Review Scraper With Ratings By App Version

Apple App Store Review Scraper With Ratings By App Version

🍎 Apple App Store Review Scraper extracts reviews, ratings, dates, versions & locales from the App Store. πŸ” Mine keywords & sentiment for ASO, CX & competitor research. πŸ“Š Export CSV/JSON, schedule jobs, automate insights. πŸš€ For PMs, marketers & analysts.

Pricing

from $4.99 / 1,000 results

Rating

0.0

(0)

Developer

API Empire

API Empire

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

0

Monthly active users

5 days ago

Last modified

Share

Apple App Store Review Scraper With Ratings By App Version reads customer reviews from any public App Store listing and rolls them up per build. Every run writes one review row per review β€” author, stars, build, title, text, dates, links β€” plus one version_trend row per version: review count, average stars, the 1β˜…β€“5β˜… spread, release notes on the live build, and the rating delta against the previous build with a drop flag. Output is typed, normalized JSON β€” no HTML, no parsing. Below: which toggle makes which row, and what is charged.

What is Apple App Store Review Scraper With Ratings By App Version?

It is an Apify Actor that reads Apple's keyless iTunes customer-reviews feed for the apps you point it at, then does the grouping work that a raw review dump leaves to you: it buckets the reviews it collected by the im:version value Apple stamps on each one, averages each bucket, and compares each build to the build before it. A second keyless call to Apple's iTunes lookup endpoint attaches the app's name, its live build number, that build's release date and its release notes.

No Apple ID, no App Store Connect account, no API key, no login and no cookie are used. Both endpoints the Actor talks to are the public ones any anonymous visitor's device can reach. There is nothing to authenticate and nothing to authorise β€” you paste App Store links and press Start.

  • Scrape reviews β€” one row per review with author, ratingNumber, version, title, content, dateIso, reviewAgeDays, reviewId, reviewUrl and reviewerProfileUrl
  • Scrape per-build quality trends β€” one row per app version with versionAverageRating, versionReviewCount, versionStars1–versionStars5, versionNegativeShare, ratingDeltaVsPrevious and ratingDropFlagged
  • Scrape release context β€” currentVersionReleaseDate, currentVersionReleaseNotes, appName, appLifetimeAverageRating and appLifetimeRatingCount, joined onto the trend rows
  • Narrow a run to a release window with a build-version filter and a date cutoff, on any storefront
  • Export as JSON, CSV or Excel β€” no proxy management, no parsing

What data does the App Store review scraper collect?

Three row shapes land in one dataset, each tagged with a type field so you can split them at read time. Review rows and build-trend rows are the two data products; accounting rows exist so that an app which produced nothing is still visible in the output rather than silently missing.

Data TypeKey FieldsJSON Field Names
Customer review (type: "review", isChild: false)One row per review: who wrote it, how many stars, which build it refers to, the full text, when it was posted and how old it is nowauthor, rating, ratingNumber, version, title, content, date, dateIso, reviewAgeDays, reviewId, reviewUrl, reviewerProfileUrl, appStoreUrl, appId, country, scrapedAt
Build quality trend (type: "version_trend", isChild: true)One row per app version seen in the collected window: volume, average stars, the full star histogram, the negative share, that build's review window, and the comparison against the build before itversion, versionRank, versionReviewCount, versionAverageRating, versionStars1, versionStars2, versionStars3, versionStars4, versionStars5, versionNegativeShare, versionFirstReviewAt, versionLastReviewAt, previousVersion, previousVersionAverageRating, ratingDeltaVsPrevious, ratingDropFlagged, comparisonSampleSize, parentId
Release context (merged onto every version_trend row)The app's display name and lifetime rating figures on every trend row; the live build's release date and release notes on the one trend row that matches itappName, isCurrentVersion, currentVersionReleaseDate, currentVersionReleaseNotes, appLifetimeAverageRating, appLifetimeRatingCount
Accounting row (type: "error", isChild: false)Written when an app yielded no usable reviews, or when a link could not be parsed as an App Store target. Never chargederrorReason, reviewsSeen, appStoreUrl, appId, country, scrapedAt

Two things about the shape that save debugging time later. Review rows never carry version* keys and trend rows never carry content or author, so a flat CSV export shows empty cells wherever a column belongs to the other row type β€” filter on type before you map fields. And rating is a string on review rows because that is exactly how Apple sends it in the feed; ratingNumber is the parsed integer, and it is null when the feed value would not parse.

Need more review data?

If you track a product on both stores, Google Play Store Review Scraper By Country Language answers the same release question on the Android side, where the version field behaves differently enough that you want a purpose-built reader rather than a shared one. Goodreads Review Scraper Rating Distribution Book Analytics applies the same rating-distribution treatment to titles rather than builds, and Fragrantica.com Scraper Reviews Sentiment Analysis covers long-form product reviews. When the complaint has already left the store, Instagram Mention Sentiment Analyzer and Reddit Posts Scraper Buyer Question Detection pick it up where users actually vent about a bad update.

How does this differ from the official App Store Connect API?

Apple's App Store Connect API is the supported route to your own reviews, and if the apps you care about are the apps you publish, use it β€” it is authenticated, it is covered by Apple's terms, and it is the only route that lets you reply to a review. This Actor exists for the other cases: competitor apps, apps you do not own, storefronts you are not enrolled in, and the per-build rollup that neither route hands you ready-made.

FeatureApp Store Connect APIApple App Store Review Scraper With Ratings By App Version
CredentialsAn App Store Connect account plus an API key: you generate a private key in App Store Connect and sign a JWT for every requestNone. Both endpoints used are keyless and anonymous
Which apps you can readApps inside your own App Store Connect accountAny app with a public App Store listing, including competitors
Reply to reviewsYes, through the customer review responses resourceNo β€” this Actor is read-only
Storefront selectionTerritory filter parameters on the requestTaken from each link's country segment β€” /gb/ reads the UK store, /jp/ reads Japan; one run mixes storefronts by mixing links
Per-build rollupNot returned as a rollup β€” aggregation is yours to writeComputed inside the run: versionAverageRating, ratingDeltaVsPrevious, ratingDropFlagged, and a versionStars1–versionStars5 histogram per build
Depth per appApple's own record of your reviewsThe public feed's window: up to 500 reviews per app per storefront, newest first (page 11 returns HTTP 400)
SetupKey generation, JWT signing, token refresh and rate-limit handling β€” consult Apple's current documentation for the limits that applyPaste links into the Console form, press Start

Choose the official API when you own the app and need a complete, authoritative, replyable review record for it. Choose this Actor when you need reviews for apps you do not own, when you want several storefronts in one dataset, or when the question you are asking is "did build 7.3.0 hurt us?" rather than "what did user X write?".

Why do teams scrape App Store reviews?

Review text on its own is a firehose. Review text attached to the build that provoked it is a release-quality signal, and that is what changes who finds this useful.

For mobile product and release teams

The workflow this Actor was shaped around: after a release, run it against your own listing with reviewsSince set to the release date, emitVersionTrendRows on, and minReviewsPerVersion high enough that a handful of angry early adopters cannot move the number. Sort the output by versionRank and read ratingDeltaVsPrevious down the column. A ratingDropFlagged: true on the live build, with versionStars1 climbing and versionNegativeShare above its usual level, is the fastest evidence you will get that a build regressed β€” and versionFirstReviewAt tells you when the complaints started arriving.

For AI engineers and agent builders

Every row is already the shape a retrieval pipeline wants. Index content with version, ratingNumber, dateIso and appId as metadata, and a RAG query like "what broke in 7.3.0?" filters to a single build before it ever reaches the model. The version_trend rows are the cheap half of the same system: a release-monitoring agent can poll them, branch on ratingDropFlagged, and pull the matching review rows into context only when the flag flips β€” a summarisation prompt over 40 rows instead of 500. No HTML parsing sits anywhere in that loop.

For ASO and competitive analysts

Point the Actor at competitors' listings and you get their release history as a quality curve rather than a version list. versionAverageRating per build, previousVersionAverageRating beside it, and currentVersionReleaseNotes on the live build let you line up what a competitor said they shipped against what their users reported afterwards. Run the same app ID across /us/, /gb/ and /de/ links to see whether a regression is global or localised β€” each storefront is a separate 500-review window, so the three sets do not overlap.

For researchers and market analysts

Everything returned is publicly posted review content: display names Apple already shows on the listing, star ratings, review text and timestamps. The per-build grouping gives longitudinal structure without a manual coding pass β€” versionStars1 through versionStars5 is a ready-made distribution per release, comparisonSampleSize states how many rated reviews each average rests on, and versionNegativeShare normalises the 1–2β˜… tail so builds with different review volumes stay comparable. Nothing behind a login is touched, and no data beyond what the store publishes is collected.

How to scrape App Store reviews by version (step by step)

The Actor runs on Apify. There are two ways to start it and no third: the Apify Console, or the Apify API with your Apify token. There is no separate signup, no vendor key and no external service to fund.

  1. Open the Actor on its Apify Store listing and click Try for free, or open it in the Apify Console if it is already in your account.
  2. Paste your App Store links into appStoreLinks β€” one per app. A full listing URL is best because the country segment decides the storefront; a bare numeric app ID also works and reads the US store.
  3. Set the release window. reviewsPerApp controls depth (default 200, ceiling 500). reviewsSince accepts an absolute date or a relative string such as 30 days. buildVersions keeps only the builds you name, and a partial version matches its whole branch β€” 5.40 keeps 5.40.0 and 5.40.1 but not 5.401.
  4. Tune the comparison. minReviewsPerVersion (default 5) is the sample size both builds must clear before a delta is computed at all; ratingDropThreshold (default 0.25 stars) is how far an average has to fall before ratingDropFlagged turns true.
  5. Start the run, then export. Rows are pushed live, so the dataset fills while the run is going. Download as JSON, CSV or Excel from the Console, or read the dataset through the Apify API.

What to do when Apple changes the feed

The Actor is maintained and the output keys are treated as a contract β€” field names and types do not change on your end. It reads two JSON endpoints rather than parsing App Store HTML, which is the layer least exposed to a store redesign. If a page stops responding the run logs it, moves to the next app, and finishes with whatever it collected rather than failing the whole job.

⬇️ Input

Every parameter below is copied from the Actor's published input schema in .actor/actor.json. None of them is required by the schema β€” but a run with no links resolves to nothing, logs an error and exits without pushing a single row, so treat appStoreLinks as required in practice.

ParameterRequiredTypeDescriptionExample Value
appStoreLinksNoarray (stringList)App Store page links, one per app. A bare numeric app ID (e.g. 1449777194) also works. The storefront comes from the link's country code β€” /gb/ reads the UK store, no country code means us. No default; prefilled in the Console with one example link.["https://apps.apple.com/us/app/quicken-simplifi-budget-smart/id1449777194"]
reviewsPerAppNointegerHow many reviews to pull for each app, newest first. Apple's feed stops at 500 per app per storefront (page 11 returns an error), so 500 is the real ceiling. Default 200, minimum 1, maximum 500.500
buildVersionsNoarray (stringList)Keep only reviews written on the listed builds. A partial version matches its whole branch: 5.40 keeps 5.40.0 and 5.40.1 but not 5.401. Leave empty to keep every build. Default: empty.["7.3.0", "7.2"]
reviewsSinceNostring (datepicker, absoluteOrRelative)Cut the run down to a release window β€” an absolute date, or a relative one such as 30 days. Because the feed is newest-first, paging stops as soon as a whole page falls outside the window. No default; leave empty for no date limit."30 days"
minReviewsPerVersionNointegerA build is compared to the one before it only when both builds have at least this many rated reviews in the collected window. Below the threshold the delta and the drop flag stay null. Default 5, minimum 1, maximum 200.10
ratingDropThresholdNonumberStars. A build whose average rating is at least this far below the previous build gets ratingDropFlagged: true. 0.25 flags a fall from 4.6β˜… to 4.35β˜…. Default 0.25, minimum 0.01, maximum 4.0.5
emitVersionTrendRowsNobooleanAdds the per-version rollup rows. Turn off to get review rows only. Default true.true
includeReleaseContextNobooleanLooks the app up on Apple's keyless lookup endpoint and attaches the live build number, its release date and its release notes to the matching trend row, plus the app name and lifetime rating figures to every trend row. One extra request per app. Default true.true
pagesPerAppNointegerEach page holds up to 50 reviews and Apple refuses page 11, so 10 pages (500 reviews) is the hard maximum. Default 10, minimum 1, maximum 10.10
pauseBetweenPagesSecondsNonumberWait between feed pages. Lower is faster, higher is gentler on the store. A random 0.3–1.0 s is added on top of whatever you set. Default 1.5, minimum 0, maximum 30.1.5
proxyConfigurationNoobject (proxy)Optional Apify Proxy settings. The feed works without a proxy and the run always starts without one; these settings are consulted only if the store throttles and the Actor escalates. Prefilled with {"useApifyProxy": false}.{"useApifyProxy": true}

Compatibility keys that are not in the form

This Actor is a superset of a simpler base Actor, and it still reads that base Actor's input keys so old task definitions keep working. They do not appear in the Console form, but if they are present in the input JSON they win over the documented parameter:

Hidden keyOverridesEffect
url, urlsβ€”Merged ahead of appStoreLinks into one deduplicated, ordered link list. Accepts a newline- or comma-separated string, a list of strings, or a list of {"url": "…"} objects
max_review, maxReviewsreviewsPerAppFirst one present wins; the schema's 1–500 bounds are not applied to it, so a value above 500 is capped in code with a warning in the log
max_pages, maxPagespagesPerAppFirst one present wins, then clamped into 1–10
delay_between_pages, delayBetweenPagesSecondspauseBetweenPagesSecondsFirst one present wins; no upper bound is applied

If you build the run input by hand and one of these is left over from an older integration, the Console form value you think you set is ignored silently. Remove them unless you specifically want the legacy behaviour.

Common pitfalls

A bare app ID always reads the US store. country is parsed out of the apps.apple.com/xx/ segment; with no segment, the storefront falls back to us. To read the UK store you need a full link containing /gb/ β€” there is no separate country parameter. The same app ID can appear several times in the list under different country segments, and each one is scraped as its own app with its own 500-review window.

pagesPerApp can silently cap reviewsPerApp. Pages hold up to 50 reviews, so pagesPerApp: 2 means roughly 100 reviews no matter what reviewsPerApp says. Leave pagesPerApp at 10 unless you are deliberately shortening the run.

A narrow buildVersions filter still costs the full page walk. The build filter runs after a review has been fetched, and reviewsPerApp counts kept rows, so filtering down to one old build usually means paging all ten pages before the run stops. Pair it with reviewsSince when you can.

An unreadable reviewsSince disables the filter rather than failing the run. If the cutoff cannot be parsed, the Actor logs ⚠️ Could not read the review-date cutoff … and runs with no date limit at all β€” a much bigger, fully charged run than you asked for. Check that log line before assuming your window applied.

Sending 0 through the API is not the same as sending nothing. minReviewsPerVersion: 0 becomes 5 and ratingDropThreshold: 0 becomes 0.25, because the code falls back on any falsy value. Both are outside the schema's minimums anyway (1 and 0.01), so send a valid value rather than a zero.

proxyConfiguration does not force a proxy on. Every app starts on a direct connection regardless of what you set here. Your settings are used only when the escalation ladder decides a proxy is needed.

Example input

{
"appStoreLinks": [
"https://apps.apple.com/us/app/quicken-simplifi-budget-smart/id1449777194",
"https://apps.apple.com/gb/app/quicken-simplifi-budget-smart/id1449777194"
],
"reviewsPerApp": 500,
"buildVersions": ["7.3.0", "7.2"],
"reviewsSince": "90 days",
"minReviewsPerVersion": 10,
"ratingDropThreshold": 0.25,
"emitVersionTrendRows": true,
"includeReleaseContext": true,
"pagesPerApp": 10,
"pauseBetweenPagesSeconds": 1.5,
"proxyConfiguration": { "useApifyProxy": false }
}

⬆️ Output

Typed, normalized JSON with a stable shape per type. Rows are pushed as they are found β€” review rows during the page walk, trend rows once an app's collection finishes β€” so the dataset fills while the run is still going. Export as JSON, CSV or Excel from the Console, or read the dataset through the Apify API.

The default dataset view lists 43 columns. That list is the exact union of the three row shapes below: there are no hidden keys the view omits, and no column in the view that the code never writes.

Review rows

One row per review, written in the key order below. rating is the raw feed string and ratingNumber is the parsed integer, which is null if the feed value would not parse. date is Apple's own timestamp with the store's UTC offset intact; dateIso is the same instant normalised to UTC, and reviewAgeDays is the age in days at the moment the row was written, rounded to one decimal.

{
"author": "budget_nerd_92",
"rating": "2",
"version": "7.3.0",
"title": "Sync broke after the update",
"content": "Three years on this app and the bank sync had never dropped once. Since 7.3.0 it loses the connection to two of my four accounts every morning and I have to re-link them by hand. Rolling back is not an option so I am stuck waiting on a fix.",
"date": "2026-07-18T09:41:07-07:00",
"reviewId": "12648810553",
"reviewUrl": "https://itunes.apple.com/us/review?id=1449777194&type=Purple+Software",
"reviewerProfileUrl": "https://itunes.apple.com/us/reviews/id118392017",
"ratingNumber": 2,
"dateIso": "2026-07-18T16:41:07Z",
"reviewAgeDays": 6.8,
"appStoreUrl": "https://apps.apple.com/us/app/quicken-simplifi-budget-smart/id1449777194",
"appId": "1449777194",
"country": "us",
"type": "review",
"isChild": false,
"scrapedAt": "2026-07-25T11:02:44Z"
}

Reviews are de-duplicated by reviewId inside a single app's collection, so the same review cannot appear twice even when Apple repeats an entry across pages. De-duplication is per app and per storefront β€” the same review can legitimately appear once under country: "us" and once under another store if you scrape both.

Build-trend rows

One row per distinct app version found among the reviews that were actually kept. versionRank is 1 for the newest build in the collected window and counts upward into the past; the rows are emitted in that order. Ordering is derived from the earliest review timestamp in each build's bucket, not from parsing the version string, so unusual numbering schemes cannot mis-order the timeline.

{
"type": "version_trend",
"isChild": true,
"version": "7.3.0",
"versionRank": 1,
"versionReviewCount": 41,
"versionAverageRating": 2.78,
"versionStars1": 14,
"versionStars2": 6,
"versionStars3": 5,
"versionStars4": 7,
"versionStars5": 9,
"versionNegativeShare": 0.4878,
"versionFirstReviewAt": "2026-07-09T02:18:55Z",
"versionLastReviewAt": "2026-07-24T21:07:31Z",
"previousVersion": "7.2.1",
"previousVersionAverageRating": 4.12,
"ratingDeltaVsPrevious": -1.34,
"ratingDropFlagged": true,
"comparisonSampleSize": 41,
"appStoreUrl": "https://apps.apple.com/us/app/quicken-simplifi-budget-smart/id1449777194",
"appId": "1449777194",
"country": "us",
"parentId": "1449777194",
"appName": "Quicken Simplifi: Budget Smart",
"isCurrentVersion": true,
"currentVersionReleaseDate": "2026-07-08T17:00:00Z",
"currentVersionReleaseNotes": "β€’ Rebuilt the bank connection layer\nβ€’ Faster transaction categorisation\nβ€’ Fixes for a crash on the Spending tab",
"appLifetimeAverageRating": 4.6,
"appLifetimeRatingCount": 51284,
"scrapedAt": "2026-07-25T11:02:51Z"
}

How to read the comparison fields, because three of them are deliberately nullable:

  • ratingDeltaVsPrevious and ratingDropFlagged are null, not 0 and false, whenever the comparison was not made. That happens for the oldest build in the window (no predecessor exists), and whenever either build has fewer rated reviews than minReviewsPerVersion. A null here means "not enough evidence", which is a different statement from "no change" β€” keep them apart in whatever you build downstream.
  • comparisonSampleSize is the number of reviews with a parseable star rating, and it is what versionAverageRating and versionNegativeShare are computed over. versionReviewCount counts every review in the bucket including any whose rating failed to parse, so the two can differ.
  • versionNegativeShare is (versionStars1 + versionStars2) / comparisonSampleSize, rounded to four decimals, and null when nothing in the bucket was rated.

And how the release-context fields behave:

  • currentVersionReleaseDate and currentVersionReleaseNotes are populated on exactly one row per app β€” the trend row whose version matches the live build reported by Apple's lookup endpoint. Every other trend row carries null for both, by design.
  • isCurrentVersion is tri-state. true on the live build, false on the others, and null when the live build is unknown β€” either because includeReleaseContext is off, or because the lookup call did not return, which logs ⚠️ Release context unavailable for app … and lets the run continue.
  • appName, appLifetimeAverageRating and appLifetimeRatingCount appear on every trend row and are null when release context is off or unavailable. The lifetime figures are Apple's own store-wide totals for the app β€” they are not derived from the reviews in your window, and they will not match versionAverageRating.
  • Review rows never carry any of these fields. If you want app names on review rows, join on appId.

A trend row that fell below the sample threshold looks like this β€” same shape, comparison suppressed:

{
"type": "version_trend",
"isChild": true,
"version": "7.1.4",
"versionRank": 4,
"versionReviewCount": 3,
"versionAverageRating": 4.667,
"versionStars1": 0,
"versionStars2": 0,
"versionStars3": 0,
"versionStars4": 1,
"versionStars5": 2,
"versionNegativeShare": 0.0,
"versionFirstReviewAt": "2026-05-02T11:44:09Z",
"versionLastReviewAt": "2026-05-19T08:12:40Z",
"previousVersion": "7.1.3",
"previousVersionAverageRating": 4.51,
"ratingDeltaVsPrevious": null,
"ratingDropFlagged": null,
"comparisonSampleSize": 3,
"appStoreUrl": "https://apps.apple.com/us/app/quicken-simplifi-budget-smart/id1449777194",
"appId": "1449777194",
"country": "us",
"parentId": "1449777194",
"appName": "Quicken Simplifi: Budget Smart",
"isCurrentVersion": false,
"currentVersionReleaseDate": null,
"currentVersionReleaseNotes": null,
"appLifetimeAverageRating": 4.6,
"appLifetimeRatingCount": 51284,
"scrapedAt": "2026-07-25T11:02:51Z"
}

Accounting rows

When an app produces no usable reviews, or a link cannot be parsed, the run writes a short row instead of leaving a silent gap. These rows are not charged.

{
"type": "error",
"isChild": false,
"appStoreUrl": "https://apps.apple.com/jp/app/quicken-simplifi-budget-smart/id1449777194",
"appId": "1449777194",
"country": "jp",
"errorReason": "no_reviews_matched",
"reviewsSeen": 143,
"scrapedAt": "2026-07-25T11:03:12Z"
}

errorReason takes exactly three values:

errorReasonMeaning
no_reviews_foundThe feed returned nothing for this app in this storefront. reviewsSeen is 0
no_reviews_matchedReviews were fetched but every one was filtered out by reviewsSince or buildVersions. reviewsSeen is how many were examined β€” a non-zero value here means your window was too narrow, not that the app has no reviews
invalid_app_urlThe link contained neither an /id<digits> segment nor a bare numeric ID of six or more digits. appId and country are null, reviewsSeen is 0, and the run continues with the next link

To drop them at read time, filter on type:

rows = [r for r in items if r["type"] != "error"]

The trend mirror dataset

When emitVersionTrendRows is on, the Actor also writes the trend rows to a second, named dataset called <runId>-version-trends, so you can pull the rollup on its own without filtering the main dataset. It is written uncharged. Note that it is a named dataset created per run, so it does not expire the way the default unnamed dataset does β€” if you run this Actor on a schedule, prune those datasets periodically or they will accumulate in your account's storage.

πŸ’° What a run charges

This Actor is priced per event. There is exactly one charged event, row_result, and it fires once per data row pushed to the dataset:

  • every review row β†’ 1 charge
  • every version_trend row β†’ 1 charge
  • every type: "error" accounting row β†’ not charged

A run over one app with reviewsPerApp: 200, emitVersionTrendRows: true and 9 distinct builds among those reviews therefore charges 200 + 9 = 209 events. Turning emitVersionTrendRows off removes the trend rows and their charges, and the mirror dataset with them. The trend-row count is not something you set directly β€” it equals the number of distinct non-empty version values among the reviews you actually kept, so a deeper reviewsPerApp produces more of them.

Failures do not bill. An app that returns nothing, a storefront with no reviews, a window that filters everything out and a malformed link all produce an uncharged accounting row. A run with no resolvable links at all pushes nothing and charges nothing.

How does this Actor compare to other App Store review scrapers?

The nearest listings by search intent are general App Store review scrapers. The table below states their capabilities as documented on their own Apify Store listings, checked on the Apify Store 2026-07-25 β€” nothing here is measured, and no speed, price or success-rate claim is being made about either of them.

FeatureApple App Store Review Scraper With Ratings By App VersionOther App Store review scrapers, as listed on 2026-07-25
Per-build rollup rowsβœ… One version_trend row per build with versionAverageRating, the versionStars1–versionStars5 histogram and versionNegativeShare❌ Not documented on either listing. riadh_chebbi/apple-app-store-reviews-scraper documents one item per review; scrapers_lat/app-store-reviews-scraper documents review rows that carry the app profile
Release-over-release comparisonβœ… previousVersion, previousVersionAverageRating, ratingDeltaVsPrevious, ratingDropFlagged, gated by minReviewsPerVersion❌ Not documented on either listing, though riadh_chebbi names "release and version tracking" as a use case
Version handling on review rowsβœ… version on every review row, plus a branch-prefix filter via buildVersionsriadh_chebbi documents appVersion per review; no version filter is documented on either listing
Date-window filteringβœ… reviewsSince takes an absolute date or a relative string, and paging stops once a whole page falls outside the window❌ Not documented on either listing
App discovery by search term❌ Links and numeric IDs onlyβœ… Both document it β€” scrapers_lat accepts a search term, riadh_chebbi accepts appNames resolved through Apple search
Multi-storefront in one runβœ… Mix links with different country segments; each is its own windowβœ… Both document a country list β€” riadh_chebbi documents countries, scrapers_lat documents a country code setting
Sort orderNewest-first only (sortby=mostrecent)Both document a most-recent / most-helpful choice
Release notes attachedβœ… currentVersionReleaseNotes and currentVersionReleaseDate on the live build's trend rowscrapers_lat documents "latest release notes" on every review row

Read that honestly: if what you need is reviews, with app discovery by name and a helpfulness sort, those listings document features this Actor does not have. What this one does that neither documents is turn the review stream into a per-build quality series with a computed, sample-gated delta. If you are building an AI agent or a RAG pipeline, the output-format row is the decision-maker β€” parsing HTML inside an agent loop is a reliability failure mode, not a feature, and every row here arrives as typed JSON.

How many reviews can you scrape per app?

500 per app per storefront, and that ceiling belongs to Apple, not to this Actor. The public customer-reviews feed serves up to 50 reviews per page and refuses page 11 with HTTP 400, so ten pages is everything there is. reviewsPerApp is capped at 500 in the schema for that reason and pagesPerApp is capped at 10; a larger value arriving through the legacy max_review key is capped in code with a warning in the log.

Pagination is handled for you. The Actor walks pages 1 upward, stops as soon as it has kept reviewsPerApp rows, stops when Apple answers 400 or 404, and stops after two consecutive pages with no review entries. With reviewsSince set it also stops early the moment an entire page falls outside your window, since the feed is sorted newest-first.

There is no cap on the number of apps in a run. appStoreLinks takes as many links as you want and they are processed one at a time, with pauseBetweenPagesSeconds plus a small random jitter between pages. Total rows for a run are (reviews kept) + (distinct builds among them) summed across apps, plus one uncharged accounting row for anything that came back empty.

To go deeper than 500 on a single app, add the same app ID again under a different country segment. Each storefront maintains its own feed and its own window, so /us/, /gb/ and /de/ links give you three independent sets rather than three copies of one.

Integrate the Actor and automate your workflow

Apple App Store Review Scraper With Ratings By App Version works with any language or tool that can send an HTTP request, because starting an Apify Actor run is an HTTP call. The only credential involved is your own Apify API token.

REST API integration

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/apple-app-store-review-scraper-with-version-quality-trends").call(run_input={
"appStoreLinks": ["https://apps.apple.com/us/app/quicken-simplifi-budget-smart/id1449777194"],
"reviewsPerApp": 500,
"reviewsSince": "90 days",
"minReviewsPerVersion": 10,
"ratingDropThreshold": 0.25,
"emitVersionTrendRows": True,
"includeReleaseContext": True,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["type"] == "version_trend" and row.get("ratingDropFlagged"):
print(row["version"], row["versionAverageRating"], row["ratingDeltaVsPrevious"], row["versionReviewCount"])

Works in Python, Node.js, Go, Ruby, cURL. Branch on type to route review rows and trend rows to separate handlers, and skip type == "error" rows β€” those are the uncharged accounting rows.

Automation platforms (n8n, Make, LangChain)

In n8n, the Apify node runs the Actor and returns dataset items directly; put a Switch node on type to split reviews from trend rows, then an IF node on ratingDropFlagged so only flagged builds reach a Slack or email step. In Make, the Apify module supports run-and-wait, which is enough to push a weekly release check into Google Sheets or Airtable without glue code. For LangChain, CrewAI and similar frameworks, wrap the Apify run endpoint as a tool β€” the response is already typed JSON, so there is no parsing step between the Actor and the model, and content with version and dateIso metadata drops straight into a vector store. Apify schedules cover the recurring post-release check, and webhooks fire your endpoint the moment a run finishes, so a drop flag can page someone the same day.

Reading publicly published review pages is broadly treated as permissible where no authentication is bypassed, and this Actor bypasses nothing β€” it reads Apple's keyless public endpoints with no account, no login and no cookie.

The caveat is what the review rows contain. author is a reviewer's public display name and reviewerProfileUrl points at their public reviews page; together with content, that is user-generated content attributable to an individual, which brings it inside GDPR, CCPA and equivalent personal-data regimes. Storing it needs a lawful basis, and the usual obligations β€” retention limits, deletion requests, purpose limitation β€” apply to your copy just as they would to any other personal dataset. If your use case only needs the release signal, the version_trend rows contain no personal data at all: they are counts, averages and dates. Turning off review-row storage and keeping only the trend rows is a genuine minimisation option, not a fig leaf.

Apple's App Store terms govern how the data may be reused. Consult legal counsel for commercial use cases involving bulk personal data.

❓ Frequently asked questions

Does this Actor work without an Apple account or App Store Connect access?

Yes, and there is no way to give it credentials even if you wanted to. It reads Apple's public iTunes customer-reviews feed and, when includeReleaseContext is on, Apple's public iTunes lookup endpoint. Both are keyless. You do not need an Apple ID, an App Store Connect account, an API key or a developer enrolment, and you do not need to own the apps you point it at.

How often is the scraped data updated?

Every run fetches live from Apple. Nothing is cached between runs, and each row carries scrapedAt as a UTC ISO timestamp for exactly when it was written. The build-trend numbers are computed from the reviews collected in that run, so they are a snapshot of a moving window rather than a permanent record β€” re-run on a schedule if you want a time series, and keep reviewsPerApp and reviewsSince constant between runs so consecutive snapshots stay comparable.

What happens if an app has no reviews, or my filters match nothing?

You get a row, not silence. The Actor pushes a type: "error" accounting row carrying errorReason β€” no_reviews_found when the feed returned nothing, no_reviews_matched when reviews were fetched but every one was filtered out β€” plus reviewsSeen, which tells you how many were examined before filtering. A non-zero reviewsSeen with no_reviews_matched means your reviewsSince or buildVersions window was too narrow. These rows are not charged, and the run carries on to the next app.

Why does a build have an average but no ratingDeltaVsPrevious?

Because the comparison was deliberately withheld. A delta and a drop flag are computed only when the build and its predecessor each have at least minReviewsPerVersion reviews with a parseable rating; otherwise both fields stay null rather than reporting a swing computed from two or three reviews. The oldest build in the collected window is always null too, since there is nothing before it to compare against. Check comparisonSampleSize on both rows to see which side fell short, then lower minReviewsPerVersion or raise reviewsPerApp if you want the comparison anyway.

Can I get reviews for a specific storefront, or reviews behind a login?

Storefronts, yes β€” put the country code in the link, and every /gb/, /de/ or /jp/ link is scraped as its own app with its own feed. There is nothing behind a login to get: App Store reviews are public, and this Actor only ever reads what an anonymous visitor sees. It cannot read TestFlight feedback, App Store Connect analytics, developer responses to reviews, or reviews for an unreleased build, and it cannot post a reply.

How do I get more than 500 reviews for one app?

Add the same app under more storefronts. Apple serves a maximum of 500 reviews per app per store β€” 50 per page, and page 11 returns HTTP 400 β€” so a single link is a hard 500 ceiling regardless of settings. Passing the same app ID with /us/, /gb/, /ca/ and /au/ segments gives you four separate windows in one run, each with its own country value and its own set of trend rows.

Does it work for AI agent workflows and LLM pipelines?

Yes. It is callable as a standard HTTP endpoint through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step. The two row shapes map cleanly onto two tool responses: hand an agent the version_trend rows for the cheap "did anything regress?" check, and pull the matching review rows into context only when ratingDropFlagged is true.

How does it handle throttling from Apple's store?

With an escalation ladder that starts on the cheapest rung. Every app begins on a direct connection with no proxy at all. If a page comes back 403 or 429, or the request fails outright, the Actor moves to an Apify datacenter proxy and then to Apify residential β€” and once it reaches residential it stays there for the rest of the run rather than flapping back. On the residential rung it retries a page up to three times with exponential backoff plus jitter before logging the failure and moving on. Requests carry a browser-grade TLS fingerprint where the runtime supports it, falling back to a plain HTTP client otherwise, and a configurable pause plus random jitter separates pages. A 400 or 404 is treated as "no further pages for this app", not as a block.

How does this compare to the other App Store review scrapers on Apify?

Checked on the Apify Store on 2026-07-25: scrapers_lat/app-store-reviews-scraper documents app discovery by ID, URL or search term, a most-recent / most-helpful sort, and review rows that carry the full app profile including latest release notes. riadh_chebbi/apple-app-store-reviews-scraper documents appNames, appIds, appUrls, a countries list, maxReviewsPerApp and sortBy, with one item per review including appVersion and helpful-vote counts. Neither listing documents per-build rollup rows, a release-over-release delta, a build-version filter or a date-window filter. That is the difference: those Actors give you a richer review row, this one gives you the review row plus the per-build series computed on top of it. Not measured here β€” that is what their listings said on that date.

Does it return data LLMs can use directly?

Yes. Every row is typed, normalized JSON with stable key names β€” no HTML, no selectors, no parsing step. content is the plain review text, ratingNumber is an integer, dateIso is a UTC ISO-8601 timestamp, and the trend fields are numbers rather than formatted strings. Pass a row straight into an LLM context window, index it into a vector store with version and appId as metadata, or hand it to an agent tool.

Can I use it without managing proxies?

Yes, and the default configuration expects you to. The feed is keyless and works from a direct connection, proxyConfiguration is prefilled with useApifyProxy: false, and the escalation ladder handles the exception case on its own. You never create a proxy account or rotate an IP. Setting a country in proxyConfiguration only localises the residential rung, and only if the ladder ever climbs that far.

What happens when Apple changes the feed or blocks the scraper?

The Actor is maintained and the output keys stay stable β€” field names and types do not change on your end. It reads two JSON endpoints rather than parsing App Store HTML, so a store redesign generally does not reach it. Per-app failures are isolated: a page that will not load is logged, the app finishes with whatever it collected, and the run continues to the next link rather than dying.

Scraper NameWhat it extracts
Google Play Store Review Scraper By Country LanguageAndroid app reviews split by country and language
Goodreads Review Scraper Rating Distribution Book AnalyticsBook reviews with the full rating distribution per title
Fragrantica.com Scraper Reviews Sentiment AnalysisLong-form product reviews with sentiment scoring
Instagram Mention Sentiment AnalyzerBrand mentions with sentiment, for complaints that left the store
Reddit Posts Scraper Buyer Question DetectionThreads and questions where users discuss a product or a release

πŸ’¬ Your feedback

Found a bug, or missing a field you need on the trend rows? Open an issue on the Actor's Issues tab β€” that is where bug reports and field requests are tracked. Reports that include the exact input JSON and the App Store link you targeted are the fastest to reproduce, because storefront and build-version behaviour usually only shows up on the specific app that triggered it.