# App Store & Google Play Reviews & ASO Scraper (`taroyamada/app-store-review-aso-scraper`) Actor

Scrape Apple App Store and Google Play customer reviews and app/ASO intelligence in bulk by app ID, store URL, bundle/package ID, or keyword search. Multi-country, full pagination, rating histograms, version breakdowns, sentiment and keyword signals. Public store endpoints only. Pay per result.

- **URL**: https://apify.com/taroyamada/app-store-review-aso-scraper.md
- **Developed by:** [naoki anzai](https://apify.com/taroyamada) (community)
- **Categories:** E-commerce, Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 app reviews

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## App Store & Google Play Reviews Scraper

ASO teams and product analysts use this Actor to acquire recent public app reviews and storefront profiles at low cost.
Input Apple IDs, App Store URLs, bundle IDs, Google Play packages, or App Store search terms.
Get normalized review rows and per-storefront rating, version, sentiment, and keyword snapshots.

> Great for ASO teams, app marketers, product managers, and competitive/market research.

### Store Quickstart

```json
{
  "appIds": ["1232780281"],
  "countries": ["us"],
  "maxReviewPages": 1,
  "includeReviews": true,
  "includeAppProfile": true,
  "runMode": "extract",
  "maxChargeUsd": 0.25,
  "dryRun": false
}
```

### Run the next report

- Compare release-window review signals across apps with [App Release & Category Review Benchmark Report](https://apify.com/taroyamada/app-release-category-review-benchmark-report).

### What you get

For each app and storefront you can get:

- **Every recent customer review** (up to ~500 per storefront): rating, title, text, author, app version, helpful votes, date.
- **An app profile / ASO snapshot**: name, developer, price, genres, content rating, current version + release dates, official average rating and total rating count, plus computed signals from the review sample:
  - **Rating histogram** (1–5 stars)
  - **Sentiment breakdown** (positive / neutral / negative share)
  - **Versions seen** in recent reviews
  - **Top keywords** customers actually use

### Inputs

Provide any combination of sources:

| Field | Description |
| --- | --- |
| `appIds` | Apple numeric track IDs, e.g. `1232780281` |
| `appUrls` | App Store URLs, e.g. `https://apps.apple.com/us/app/notion/id1232780281` |
| `bundleIds` | Apple bundle identifiers, e.g. `com.notion.notion` |
| `searchTerms` | Keywords to discover App Store apps (ASO / competitor discovery) |
| `playAppIds` | Google Play package names, e.g. `com.spotify.music` |
| `playUrls` | Google Play URLs, e.g. `https://play.google.com/store/apps/details?id=com.spotify.music` |
| `countries` | Storefront codes, e.g. `["us","gb","jp"]` |
| `language` | Language code for Google Play requests, e.g. `en` |
| `maxReviewPages` | Pages of reviews per app per storefront (Apple ~50/page, Google Play ~100/page) |
| `reviewSort` | `mostRecent` or `mostHelpful` |
| `minRating` | Keep only reviews ≥ this rating (0 = all) |
| `includeReviews` / `includeAppProfile` | Toggle each output type |
| `emitUnchanged` / `baselineStableKeys` | For recurring monitoring, set `emitUnchanged: false` and pass stable keys from the previous run to emit and charge only new rows |

#### Example: same app on both stores

```json
{
  "appIds": ["1232780281"],
  "playAppIds": ["notion.id"],
  "countries": ["us"],
  "maxReviewPages": 5
}
```

#### Example: reviews for specific apps, two countries

```json
{
  "appIds": ["1232780281"],
  "countries": ["us", "gb"],
  "maxReviewPages": 10,
  "reviewSort": "mostRecent"
}
```

#### Example: discover competitors by keyword and snapshot them

```json
{
  "searchTerms": ["habit tracker", "budget app"],
  "maxAppsPerTerm": 10,
  "countries": ["us"],
  "includeReviews": true,
  "includeAppProfile": true
}
```

#### Example: recurring no-change-safe run

```json
{
  "appIds": ["1232780281"],
  "countries": ["us"],
  "maxReviewPages": 1,
  "includeReviews": true,
  "includeAppProfile": true,
  "runMode": "monitor",
  "monitorKey": "notion-us-reviews",
  "initialRunMode": "baseline_only",
  "emitUnchanged": false,
  "dryRun": false
}
```

### Output

Each dataset item is either an `app_review` or an `app_profile` row.

**Review row (charged as `app-review`):**

```json
{
  "rowType": "app_review",
  "appId": "1438388363",
  "appName": "Habit Tracker",
  "country": "us",
  "reviewId": "14143606963",
  "author": "Chelsea123",
  "rating": 5,
  "title": "Love it",
  "content": "Keeps me consistent every day...",
  "appVersion": "2.14.11",
  "voteSum": 3,
  "voteCount": 4,
  "updated": "2026-06-03T08:58:57-07:00",
  "sourceUrl": "https://apps.apple.com/us/app/id1438388363"
}
```

**App profile row (charged as `app-profile`):**

```json
{
  "rowType": "app_profile",
  "appId": "1438388363",
  "appName": "Habit Tracker",
  "country": "us",
  "primaryGenre": "Productivity",
  "version": "2.14.12",
  "averageUserRating": 4.79,
  "userRatingCount": 141798,
  "ratingHistogram": { "1": 13, "2": 1, "3": 8, "4": 15, "5": 63 },
  "sentiment": { "positive": 78, "neutral": 8, "negative": 14, "positiveShare": 0.78 },
  "versionsSeen": [{ "version": "2.14.12", "count": 40 }],
  "topKeywords": [{ "word": "habits", "count": 24 }, { "word": "love", "count": 23 }]
}
```

### Pricing — pay for raw rows or ASO snapshots

| Event | Price | When |
| --- | --- | --- |
| `app-review` | **$0.0005** | per public review row delivered |
| `app-profile` | **$0.02** | per app and storefront ASO snapshot |

`app-review` is the commodity row. The differentiated output is `app-profile`: official rating + total ratings, **rating histogram**, **sentiment breakdown**, **top keywords**, and **versions seen**, in one row. Turn it off (`includeAppProfile: false`) if you only want raw reviews; keep it on for ASO, product, and competitive research workflows.

**How billing works (fair by design):**

- `app-review` is charged **per review delivered**. An app that returns no reviews produces **no review charges**; it is reported as a run warning only.
- `app-profile` is charged **once per app per storefront**, when the app's official profile is successfully retrieved. The profile carries value even with no recent reviews (official rating, total ratings, version, genre), so it is charged whenever you enable it and the app is found. If the app's metadata can't be retrieved, **no profile is emitted or charged**. Set `includeAppProfile: false` to never incur this event.
- Recurring monitoring can avoid repeat charges: use `runMode: "monitor"`, `initialRunMode: "baseline_only"`, and `emitUnchanged: false`. The first run stores a private baseline; unchanged later runs produce **0 dataset rows** and **0 charges**.

### Use cases

- **ASO reports** — track keywords, ratings, and review themes for your app and competitors across storefronts.
- **Product** — surface bugs, churn drivers, and feature requests from recent reviews by version.
- **Market & competitive research** — snapshot a whole category by keyword and compare rating histograms and sentiment.
- **Review monitoring / alerting** — schedule recurring runs and feed new low-star reviews into your tools.

### FAQ

- **Does it need an account or API key?** No. It uses Apple's public iTunes Search/Lookup/Customer-Reviews-RSS endpoints and Google Play's public store endpoints.
- **How many reviews per app?** Apple exposes the most recent ~500 per storefront (10 pages × 50). Google Play is paginated via `maxReviewPages` (~100/page). Scrape more countries to widen coverage.
- **Which countries?** Any storefront via its two-letter code (`us`, `gb`, `jp`, `de`, ...).
- **Both stores?** Yes — provide Apple sources (`appIds`/`appUrls`/`bundleIds`/`searchTerms`) and/or Google Play sources (`playAppIds`/`playUrls`). Each row carries a `platform` field.

### Compliance

Only public, non-authenticated Apple endpoints are used. Output is for research and analytics. **Do not** use review data to identify, target, or contact individual reviewers, or to generate or post fake reviews. See `docs/source-compliance.md`.

# Actor input Schema

## `appIds` (type: `array`):

Numeric Apple App Store track IDs (e.g. 1232780281).

## `appUrls` (type: `array`):

Full App Store URLs (e.g. https://apps.apple.com/us/app/notion/id1232780281).

## `bundleIds` (type: `array`):

App bundle identifiers (e.g. com.notion.notion). Resolved to apps per storefront.

## `searchTerms` (type: `array`):

Keywords to discover Apple App Store apps. Useful for ASO and competitive monitoring.

## `playAppIds` (type: `array`):

Google Play app package names, e.g. com.spotify.music.

## `playUrls` (type: `array`):

Google Play store URLs, e.g. https://play.google.com/store/apps/details?id=com.spotify.music.

## `maxAppsPerTerm` (type: `integer`):

How many top apps to take from each search term.

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

Two-letter App Store country codes to scrape (e.g. us, gb, jp, de). Reviews and ratings are per storefront.

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

Two-letter language code used for Google Play requests, e.g. en, ja, de.

## `maxReviewPages` (type: `integer`):

Each page returns up to 50 recent reviews (Apple exposes up to 10 pages = ~500 reviews per storefront).

## `reviewSort` (type: `string`):

Order reviews by most recent or most helpful.

## `includeReviews` (type: `boolean`):

Emit one row per customer review.

## `includeAppProfile` (type: `boolean`):

Emit one metadata + ASO + aggregate-rating row per app per storefront.

## `minRating` (type: `integer`):

Only keep reviews with at least this star rating (0 = keep all).

## `proxy` (type: `object`):

Optional. Apple's public endpoints generally work without a proxy; enable one only for very large runs.

## `dryRun` (type: `boolean`):

When true, emit illustrative sample rows without live calls or billing. Set to false to scrape real data.

## `runMode` (type: `string`):

Extract always returns current rows. Audit returns a current snapshot. Monitor stores a private baseline and emits only new or changed rows.

## `monitorKey` (type: `string`):

Optional stable name for a recurring monitor. State is isolated by the calling Apify user and this key.

## `initialRunMode` (type: `string`):

Baseline only stores the first snapshot for free. Emit backfill returns the initial rows.

## `maxChargeUsd` (type: `number`):

Optional Actor-side budget. Zero uses the Apify run spending limit only.

## `emitUnchanged` (type: `boolean`):

Keep enabled for extract/audit runs. Monitor mode defaults to changed rows only and stores its own baseline.

## `baselineStableKeys` (type: `array`):

Optional stableKey values from a previous run. When emitUnchanged is false, matching rows are skipped and not charged.

## Actor input object example

```json
{
  "appIds": [
    "1232780281"
  ],
  "appUrls": [],
  "bundleIds": [],
  "searchTerms": [],
  "playAppIds": [],
  "playUrls": [],
  "maxAppsPerTerm": 5,
  "countries": [
    "us"
  ],
  "language": "en",
  "maxReviewPages": 5,
  "reviewSort": "mostRecent",
  "includeReviews": true,
  "includeAppProfile": true,
  "minRating": 0,
  "proxy": {
    "useApifyProxy": false
  },
  "dryRun": true,
  "runMode": "extract",
  "monitorKey": "",
  "initialRunMode": "baseline_only",
  "maxChargeUsd": 0,
  "emitUnchanged": true,
  "baselineStableKeys": []
}
```

# 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 = {
    "appIds": [
        "1232780281"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("taroyamada/app-store-review-aso-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 = { "appIds": ["1232780281"] }

# Run the Actor and wait for it to finish
run = client.actor("taroyamada/app-store-review-aso-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "appIds": [
    "1232780281"
  ]
}' |
apify call taroyamada/app-store-review-aso-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=taroyamada/app-store-review-aso-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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