# App Reviews API: Google Play Reviews + App Store Reviews (`hiver/app-reviews-scraper`) Actor

App reviews API and app review scraper for Google Play and the App Store: package names, App Store IDs or URLs and iOS bundle IDs in one list, one table of reviews out (rating, text, date, app version, developer reply). Filter by rating, country, date; only-new mode. $0.09/1k reviews. Unofficial.

- **URL**: https://apify.com/hiver/app-reviews-scraper.md
- **Developed by:** [hiver](https://apify.com/hiver) (community)
- **Categories:** Marketing, Developer tools, Lead generation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.08 / 1,000 review returneds

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 Reviews API: Google Play Reviews + App Store Reviews in One Table

**Reviews of any public app on Google Play and the Apple App Store, from one API call.** Put Google Play package names (`com.spotify.music`), App Store IDs or URLs and iOS bundle IDs (`com.spotify.client`) in the same list and get one row per review with the same columns: store, app, rating, title, text, date, app version, developer reply. Newest first, with filters and an **only new since last run** mode for monitoring.

**$0.09 per 1,000 reviews from either store.** The three most-used review Actors on Apify Store (455, 321 and 307 monthly users) each cover one store, at $0.10 to $0.15 per 1,000 on the free plan (Store data checked 2026-10-10). Error rows and reviews you already have are free.

**Nigeria, 300 newest App Store reviews: 300 of 300 from the Nigerian storefront, 0 US reviews, past the 150 mark** (Lab bench, 2026-10-10). Wrong-country reviews and runs that stop near 150 reviews are two problems buyers report on the biggest review Actors' Issues tabs, so our board's Lab bench now tests both.

**Measured, not promised.** On 2026-10-10 (08:21Z, build 0.1.8) the bench gave this Actor and popular review Actors the same jobs on both stores. For OPay, a Nigerian app, it checked against Apple's own Nigerian review list that the reviews at positions 160 and 240 are returned and that none of the US store's 4 newest reviews are. The other checks: the right app, star ratings 1-5, 2026 dates, the app version and, on Google Play, developer replies.

| App Store job (same input for every Actor) | This Actor | Most-used App Store review Actor | Other Actor that covers both stores |
|---|---|---|---|
| Nigeria, OPay, 300 newest | **300 reviews, 5 of 5 checks**, 85% fields filled, 57 s | 300 reviews, 5 of 5 checks, 71% fields filled, 183 s | 0 reviews |
| US, Spotify, 20 newest | 20 reviews, 3 of 4 checks, 71% fields filled, 7 s | 20 reviews, 3 of 4 checks, 71% fields filled, 41 s | 20 reviews, 4 of 4 checks, 86% fields filled, 3 s |
| Price per 1,000 reviews in these runs | **$0.09** | $0.10 | $0.44 |

The most-used App Store Actor also returned all 300 Nigerian reviews: on country and depth it ties with this Actor, which filled more fields and finished about 3 times faster.

| Google Play job (same input for every Actor), 2026-10-10 | This Actor | Most-used Google Play review Actor | Other Actor that covers both stores |
|---|---|---|---|
| OPay, 300 newest English reviews | **300 reviews, 4 of 4 checks**, 97% fields filled, 20 s | not measured | 300 reviews, 3 of 4 checks (no developer replies), 83% fields filled, 6 s |
| Spotify, 20 newest English reviews | 20 reviews, 4 of 4 checks, 85% fields filled, 7 s | not measured | 20 reviews, 3 of 4 checks (no developer replies), 84% fields filled, 3 s |
| Price per 1,000 reviews in these runs | **$0.09** | not measured | $0.43 |

On Google Play the other two-store Actor was faster; this Actor returned developer replies and filled more fields, at about a fifth of the price. Google Play lists reviews per language, not per country, so these jobs test depth and fields, not country. The most-used Google Play review Actor is not measured yet: it never started, because the bench sent it a lowercase country code its input does not accept. That was the bench's mistake, not that Actor's; its results go here after the next bench. Two jobs per store is a small sample; the bench runs again after every code change.

![Real output rows: Spotify reviews from Google Play and the App Store in the same columns](https://raw.githubusercontent.com/hiver-data/app-reviews-scraper/main/media/app-reviews-output/output.gif)

[Watch the 1-minute walkthrough (MP4)](https://github.com/hiver-data/app-reviews-scraper/blob/main/media/example-app-reviews/walkthrough.mp4)

Unofficial. Not affiliated with, endorsed by or sponsored by Google or Apple. It reads the public review pages and endpoints the stores show to anyone without logging in. It returns review content only: no reviewer profiles, no reviewer photos or IDs.

### What you can do with it

- **Watch your own app and your competitors' on both stores** in one run and one schema.
- **Pull every 1-2 star review since a date** to see what broke after a release.
- **Schedule it hourly or daily with "only new"** and send new reviews to Slack, email, Google Sheets or a webhook with Apify integrations.
- **Feed an AI agent** one call that returns reviews from both stores in a single shape.

### App Store reviews by bundle ID

Know your iOS apps by bundle ID (`com.spotify.client`) rather than by their numeric App Store ID? Paste the bundle IDs into `apps`. When Google Play has no app with that ID, the Actor looks it up on the App Store in the run's country (the lookup is free) and returns its reviews. Every App Store row carries `bundleId`, so you can join reviews to App Store Connect exports or your own app list. An ID found on neither store returns one free row that says so.

Board test run W9GrglUWNc1kah7ue, 2026-10-11, build 0.1.12: `com.spotify.client` and `com.calm.calmapp` (App Store only), `ios:com.zhiliaoapp.musically`, the Play package `com.spotify.music` and a made-up ID in one list gave 12 reviews, each with the right `bundleId` or `null` on Google Play, and 1 free error row ("not found on Google Play or the App Store (us)") in 20 s. One of the App Store rows, as returned:

```json
{ "store": "apple", "appId": "324684580", "appName": "Spotify: Music and Podcasts", "bundleId": "com.spotify.client",
  "country": "us", "reviewId": "14647223396", "rating": 5, "title": "Best streaming app", "text": "Best streaming app",
  "date": "2026-10-09T20:37:39Z", "appVersion": "9.1.90", "isEdited": false, "developerReply": null, "error": null }
```

Some apps use the same ID on both stores (TikTok `com.zhiliaoapp.musically`). Without a prefix you get the Google Play app; write `ios:` in front, as the test run did, or set `dottedIdStore` to `app_store` to get the App Store one. The three most-used Actors made only for App Store reviews on Apify Store (459, 121 and 58 monthly users) take numeric App Store IDs or app URLs (one also takes an app name); none of their input schemas mentions bundle IDs (latest builds, checked 2026-10-11). Some general App Store scrapers do take bundle IDs.

### Input

| Field | What it does |
|---|---|
| `apps` | App URLs or IDs, mixed stores in one list. Google Play: `com.spotify.music` or a `play.google.com` URL. App Store: `id324684580`, `324684580`, an `apps.apple.com` URL or an iOS bundle ID (`com.spotify.client`, looked up free on the App Store when Google Play has no such app). Force a store with `ios:` or `android:`. |
| `dottedIdStore` | What unprefixed dotted IDs are: `auto` (default: Google Play first, then the App Store), `app_store` (all are iOS bundle IDs) or `google_play`. |
| `countries` | Store countries, e.g. `["us","gb","de"]`. Each App Store app is fetched once per country; a country inside an App Store URL (`/gb/`) wins for that app. Google Play lists reviews per language, so each Play app is fetched once (the `gl=` of a Play URL, else the first country) and `language` picks the market (see below). |
| `language` | Two-letter language (`en`, `de`, ...). On Google Play it also picks which reviews you get: Play lists reviews per language, so `ro` returns reviews written in Romanian. On the App Store it sets the interface language; reviews come per country. |
| `sort` | `newest` (default) or `mostHelpful`. |
| `maxReviewsPerApp` | Matching reviews per app and country, 1 to 5,000 (default 100). |
| `sinceDate` | `YYYY-MM-DD`; older reviews are skipped. |
| `ratings` | For example `["1","2"]`. |
| `onlyNew` + `monitorId` | Return only reviews not returned by an earlier run with the same monitor ID. The first run sets the baseline. |

Example:

```json
{
  "apps": ["com.spotify.music", "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580"],
  "countries": ["us"],
  "maxReviewsPerApp": 100,
  "ratings": ["1", "2"],
  "sinceDate": "2026-10-01"
}
```

### Output

One row per review. Real rows from board test run W9GrglUWNc1kah7ue on 2026-10-11 (build 0.1.12; text shortened here):

```json
{ "store": "google", "appId": "com.spotify.music", "bundleId": null, "appName": "Spotify: Music and Podcasts", "country": "us",
  "reviewId": "ccf4d10d-0555-40d4-900a-0a771fc478ee", "rating": 2, "title": null,
  "text": "Constant new breaks and bugs. New one where forward and back inputs randomly swap ...",
  "date": "2026-10-10T00:17:45Z", "appVersion": null, "thumbsUp": 0, "isEdited": null,
  "developerReply": "Hi. We're open to feedback, and we'd like to get some more info about this ...",
  "developerReplyDate": "2026-10-10T14:17:06Z",
  "reviewUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&reviewId=ccf4d10d-0555-40d4-900a-0a771fc478ee", "error": null }

{ "store": "apple", "appId": "324684580", "bundleId": "com.spotify.client", "appName": "Spotify: Music and Podcasts", "country": "us",
  "reviewId": "14647218299", "rating": 5, "title": "Amazing Options", "text": "Loving Spotify their selections are amazing ...",
  "date": "2026-10-09T20:35:46Z", "appVersion": "9.1.90", "thumbsUp": 0, "isEdited": false,
  "developerReply": null, "developerReplyDate": null,
  "reviewUrl": "https://itunes.apple.com/us/review?id=324684580&type=Purple%20Software", "error": null }
```

What each store provides (the Actor never invents what the store does not publish):

| Field | Google Play | App Store |
|---|---|---|
| rating, text, date, reviewId | yes | yes |
| bundleId | no (`null`; `appId` is the package name) | yes, from Apple's lookup |
| title | no (Google reviews have none: `null`) | yes |
| appVersion | when published on the review | from Apple RSS for reviews matched by review ID; otherwise `null` |
| developerReply and date | when the developer replied | when the developer replied |
| thumbsUp | yes | RSS helpful-vote sum for matched reviews; zero is retained, missing is `null` |
| isEdited | no | yes |
| reviewUrl | review-specific Google link | Apple-published RSS related link for matched reviews; this may open the app review page, not an individual-review permalink |

Apps that cannot be fetched produce one row with an `error` note. If any app fails or an input correction is needed, the whole diagnostic run is uncharged. Reviews are spooled to disk until all app outcomes are known, then written to the dataset; normal successful runs still honor the spending cap.

### RSS enrichment limits

For each App Store app/country the Actor fetches one public RSS feed of recent reviews and joins metadata by exact review ID. Catalog text, dates, sorting and developer replies remain authoritative. Reviews outside that recent RSS window, unmatched IDs, or unavailable feeds keep `null` metadata; no current app version is substituted for the version a reviewer used. RSS vote sums are not the number of voters. Google Play behavior is unchanged.

### Price

Pay per event: **$0.09 per 1,000 reviews** ($0.00009 each), the same price for Google Play and App Store reviews. Error rows, empty results and already-seen reviews in only-new mode are free. Platform usage is included.

### Good to know

- **Politeness:** the Actor spaces its requests per store and backs off when a store answers "too many requests". If a store keeps refusing, it stops using it for that run and tells you in the row's `error` field. It does not try to get around blocks.
- **Newest first** is the default and the only order used by `onlyNew` and `sinceDate` early-stop. `mostHelpful` is the store's own ranking.
- **The App Store keeps reviews per country; Google Play keeps them per language.** In the Lab's check on 2026-10-10, OPay's 100 newest English Google Play reviews were the same for the Nigerian, US, UK and Romanian stores, and Romanian or German shared none with English. So the Actor fetches each Google Play app once however many countries you list (the `gl=` of a Play URL, else the first country) and says so in the run's status message; pick the Play market with `language` (for example `ro` for reviews written in Romanian). Since build 0.1.9 a Play run with `us`, `gb` and `de` returns and charges each review once (board test run 2026-10-10: 20 reviews, 20 charges). On the App Store each country is its own list (none of the US store's 4 newest OPay reviews were among the 300 newest Nigerian ones).
- The stores may not list every review ever written. Numbers in `maxReviewsPerApp` are an upper bound.
- Google Play and App Store pages and endpoints can change without notice; if a store changes, rows will carry an `error` note rather than wrong data.
- An empty first Google Play page is retried once at the normal request rate. Zero reviews, unavailable apps and unrecognised IDs finish successfully with a status message explaining what to try. Other apps continue. Invalid `sinceDate` is ignored with a warning. Runs with these diagnostics are uncharged, including any reviews preserved from good apps; a truly empty `apps` input still fails. Per-app logs show raw page counts and emitted review counts.
- Review text is user-written content. Check the stores' terms and applicable law for how you use it.

### Pricing table & example

Pay per event. Prices per 1,000 events, by Apify plan (higher plans get a discount automatically):

| Event | Free | Starter (Bronze) | Scale (Silver) | Business (Gold) |
|---|---|---|---|---|
| Review returned (`review`) | $0.09 | $0.09 | $0.085 | $0.08 |

**Worked example:** 5,000 billable `review` events on the Free plan cost 5,000 × $0.00009 = **$0.45**; on Gold, 5,000 × $0.00008 = $0.40. Rows that the Price section lists as free cost nothing. Apify platform usage is included in these prices.

### Input example

```json
{
  "apps": [
    "com.spotify.music"
  ],
  "countries": [
    "us"
  ],
  "maxReviewsPerApp": 20
}
```

### FAQ

**Which stores and how many reviews?**\
Google Play and the Apple App Store, any country the store serves. Use the max-reviews input to cap volume and cost; the stores themselves limit how far back their public review pages go.

**What is not covered?**\
Reviewer profiles, photos and IDs are not collected. Reviews the stores do not show publicly cannot be returned.

**app-store-scraper returns no App Store reviews. Is this an alternative?**\
Yes, for reviews. In our check on 2026-10-12 (Python 3.13, one server), the PyPI package `app-store-scraper` 0.3.5 (last release 2020, repository archived) returned 0 reviews on 4 of 4 calls: it looks for an access token on the App Store web page that is no longer there, and Apple's review endpoint answers 401. The npm package of the same name (0.18.0) returned an empty array on 27 of 31 first-page calls, because Apple's review RSS feed often comes back empty. This Actor pages through Apple's public web review endpoint without a token and returns Google Play reviews in the same table. Put App Store IDs (`324684580`), URLs or bundle IDs in `apps`; `maxReviewsPerApp` and `sinceDate` replace `how_many` and `after`. It returns no reviewer names.

**Is there an official Google Play or App Store reviews API?**\
Only for apps you own. Google's Play Developer API returns your own app's reviews created or modified in the last week ([Google's docs](https://developers.google.com/android-publisher/reply-to-reviews), checked 2026-10-10), and Apple's App Store Connect API covers apps in your own developer account. For competitors' apps this Actor reads the public review pages instead; how far back it can go depends on what each store's public pages show.

**Can I get only new reviews?**\
Yes: only-new mode with a monitor ID returns reviews earlier runs did not. Combine it with an Apify schedule.

**Is this official?**\
No. Unofficial and not affiliated with, endorsed by or sponsored by any company or site named in the results or this README.

**Something looks wrong or you need a field added?**\
Open an issue from the Actor's Issues tab with the input and run link.

# Actor input Schema

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

App URLs, Google Play package IDs, numeric App Store IDs or iOS bundle IDs (com.spotify.client), one per line. Unprefixed dotted IDs try Google Play first, then the App Store if not found. Force a store with ios: / appstore: or android: / play: (case-insensitive). App Store URLs with /gb/ use that country for that app.

## `countries` (type: `array`):

Two-letter store countries, for example `us`, `gb`, `de`. Each store lists reviews per country, so every app is fetched once per country listed.

## `language` (type: `string`):

Two-letter interface language, for example `en`, `de`, `fr`. It sets the language of the store page and labels; reviews come back in the language their authors wrote them in.

## `sort` (type: `string`):

Newest first (default), or the store's own 'most helpful' order. 'Only new' mode always uses newest.

## `maxReviewsPerApp` (type: `integer`):

Reviews that match your filters, 1 to 5000.

## `sinceDate` (type: `string`):

Date as `YYYY-MM-DD`. Older reviews are skipped.

## `ratings` (type: `array`):

For example `1` and `2` for complaints. Leave empty for all ratings.

## `onlyNew` (type: `boolean`):

Return only reviews not returned by an earlier run with the same Monitor ID. The first run returns the latest reviews and sets the baseline. Schedule the Actor (hourly, daily) and send the dataset to Slack, email or a webhook.

## `monitorId` (type: `string`):

Names the saved state for 'only new'. Use a different ID for each watchlist.

## `dottedIdStore` (type: `string`):

For unprefixed dotted IDs: auto tries Google Play first, then Apple if missing; app_store treats them as iOS bundle IDs; google_play uses only Play. Explicit prefixes and store URLs override this setting.

## Actor input object example

```json
{
  "apps": [
    "com.spotify.music",
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580"
  ],
  "countries": [
    "us"
  ],
  "language": "en",
  "sort": "newest",
  "maxReviewsPerApp": 100,
  "onlyNew": false,
  "monitorId": "default",
  "dottedIdStore": "auto"
}
```

# 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": [
        "com.spotify.music",
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580"
    ],
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hiver/app-reviews-scraper").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": [
        "com.spotify.music",
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    ],
    "countries": ["us"],
}

# Run the Actor and wait for it to finish
run = client.actor("hiver/app-reviews-scraper").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": [
    "com.spotify.music",
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580"
  ],
  "countries": [
    "us"
  ]
}' |
apify call hiver/app-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hiver/app-reviews-scraper"
        }
    }
}
```

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/oATotl41WKWdv4Y5g/builds/qYpMxu5h4cM3RxcLa/openapi.json
