App Store and Google Play Compared: Rating, Reviews, Price
Pricing
from $2.50 / 1,000 app checkeds
App Store and Google Play Compared: Rating, Reviews, Price
App Store vs Google Play rating comparison for a list of apps: compare an app's rating on iOS and Android, rating and review counts, Google Play installs, version, last update and price, with the rating gap worked out. Any country, no login. Export ratings to Google Sheets or CSV or via API in n8n.
Pricing
from $2.50 / 1,000 app checkeds
Rating
0.0
(0)
Developer
Adriana Voss
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Give it a list of apps and get back one row per app with both stores side by side: rating, number of ratings, version, last update and price on the Apple App Store and on Google Play, plus Google Play installs and written-review count, and the rating gap between the two already worked out. No API key, no login, no proxy, and an app that neither store has is never charged.
It answers the question a single-store scraper cannot: how is the same app doing on iOS compared with Android, right now, in this country.
Who uses it
- App marketers and ASO teams tracking their own app and competitors on both stores in one sheet, instead of two exports that have to be joined by hand.
- Product managers who want to see which platform is rated lower, and whether the Android and iOS releases have drifted apart.
- Investors and analysts screening a list of apps: installs, rating volume and price in one comparable row per company.
- Agencies building a weekly client report on app ratings across both stores.
- AI agents and automations that need a structured answer to "how is this app rated on each store?" in one call.
What you get
| Group | Columns |
|---|---|
| Comparison | name, country, onBothStores, ratingGap, priceGap |
| Apple App Store | appStoreStatus, appStoreId, appStoreBundleId, appStoreName, appStoreDeveloper, appStoreCategory, appStoreRating, appStoreRatingCount, appStoreVersion, appStoreUpdatedAt, appStorePrice, appStoreCurrency, appStoreUrl |
| Google Play | playStatus, playId, playName, playDeveloper, playCategory, playRating, playRatingCount, playReviewCount, playInstalls, playVersion, playUpdatedAt, playPrice, playCurrency, playInAppPurchases, playUrl |
| Every row | sourceUrl, scrapedAt |
ratingGap is the App Store rating minus the Google Play rating: 0.42 means the app is rated
0.42 stars higher on the App Store.
How to use App Store and Google Play Compared
- In the Apify Console. Open the actor page and click Start — the
appsfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~app-cross-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"apps":["324684580 com.spotify.music","479516143 com.mojang.minecraftpe"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
country sets the storefront read on both stores (two letters: us, gb, de, jp, ...).
Ratings, prices and availability differ by country, and us is used when you leave it alone.
Input
{"apps": ["324684580 com.spotify.music","479516143 com.mojang.minecraftpe"]}
One app per line: its App Store id and its Google Play package, separated by a space. The App Store id is the number after "id" in the app's App Store link, and the Google Play package is the part after "id=" in its Google Play link. You can paste the two links instead. A line with only one of the two returns that store alone. Accepted formats: 324684580 com.spotify.music, https://apps.apple.com/us/app/id479516143 https://play.google.com/store/apps/details?id=com.mojang.minecraftpe, com.spotify.music.
Each line names one app on both stores, in either order. All of these are read the same way:
| You write | Read as |
|---|---|
324684580 com.spotify.music | App Store id and Google Play package |
| the app's App Store link and its Google Play link on one line | the same, taken from the links |
com.spotify.music | Google Play only; the App Store columns stay empty |
324684580 | App Store only; the Google Play columns stay empty |
ios:com.spotify.client android:com.spotify.music | App Store bundle id instead of the numeric id |
The App Store id is the number after id in the app's App Store link. The Google Play package is
the part after id= in its Google Play link. The actor does not guess the matching app on the other
store: you name both, so a row never pairs two different apps.
Output
One row per app.
| query | found | status | name | country | onBothStores | ratingGap | priceGap | appStoreStatus | appStoreId | appStoreBundleId | appStoreName | appStoreDeveloper | appStoreCategory | appStoreRating | appStoreRatingCount | appStoreVersion | appStoreUpdatedAt | appStorePrice | appStoreCurrency | appStoreUrl | playStatus | playId | playName | playDeveloper | playCategory | playRating | playRatingCount | playReviewCount | playInstalls | playVersion | playUpdatedAt | playPrice | playCurrency | playInAppPurchases | playUrl | sourceUrl | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 324684580 com.spotify.music | true | OK | <on both stores?> | <rating gap (app store minus google play)> | <price gap (app store minus google play)> | 1970-01-01T00:00:00.000Z |
A real row from 2026-10-11, trimmed to the columns that tell the story:
| name | appStoreRating | playRating | ratingGap | appStoreRatingCount | playRatingCount | playReviewCount | appStoreVersion | playVersion | appStorePrice | playPrice | playInstalls |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Spotify: Music and Podcasts | 4.77175 | 4.348034381866455 | 0.42 | 42505656 | 36463640 | 1855748 | 9.1.90 | 0 | 0 | 1,000,000,000+ |
An empty cell is a real "not known", never a hidden zero. playVersion is empty here because
Google Play lists this app's version as "Varies with device"; an app with one fixed Android
version gets it filled. A 0 in a price column is the store's own answer: the app is free.
appStoreStatus and playStatus say what each store answered:
| Value | Meaning |
|---|---|
OK | The store returned the app |
NOT_FOUND | The store has no such app in this country |
NOT_REQUESTED | The line did not name the app on this store |
BLOCKED / ERROR | The store could not be read this time; the row is not charged |
An app that neither store has comes back as a row with found: false, a status and a message
that says why, and is never charged.
Example runs
Three inputs that return real rows with no key or account of your own.
1. The prefilled example: one free app and one paid app, both stores.
{"apps": ["324684580 com.spotify.music", "479516143 com.mojang.minecraftpe"]}
Two rows. The second shows a paid app with a fixed Android version, so appStorePrice,
playPrice, priceGap and playVersion are all filled.
2. The same app in another country. Ratings and prices are per storefront:
{"apps": ["324684580 com.spotify.music"],"country": "de"}
3. A rating watchlist with only the columns a weekly report needs.
{"apps": ["324684580 com.spotify.music","479516143 com.mojang.minecraftpe"],"columns": ["name", "appStoreRating", "playRating", "ratingGap", "appStoreRatingCount", "playRatingCount"]}
How an app gets billed
One event, app-checked, charged once per app row returned.
- The price is per app, not per store: a row with both stores costs the same as a row with one.
- An app that neither store has comes back
found: falseand is not charged. - A row where one store answered "no such app here" is charged: that is the answer.
- A row where a store could not be read (
BLOCKEDorERROR) is delivered with what the other store gave and is not charged. - Rows removed by your own keyword filters, or past "Stop after this many results", are not charged.
Pricing
Pay-per-event. A flat per-run fee covers session/proxy warmup; each independent
source is billed under its own event, only when that source actually returns
data — see .actor/pay_per_event.json for exact prices. A miss is never
charged.
$5 per 1,000 apps on the free plan, less on paid Apify plans. There is no per-store charge on top.
Limits worth knowing before you buy
- You name the app on both stores. There is no automatic matching of an iOS app to its Android twin, because the two stores share no common id and a wrong pairing would be worse than none.
- Review text is not included. The row carries counts and averages, not the reviews themselves.
- The App Store publishes a rating count, not a written-review count.
playReviewCounthas no App Store twin. playVersionis empty for apps whose version varies with the device.playUpdatedAtis filled for those apps and tells you when the last release went out.- Google Play installs are a band, such as
50,000,000+, exactly as the store shows it. - One country per run. To compare storefronts, run it once per country.
- No history. Each run is a snapshot; schedule it and keep the datasets to build a trend.
Frequently asked questions
How long does a run take? About a second per app. Apps are checked three at a time by default.
Do I need a proxy or an API key? No.
Can I look up an app that is only on one store? Yes. Write only that store's id on the line; the
other store's columns stay empty and its status reads NOT_REQUESTED.
Why does the rating differ from what I see in the store? Ratings are per country. Set country
to the storefront you are looking at.
Can I track ratings over time? Save the actor as a task, add a schedule, and compare the
appStoreRating, playRating and count columns between runs in your own sheet or workflow.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~app-cross-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"apps":["324684580 com.spotify.music","479516143 com.mojang.minecraftpe"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~app-cross-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"apps":["324684580 com.spotify.music","479516143 com.mojang.minecraftpe"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~app-cross-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"apps":["{{app}}"]}, mapping the row's app into the apps array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "App Store and Google Play app data, one row per app" — the agent will find and run this actor.