# App Store & Google Play Reviews Scraper (`rel8ble/app-reviews-scraper`) Actor

Use this to get app reviews from the Apple App Store and Google Play. Input: app URLs, App Store IDs or Play package names, country, sort, max reviews. One result = one review: rating, title, text, date, app version, reviewer, helpful votes, developer reply, app info. $0.25 per 1,000 reviews.

- **URL**: https://apify.com/rel8ble/app-reviews-scraper.md
- **Developed by:** [Giovanni Rich](https://apify.com/rel8ble) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.25 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 & Google Play Reviews Scraper (App Reviews API)

This **App Store reviews scraper** and **Google Play reviews scraper** pulls **iOS app reviews** and Android app reviews in one run: star rating, title, full text, date, app version, reviewer name, helpful votes and the **developer's reply**, with the app's metadata (average rating, rating count, version, genre, installs, icon) on every row. Use it as a pay-per-result **app reviews API**: paste app URLs or IDs from either store and get one clean, unified table back as JSON, CSV or Excel.

It reads the stores' own public web data over plain HTTP, with **no headless browser and no login**. That keeps it fast (20,000 reviews in under 30 seconds in testing) and cheap: **$0.25 per 1,000 reviews**.

### How to use

1. **Enter your apps**: paste App Store or Google Play URLs (or IDs like `324684580` / `com.spotify.music`), then pick a country, sort order and how many reviews per app.
2. **Click Start**: the run pages through each app's reviews and saves one row per review.
3. **Download the results** as JSON, CSV, Excel or HTML from the Output tab, or fetch them with the Apify API.

### What you get

- **The review**: star rating (1-5), title (App Store), full text, date, reviewer name, reviewer avatar (Google Play), app version the review was written on (Google Play), helpful votes, and whether it was edited (App Store).
- **The developer's reply**: reply text and reply date, on both stores, whenever the developer answered.
- **App metadata on every row**: app name, developer, bundle ID / package name, average rating, total ratings, total reviews (Google Play), current version, genre, price and currency, installs (Google Play), content rating, release and last-update date, icon and store URL.
- **Deep history**: pages through as many reviews as the store serves, not just the first 500. Set `maxReviewsPerApp` to 0 for everything.
- **Any country**: 150+ App Store storefronts and every Google Play country, plus the Google Play review language.
- **Filters that save you money**: only 1-2 star complaints, only reviews since a date (great for daily monitoring), and 4 sort orders. You only pay for reviews that pass your filters.

### Use cases

- **App reputation monitoring**: schedule a daily run with `reviewsSince` and push new 1-2 star reviews to Slack, email or your helpdesk.
- **Competitor research**: pull thousands of reviews for competing apps and see what users love and hate.
- **App store optimization (ASO)**: find the words and features users mention most in high and low ratings.
- **Product feedback and bug triage**: find complaints tied to a specific app version (Google Play).
- **Sentiment analysis, LLM and NLP datasets**: clean text with ratings and dates in JSON, CSV or Excel.
- **Customer support audits**: measure how often, and how fast, a developer replies to reviews.

### Input

| Field | Default | Description |
|---|---|---|
| `apps` | Spotify on both stores | App Store or Google Play apps, one per line. App Store: `https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580` or `324684580`. Google Play: `https://play.google.com/store/apps/details?id=com.spotify.music` or `com.spotify.music`. Mix both stores freely. |
| `country` | `us` | 2-letter store country (`us`, `gb`, `de`, `fr`, `jp`, `br`, `in` ...). Each country has its own reviews. Overrides any country in the URLs. |
| `language` | `en` | Google Play only: the review language (`en`, `de`, `es`, `ja` ...). The App Store returns the country's reviews in whatever language they were written. |
| `sort` | `newest` | `newest`, `mostHelpful`, `mostFavorable` or `mostCritical`. |
| `maxReviewsPerApp` | 100 | Stop each app after this many reviews. `0` = everything the store will page through. |
| `stars` | all | e.g. `[1, 2]` to keep only complaints. |
| `reviewsSince` | - | `YYYY-MM-DD`. With `sort = newest` the run stops as soon as it reaches older reviews. |
| `maxConcurrency` | 5 | Parallel requests (apps run in parallel, each app pages one request at a time). |
| `maxRequestRetries` | 6 | Retries per request, each on a fresh session / proxy IP. |
| `proxyConfiguration` | Apify Proxy | The default datacenter proxy works for both stores. |

#### Input example

```json
{
    "apps": [
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
        "com.spotify.music"
    ],
    "country": "us",
    "language": "en",
    "sort": "newest",
    "maxReviewsPerApp": 500,
    "stars": [1, 2],
    "reviewsSince": "2026-09-01"
}
```

### Output example

One row per review. Both stores use the same field names, so you can merge them straight away. Fields a store does not have are `null` (see the fill rates below). These are real rows from a test run, trimmed slightly:

**App Store** (with a developer reply):

```json
{
    "store": "appStore",
    "appId": "324684580",
    "appName": "Spotify: Music and Podcasts",
    "country": "us",
    "reviewId": "14581732970",
    "rating": 1,
    "title": "Ads",
    "text": "screw you",
    "reviewDate": "2026-09-22T22:36:00.000Z",
    "reviewerName": "My Review On Apps And Games",
    "version": null,
    "thumbsUp": 0,
    "voteCount": 0,
    "isEdited": true,
    "developerReply": "Hey! We hear you and we'll pass your feedback along to the Ads team. For an ad-free experience, we'd recommend checking out our Premium plans at spotify.com/premium. ...",
    "developerReplyDate": "2026-09-16T13:55:16.000Z",
    "reviewUrl": "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "appDeveloper": "Spotify",
    "appBundleId": "com.spotify.client",
    "appRating": 4.77,
    "appRatingCount": 42231119,
    "appVersion": "9.1.84",
    "appGenre": "Music",
    "appPrice": 0,
    "appCurrency": "USD",
    "appContentRating": "12+",
    "appUpdated": "2026-09-18T06:25:38.000Z",
    "appIcon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/97/1a/25/971a25f3-2f18-8d3f-892a-ab5eea139e78/AppIcon-0-0-1x_U007epad-0-1-0-0-sRGB-85-220.png/512x512bb.jpg",
    "appUrl": "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "sort": "newest",
    "position": 113,
    "scrapedAt": "2026-09-24T05:38:30.377Z"
}
```

**Google Play** (with the app version and a developer reply):

```json
{
    "store": "googlePlay",
    "appId": "com.spotify.music",
    "appName": "Spotify: Music and Podcasts",
    "country": "us",
    "language": "en",
    "reviewId": "1e96015e-4577-4274-b4de-f71e0a4eae8e",
    "rating": 5,
    "title": null,
    "text": "The app has has gotten better with music suggestions, but recently podcast video episodes have trouble playing.",
    "reviewDate": "2026-09-23T05:14:13.000Z",
    "reviewerName": "Hennessy",
    "reviewerImage": "https://play-lh.googleusercontent.com/a-/ALV-UjV2jiGX3V1M63itQyJfTVNvah6dJDx4WRMU-tm_2TZVNaVnUc0",
    "version": "9.1.82.2161",
    "thumbsUp": 4,
    "developerReply": "Hi there! Have you tried a quick reinstall? If that doesn't help, we'd recommend reaching out to our Customer Service at http://support.spotify.com. ...",
    "developerReplyDate": "2024-01-03T12:49:34.000Z",
    "reviewUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&hl=en&gl=us&reviewId=1e96015e-4577-4274-b4de-f71e0a4eae8e",
    "appDeveloper": "Spotify AB",
    "appRating": 4.35,
    "appRatingCount": 36360224,
    "appReviewCount": 1853519,
    "appVersion": "Varies with device",
    "appGenre": "Music & Audio",
    "appInstalls": "1,000,000,000+",
    "appContentRating": "Teen",
    "appReleased": "May 27, 2014",
    "appUpdated": "2026-09-18T07:51:07.000Z",
    "appUrl": "https://play.google.com/store/apps/details?id=com.spotify.music&hl=en&gl=us",
    "sort": "newest",
    "position": 16,
    "scrapedAt": "2026-09-24T05:38:21.182Z"
}
```

The dataset has two views: **Overview** (the review table) and **App metadata**. A `RUN_SUMMARY` record in the key-value store lists, per app: reviews saved, reviews filtered out, requests used and why it stopped (`maxReviewsPerApp reached`, `no more reviews from the store`, `app not found`, ...).

#### Field fill rates (real test run: 20,000 reviews, Spotify and Uber on both stores, 5,000 each)

| Field | App Store | Google Play |
|---|---|---|
| rating, text, reviewDate, reviewerName, reviewId | 100% | 100% |
| title | 100% | n/a (Play reviews have no title) |
| version (per review) | n/a (not public on the App Store) | 84% |
| reviewerImage | n/a | 100% |
| thumbsUp (helpful votes) | 100% | 100% |
| developerReply / developerReplyDate | 7% (only reviews the developer answered) | 5% |
| app metadata (name, developer, rating, rating count, version, genre, icon, URL, updated) | 100% | 100% |
| appInstalls, appReviewCount | n/a | 100% |

0 failed requests and 0 duplicate reviews across all local and cloud test runs.

### Pricing

**Pay per result: $0.25 per 1,000 reviews.** You pay only for reviews saved to the dataset. Reviews dropped by your `stars` or `reviewsSince` filters are free, and so are apps that turn out not to exist. There is no start fee.

- 1,000 reviews = $0.25
- 10,000 reviews = $2.50
- 100,000 reviews = $25

The Apify free plan includes $5 of monthly credit, enough to try the actor on real apps.

### Integrations

- **Apify API**: start runs and read results over REST, or with the official JavaScript and Python clients.
- **Make, Zapier and n8n**: trigger a run and send new reviews to any app in your workflow.
- **Google Sheets**: export the dataset straight to a spreadsheet.
- **Webhooks**: get notified when a run finishes and pull the new reviews automatically.
- **Schedules**: run daily or weekly with `reviewsSince` to track only new reviews.
- **MCP for AI agents**: through the Apify MCP server (https://mcp.apify.com), Claude, ChatGPT, Cursor and other agents can call this actor and read app reviews directly.

### Limits (read before large runs)

- **App Store reviews have no app version.** Apple's public review data doesn't include it, so `version` is `null` for App Store rows. Google Play rows have it (about 85% of reviews).
- **Google Play shows reviews in one language per run** (`language`). To collect German and English Play reviews, run twice. The App Store has no language filter.
- **Star filters on Google Play go one star at a time.** With `stars: [1, 2]` you get the newest 1-star reviews first, then 2-star.
- **Reviews are per country.** The same app has different reviews in `us` and `gb`. Run each country separately (or schedule one task per country).
- Google Play's "most critical" order doesn't exist, so the actor walks 1-star, then 2-star and so on, newest first within each star.
- Reviewer names and avatars are personal data in many jurisdictions (GDPR, CCPA). Make sure you have a lawful basis before storing them.

### FAQ

**Is it legal to scrape App Store and Google Play reviews?** The actor only collects reviews that both stores publicly show to any visitor, without logging in. Scraping public data is generally allowed, but you are responsible for how you use it: follow the stores' terms of service and privacy laws such as GDPR and CCPA, especially for reviewer names and avatars. This is not legal advice; ask a lawyer if you are unsure.

**How does it avoid blocks?** It sends plain HTTP requests with realistic Chrome desktop headers through Apify Proxy, rotates sessions from a session pool (a session that gets HTTP 403/429/503 or an unexpected page is retired and the request moves to a fresh IP), and retries each request up to 6 times with jittered exponential backoff. If the log still shows repeated blocks, switch the proxy to the RESIDENTIAL group.

**What are the limits?** Up to as many reviews as each store will page through (`maxReviewsPerApp: 0`), paged 200 per request. One country and, on Google Play, one language per run. App Store rows have no per-review app version. Concurrency is capped at 20 parallel requests. See the Limits section above for details.

**Do I need an Apple or Google account?** No. The actor reads what both stores show to any visitor, without logging in.

**How many reviews can I get?** As many as the store will page through. In testing the App Store went more than 500,000 reviews deep on Spotify, and Google Play keeps returning pages until the history ends.

**How do I find an app's ID?** Open the app in the store and copy the URL. App Store IDs are the number after `id` (`id324684580`). Google Play IDs are the `id=` parameter (`com.spotify.music`).

**Can I monitor new reviews every day?** Yes. Use `sort: newest` with `reviewsSince` set to yesterday's date (or schedule the actor and filter by `reviewDate`). The run stops as soon as it reaches older reviews, so a daily run costs only the new reviews.

**Why is an app "not found"?** The ID is wrong, or the app isn't available in the chosen `country`. It's listed in `RUN_SUMMARY` and doesn't stop the other apps.

### How it works (for developers)

- **App Store**: app metadata from Apple's public iTunes lookup API; reviews from the public review-list endpoint the App Store uses, paged 200 at a time for the requested storefront.
- **Google Play**: app metadata from the data embedded in the public app page; reviews from the public reviews call the Play website makes when you scroll, paged 200 at a time with continuation tokens.
- Crawlee `BasicCrawler` with got-scraping (real browser header sets), a session pool with retire-on-block, jittered exponential backoff, up to 6 retries per request, and deduplication by review ID. State persists across migrations. Charging uses `Actor.pushData(item, 'result')` and stops cleanly at your spending limit.

# Actor input Schema

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

Provide at least one. List of apps from either store. App Store: URL (https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580) or numeric ID ("324684580"). Google Play: URL or package name ("com.spotify.music"). Default: Spotify on both stores (demo only).

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

Optional. 2-letter lowercase store country code, e.g. "us", "gb", "de", "jp", "br". Each country has its own reviews; overrides the country in URLs. Default "us".

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

Optional, Google Play only. Language code of reviews to return, e.g. "en", "de", "es", "ja". The App Store returns reviews in whatever language they were written. Default "en".

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

Optional. Review order: "newest" (default), "mostHelpful", "mostFavorable" or "mostCritical". On Google Play "mostCritical" returns 1-star first, then 2-star, and so on.

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

Optional. Maximum reviews per app, integer >= 0. Default 100; 0 = everything the store pages through. You pay per review, so use 20-50 for a quick answer.

## `stars` (type: `array`):

Optional. Keep only these star ratings, as a JSON array of integers 1-5, e.g. \[1, 2] for complaints. \[] or omit = all ratings. Only matching reviews are charged.

## `reviewsSince` (type: `string`):

Optional. Only reviews written on or after this date, format YYYY-MM-DD, e.g. "2026-09-01". With sort "newest" the run stops at the first older review.

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

Optional, advanced. Number of parallel requests, integer 1-20. Default 5. Leave unset unless the run is very large.

## `maxRequestRetries` (type: `integer`):

Optional, advanced. Retries per failed or blocked request, integer 0-20; each retry uses a new proxy session. Default 6. Leave unset.

## `proxyConfiguration` (type: `object`):

Optional, advanced. Apify Proxy settings object, e.g. {"useApifyProxy": true}. Default: Apify Proxy on (datacenter). Leave unset; switch to {"useApifyProxy": true, "apifyProxyGroups": \["RESIDENTIAL"]} only if the run log shows repeated blocks.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "https://play.google.com/store/apps/details?id=com.spotify.music"
  ],
  "country": "us",
  "language": "en",
  "sort": "newest",
  "maxReviewsPerApp": 20,
  "stars": [],
  "maxConcurrency": 5,
  "maxRequestRetries": 6,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `reviews` (type: `string`):

All reviews with rating, text, date, version, developer reply and app metadata.

## `summary` (type: `string`):

Per-app review counts, requests and stop reasons.

# 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": [
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
        "https://play.google.com/store/apps/details?id=com.spotify.music"
    ],
    "maxReviewsPerApp": 20,
    "stars": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rel8ble/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": [
        "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
        "https://play.google.com/store/apps/details?id=com.spotify.music",
    ],
    "maxReviewsPerApp": 20,
    "stars": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("rel8ble/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": [
    "https://apps.apple.com/us/app/spotify-music-and-podcasts/id324684580",
    "https://play.google.com/store/apps/details?id=com.spotify.music"
  ],
  "maxReviewsPerApp": 20,
  "stars": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call rel8ble/app-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rel8ble/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/1nfrM12ykh6IFCyUI/builds/RmnbL5xHtuTYaUlc6/openapi.json
