# App Store and Google Play Compared: Rating, Reviews, Price (`accountable_eel/app-cross-store-lookup`) Actor

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.

- **URL**: https://apify.com/accountable_eel/app-cross-store-lookup.md
- **Developed by:** [Adriana Voss](https://apify.com/accountable_eel) (community)
- **Categories:** E-commerce, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 app checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## App Store and Google Play Compared: Rating, Reviews, Price

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

1. **In the Apify Console.** Open the actor page and click **Start** — the `apps` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   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"]}'
   ```
3. **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

```json
{
  "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 | <app name> | <storefront country> | \<on both stores?> | \<rating gap (app store minus google play)> | \<price gap (app store minus google play)> | <app store result> | <app store id> | <app store bundle id> | <name on the app store> | <developer on the app store> | <app store category> | <app store rating> | <app store ratings> | <app store version> | <app store last updated> | <app store price> | <app store currency> | <app store link> | <google play result> | <google play package> | <name on google play> | <developer on google play> | <google play category> | <google play rating> | <google play ratings> | <google play written reviews> | <google play installs> | <google play version> | <google play last updated> | <google play price> | <google play currency> | <google play in-app purchases> | <google play link> | <source url> | 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.**

```json
{
  "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:

```json
{
  "apps": ["324684580 com.spotify.music"],
  "country": "de"
}
```

**3. A rating watchlist with only the columns a weekly report needs.**

```json
{
  "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: false` and 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 (`BLOCKED` or `ERROR`) 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.** `playReviewCount` has no
  App Store twin.
- **`playVersion` is empty for apps whose version varies with the device.** `playUpdatedAt` is
  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.

```bash
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.

# Actor input Schema

## `apps` (type: `array`):

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. You're only charged for the ones we actually find — a miss costs nothing.

## `country` (type: `string`):

The two-letter country code of the storefront to read on both stores, such as us, gb, de, fr, jp or br. Ratings, prices and availability differ by country. Leave as us if unsure.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## Actor input object example

```json
{
  "apps": [
    "324684580 com.spotify.music",
    "479516143 com.mojang.minecraftpe"
  ],
  "country": "us",
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "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"
  ],
  "maxConcurrency": 3
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "apps": [
        "324684580 com.spotify.music",
        "479516143 com.mojang.minecraftpe"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/app-cross-store-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "apps": [
        "324684580 com.spotify.music",
        "479516143 com.mojang.minecraftpe",
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/app-cross-store-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "apps": [
    "324684580 com.spotify.music",
    "479516143 com.mojang.minecraftpe"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/app-cross-store-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/app-cross-store-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/KnxlQdctuqOhhFw6Q/builds/HCRNJWy40tec6nYz5/openapi.json
