# App Store & Google Play Reviews Scraper: iOS & Android API (`cleanyield/app-store-google-play-reviews`) Actor

Scrape App Store and Google Play reviews in one run and one fixed JSON format: every review you ask for (no hidden cap), the right app and country in every row, rating, text, date, version, developer reply. Monitor mode outputs only new reviews and alerts you on negative ones.

- **URL**: https://apify.com/cleanyield/app-store-google-play-reviews.md
- **Developed by:** [Clean Yield](https://apify.com/cleanyield) (community)
- **Categories:** Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.08 / 1,000 reviews

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 & Google Play Reviews Scraper: iOS & Android API

**You only pay for valid, complete results** — every review is schema-validated before it is saved; incomplete or failed results are retried and never charged.

> **Switching from another review scraper? Paste your current input as is.** Common field names (`appIds`, `startUrls`, `appIdOrUrl`, `country`, `language`, `maxItems`, `sort`, `rating`, `deviceType`…) are translated automatically, and `outputFormat: "leader-compatible"` keeps the output field names your integration already reads. Full list in [Migrating from other scrapers](#migrating-from-other-scrapers).
>
> | Typical field | Here |
> |---|---|
> | app IDs / URLs / package names | `apps` (both stores in one list) |
> | country / language | `countries` / `languages` (several per run) |
> | max items | `maxTotalReviews` / `maxReviewsPerApp` (exact, no hidden cap) |
> | sort | `sortBy` (`newest`, `mostHelpful`, `rating`) |
> | score / user name / reply text | `rating` / `authorName` / `developerReply.text` |

An **iOS & Android app reviews API**: scrape App Store and Google Play reviews and ratings into **one clean JSON format** — rating, title, text, date, app version, developer reply, device and a working link — for any app, country, language and device (phone, tablet, Chromebook, watch, TV, car), mixed in a single run. Built for product, support and ASO teams who read reviews every week: **monitor mode returns only new reviews and alerts you on 1–2★ ones in Slack or by webhook.** Also works as an iOS app reviews scraper, a Google Play reviews API, an app review monitoring tool and a competitor app review tracker.

Want to try first? Use the free Lite version: https://apify.com/cleanyield/app-store-google-play-reviews-lite

### Why use this App Store & Google Play reviews API?

- 📥 **Every review you ask for**: no hidden 10-review cap — ask for 50 or 50,000 and get them, following both stores' pages to the end (measured: 50 of 50 per app on the Free plan).
- 🎯 **The right app and country in every row**: `appName`, `store`, `country` and `language` come from what you asked for and are checked on 59 real inputs, so rows never mix apps or storefronts.
- 🧷 **One fixed schema for both stores**: iOS and Android reviews in one run and one format that does not change between runs (`schemaVersion`), with links that actually open.
- ✅ **Complete, accurate data**: 100% of returned reviews have every required field filled; app, store, country, language and device verified on a fixed test set of 59 real inputs (popular and small apps, 13 App Store countries, 7 Google Play languages, 3 Google Play devices, both stores, keyword/version/date filters, nonexistent apps). See [Measured quality](#measured-quality).
- 📱 **Every review Google Play has**: tablet, Chromebook, watch, TV and car reviews are separate lists on Google Play; set `devices` to get them too. Google's per-review feature answers (ads, battery, multiplayer…) come in `criteriaRatings`.
- 🔎 **Filters that save money**: stars, date range (`reviewsSince` / `reviewsUntil`), keywords (`crash`, `refund`…, accent-insensitive) and app versions. You pay only for reviews that match.
- ⭐ **Pro mode (one checkbox)**: topics per review, a 0–100 urgency score to sort your support queue, a one-sentence AI conclusion, a suggested reply for 1–3★ reviews, developer data, a 0–100 reputation score and the rating trend.
- 🔄 **Drop-in replacement**: accepts the input of other popular review scrapers (`appIds`, `startUrls`, `country`, `maxItems`, `sort`…) and can output their field names. See [Migrating from other scrapers](#migrating-from-other-scrapers).
- 🧹 **Ready for sentiment analysis and spreadsheets**: ISO dates (UTC), ISO country codes, stars 1–5, `isNegative` flag, stable `reviewId`, deduplicated across countries and languages.
- 🔁 **Built for monitoring**: schedule it and get only the reviews that are new (or edited by their author) since the last run, with Slack/webhook alerts that put negative reviews first.
- 🚨 **Actionable alerts (add-on)**: negative spike vs last week, a new app version rated lower than the previous one, unanswered negative reviews, and how your app ranks against competitors.
- ⚡ **Fast**: the default input (200 Instagram reviews from both stores) finishes in about 2 s of run time; 5,000 reviews in about 30 s, so sync mode works from Clay, n8n and Make.
- 📊 **Dashboard in every run**: a shareable page (DASHBOARD in the key-value store) with averages, negative share per app and, in Pro, what negative reviews talk about.
- 🔒 **Privacy by default**: reviewer names are omitted unless you ask for them.
- 🤖 **AI-agent ready**: pay-per-result only (no usage fees), works through MCP without an Apify account.

#### This Actor vs typical alternatives

Measured head-to-head on 2026-11-05: this Actor and the most-used single-store review scrapers (App Store and Google Play), run on Apify with the same 10 apps from our fixed test set (6 App Store countries, 4 Google Play languages), asking for 50 reviews each, compared review by review. Our full test set (59 real inputs) is in "Measured quality".

| Same 10 apps, same request | Typical alternatives (measured, Free plan) | This Actor |
|---|---|---|
| Reviews returned when asking for 50 | 10 per run on the Free plan | **50** |
| Useful fields filled (same 90 reviews) | 81% (no app name or store) | **99%** |
| Rating, title, text, version and date match | 100% | 100% (re-measured 6 Nov 2026) |
| Successful runs | 9 / 10 (monthly run limit on Free plan) | **10 / 10** |
| Both stores in one run, one schema | ✗ (one Actor per store) | **✓** |
| Cost per 1,000 reviews | USD 0.10 | USD 0.10 (monitor mode: only new reviews) |
| Time per run | **~5 s for 10 reviews** | ~5 s for 50 reviews |
| Monitor mode (only new) + Slack/webhook alerts | ✗ | **✓** |
| Tablet / Chromebook / watch / TV / car reviews (Google Play) | ✗ or mobile/tablet/Chromebook only | **✓ all 6 devices** |
| Keyword, app version and date-range filters, both stores | partial (one store) | **✓** |
| Topics, urgency score, AI conclusion and suggested reply | ✗ | **✓ Pro mode** |

### What data can you get from the App Store and Google Play?

| Field | Example |
|---|---|
| `reviewId` | `googleplay:com.calm.android:5ae5d822-…` (stable across runs) |
| `store` | `appstore` or `googleplay` |
| `appId` / `appName` | `363590051` / `Netflix` |
| `country` | `US` (App Store storefront; `null` on Google Play, which groups by language) |
| `language` | `en`, `pt-BR` (Google Play; `null` on App Store, Apple does not report it) |
| `rating` | `1`–`5` |
| `title` | `Trouble streaming while traveling` (App Store only) |
| `text` | Review text |
| `date` | `2026-11-03T06:51:42.000Z` |
| `appVersion` | `26.4.0` |
| `developerReply` | `{ "text": "Thank you for…", "date": "2026-10-19T20:22:47.588Z" }` (Google Play) |
| `thumbsUp` | Helpful votes |
| `isNegative` | `true` for 1–2★ |
| `url` | Opens the review (Google Play) or the app's reviews page (App Store) |
| `device` | `phone`, `tablet`, `chromebook`, `watch`, `tv`, `car` (Google Play; `null` on App Store) |
| `criteriaRatings` | `[{ "criteria": "vaf_app_quality_ads_presence", "label": "ads presence", "rating": 2 }]` — the reviewer's answers to Google Play's extra questions (some apps and games) |
| `authorName` / `authorImageUrl` | `null` by default (set `omitAuthorNames: false` to get the public name and, on Google Play, the profile picture) |
| `topics` | Pro: `["crashes_bugs", "payments_billing"]` |
| `priorityScore` | Pro: `0`–`100` urgency |
| `insight` | Pro: one-sentence AI conclusion about the review |
| `suggestedReply` | Pro: AI reply draft for 1–3★ reviews, in the review's language |
| `appDetails` | Pro: developer, developer website, category, store rating, rating count, reputation score 0–100 and 7/30-day trend |
| `changeType` | `new` / `changed` in monitor mode |
| `schemaVersion` | `2` (fields are only ever added, never renamed) |

#### Example output

Real items from a run on 4 Nov 2026:

```json
[
  {
    "reviewId": "googleplay:com.calm.android:5ae5d822-eb17-45aa-aadc-8b4671a4759a",
    "store": "googleplay",
    "appId": "com.calm.android",
    "appName": "Calm - Sleep, Meditate, Relax",
    "country": null,
    "language": "en",
    "rating": 5,
    "title": null,
    "text": "This app works well for sleep and square breathing.",
    "date": "2026-11-02T02:02:27.884Z",
    "appVersion": null,
    "authorName": null,
    "developerReply": {
      "text": "Thank you for your kind feedback! We're so glad Calm is helping you sleep better and practice square breathing. Wishing you many more restful nights and peaceful moments!",
      "date": "2026-10-19T20:22:47.588Z"
    },
    "device": "phone",
    "criteriaRatings": null,
    "authorImageUrl": null,
    "thumbsUp": 1,
    "isNegative": false,
    "url": "https://play.google.com/store/apps/details?id=com.calm.android&reviewId=5ae5d822-eb17-45aa-aadc-8b4671a4759a",
    "topics": null,
    "priorityScore": null,
    "insight": null,
    "suggestedReply": null,
    "appDetails": null,
    "changeType": null,
    "scrapedAt": "2026-11-04T21:27:52.497Z",
    "schemaVersion": 2
  },
  {
    "reviewId": "appstore:363590051:14744797101",
    "store": "appstore",
    "appId": "363590051",
    "appName": "Netflix",
    "country": "US",
    "language": null,
    "rating": 1,
    "title": "Trouble streaming while traveling",
    "text": "Doesn’t allow me to watch while I’m traveling",
    "date": "2026-11-03T06:51:42.000Z",
    "appVersion": "26.4.0",
    "authorName": null,
    "developerReply": null,
    "device": null,
    "criteriaRatings": null,
    "authorImageUrl": null,
    "thumbsUp": 0,
    "isNegative": true,
    "url": "https://apps.apple.com/us/app/netflix/id363590051?see-all=reviews",
    "topics": null,
    "priorityScore": null,
    "insight": null,
    "suggestedReply": null,
    "appDetails": null,
    "changeType": null,
    "scrapedAt": "2026-11-04T21:27:47.299Z",
    "schemaVersion": 2
  }
]
```

Pro mode fills the Pro fields. Real values from a Pro run on 4 Nov 2026 (Calm, Google Play, tablet review about billing):

```json
{
  "device": "tablet",
  "topics": ["payments_billing"],
  "priorityScore": 35,
  "appDetails": {
    "developer": "Calm.com, Inc.",
    "developerWebsite": "http://www.calm.com",
    "category": "Health and fitness",
    "storeRating": 4.48,
    "ratingCount": 617051,
    "currentVersion": null,
    "appUrl": "https://play.google.com/store/apps/details?id=com.calm.android",
    "reputationScore": 62,
    "trend": { "last7dAverage": 3.59, "prev7dAverage": 3.67, "last30dAverage": 3.57, "last7dCount": 27, "last30dCount": 35 }
  }
}
```

Every run also saves **REVIEW_STATS** in the key-value store: per app, the store rating and rating count, average of the reviews returned, 1–5★ distribution, share of negative reviews, new vs edited, and a per-country/language breakdown.

### Pro mode: topics, urgency, AI conclusion and suggested replies

Turn on **Pro mode** (one checkbox). The basic run stays as cheap and fast as before; Pro adds, per review:

- **`topics`** — what the review is about, in 6 languages (EN, ES, PT, DE, FR, IT): `crashes_bugs`, `login_account`, `payments_billing`, `ads`, `performance`, `ux_design`, `update_regression`, `customer_support`, `privacy_security`, `notifications`, `missing_feature`, `praise`.
- **`priorityScore` (0–100)** — sort by it to answer the most urgent reviews first. Stars (1★ = 50, 2★ = 40, 3★ = 20, 4★ = 5) + severe topic (crash, billing, login, privacy: +15) + recency (last 3 days +15, last 14 days +8) + helpful votes (up to +10) + negative without a developer reply (+10).
- **`insight`** — one sentence written by AI (Claude Haiku) from this review's data only, e.g. what the user is unhappy about and what it points to.
- **`suggestedReply`** — for 1–3★ reviews, a polite reply draft in the review's language that names the user's problem (it never promises refunds or dates).
- **`appDetails`** — developer, developer website, category, store rating and rating count from the store listing, a **reputation score 0–100** (60% average stars of recent reviews, 25% share of non-negative reviews, 15% share of negative Google Play reviews with a developer reply; App Store: 70/30) and the **rating trend** (last 7 days vs previous 7, last 30 days). In monitor mode the trend uses the history the Actor keeps, so it gets richer with every scheduled run.

Pro also turns on the actionable alerts and competitor comparison below. **You pay the Pro fee only for reviews that received the AI conclusion**; if AI is not available in a run, the rest of Pro is delivered for free.

### Use cases

- **Support triage**: every morning, the new 1–2★ reviews of your iOS and Android app in Slack, with version and link.
- **Release QA**: catch a new version that is rated lower than the previous one before the store rating moves.
- **Competitor tracking**: compare recent sentiment of your app vs competitors on each store.
- **ASO and localization**: reviews per App Store country and per Google Play language in one dataset.
- **Voice of customer / LLM analysis**: clean, deduplicated text ready for your own topic analysis.

### Schedule it: monitor App Store and Google Play reviews and get only what changed

1. Turn on **Monitor mode** (optionally give it a **Monitor state name**, e.g. `my-app-reviews-daily`).
2. Add a Slack incoming webhook or a webhook URL (Zapier, Make, n8n — use it to send emails too). Turn on **Alert only when there are negative reviews** if you only care about 1–2★.
3. Open **Schedules** → **Create schedule** → daily or weekly → select this Actor with your input.

Each run compares with the previous one and outputs only new reviews (`changeType: "new"`) and reviews whose author edited the rating or text (`changeType: "changed"`; a developer reply alone does not count as a change). It pages newest-first and stops as soon as it reaches reviews it already saw, so a daily check usually costs one request per app and country/language. The first run saves a baseline. You pay per new review plus a small per-app check fee.

The Slack alert lists the new negative reviews first (stars, app, store, version, text and link), then a line per app with new reviews, negatives and average.

### Actionable alerts and competitor comparison

Turn on **Actionable alerts and competitor comparison** to get deterministic alerts you cannot get by pasting the JSON into a chatbot, because they need history and thresholds:

- **Negative spike**: share of 1–2★ reviews this week vs the previous week (minimum 10 reviews per week).
- **Rating drop**: average of new reviews this week vs the previous week.
- **Version rated lower**: latest app version vs the previous one (minimum 10 reviews each).
- **Unanswered negative reviews**: 1–2★ Google Play reviews from the last 7 days without a developer reply.
- **Behind competitors**: list your app first, then competitors; your app is ranked against them on the same store (App Store and Google Play are never mixed).

Real alerts from a run on 4 Nov 2026 (Uber vs Lyft vs Bolt):

> **HIGH** — Uber: Rides, eats, hotels: version 4.655.10001 averages 3★ (22 reviews, 50% negative) vs 4.1★ for 4.654.10002 (252 reviews).
> **HIGH** — Lyft: 91 of 91 negative Google Play reviews from the last 7 days have no developer reply.
> **MEDIUM** — Uber: Rides, eats, hotels averages 3.95★ in recent reviews (25% negative) vs 4.39★ for Bolt: Request a Ride (13% negative) on Google Play. Rank 3 of 3.

Stored as **REPORT** (JSON) and **REPORT_MD** (Markdown) and included in monitor alerts. Charged as one `app-insights` event (USD 0.01) per app, only when there was enough data to compare. In monitor mode the Actor keeps a daily and per-version history, so week-over-week alerts work even for apps with thousands of reviews a day.

### How much does it cost to scrape App Store and Google Play reviews?

Pay per result (pay per event): no subscription, no platform usage fees, and failed or incomplete reviews are free.

| What | Price |
|---|---|
| Review returned | USD 0.10 per 1,000 |
| App checked in monitor mode (per app, per run) | USD 0.005 |
| Actionable alerts add-on (per app with enough data) | USD 0.01 |
| Pro mode, per review with the AI conclusion (on top of the review price) | USD 2.00 per 1,000 |

| Example | Cost |
|---|---|
| 100 reviews | USD 0.01 |
| 1,000 reviews | USD 0.10 |
| 10,000 reviews | USD 1.00 |
| Daily monitor of 1 app (iOS + Android), ~20 new reviews/day, for a month | ~USD 0.36 |
| Weekly monitor of 20 apps with alerts, ~200 new reviews/week, for a month | ~USD 1.30 |
| 1,000 reviews with Pro mode | USD 2.10 |
| Daily monitor of 1 app (iOS + Android) with Pro, ~20 new reviews/day, for a month | ~USD 1.56 |

**No surprises:** at the start of every run the log shows the price and the maximum the run can cost with your input (for example `Price: USD 0.0001 per valid result…; this run will cost at most USD 0.02 (200 results)`). Use **Maximum reviews for the whole run** or **Maximum cost per run** for a hard stop.

Apify subscribers get a lower price per event (tiers on the Pricing tab). Set **Maximum cost per run** and the Actor stops exactly there; reviews it could not deliver are not marked as seen, so the next monitor run picks them up.

### How to use the App Store & Google Play Reviews Scraper

1. Click **Try for free**.
2. Paste app links or ids in **Apps** (e.g. `https://apps.apple.com/us/app/instagram/id389801252` and `com.instagram.android`).
3. Pick App Store **countries** and Google Play **languages**, then click **Start**. Download JSON, CSV or Excel, or get the data through the API.

#### Input example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/instagram/id389801252",
    "https://play.google.com/store/apps/details?id=com.instagram.android"
  ],
  "countries": ["us"],
  "languages": ["en"],
  "maxReviewsPerApp": 100
}
```

#### Input fields

| Field | Description |
|---|---|
| `apps` | App Store URLs or numeric ids, Google Play URLs or package names (`appstore:<bundle id>` for an iOS bundle id). Both stores can be mixed. For comparisons, list your app first. |
| `countries` | App Store country codes (each storefront has its own reviews, ~500 most recent per country). Default `us`. |
| `languages` | Google Play languages (`en`, `es`, `pt-BR`…). Default `en`. |
| `maxReviewsPerApp` | Max reviews per app per country/language (counting only reviews that pass the filters). Default 500. |
| `devices` | Google Play devices: `phone` (default), `tablet`, `chromebook`, `watch`, `tv`, `car`. Each is a separate list on Google Play. |
| `sortBy` | `newest` (default, required for monitor mode), `mostHelpful` (most-voted first; Google Play calls it "Most relevant") or `rating` (highest stars first, Google Play only). |
| `minRating` / `maxRating` | Star filter, e.g. `maxRating: 2` for only negative reviews. |
| `reviewsSince` / `reviewsUntil` | Date range: a date (`2026-10-01`) or a period (`7 days`, `2 weeks`). Stops paging early. |
| `keywords` | Only reviews mentioning any of these words (title or text, case- and accent-insensitive, word start: `ads` does not match "downloads"). |
| `appVersions` | Only reviews of these versions (`8.12` matches `8.12.3`). |
| `maxTotalReviews` | Optional cap for the whole run. |
| `proMode` | Pro extras (see [Pro mode](#pro-mode-topics-urgency-ai-conclusion-and-suggested-replies)). |
| `outputFormat` | `standard` or `leader-compatible` (field names of other popular review scrapers). |
| `savedListName`, `saveList` | Save your list of apps under a name; scheduled runs reuse it. |
| `omitAuthorNames` | Default `true`: no reviewer names in the output. |
| `monitorMode`, `monitorStateName` | Only new/edited reviews since the last run. |
| `notifyWebhookUrl`, `notifySlackWebhookUrl`, `notifyOnlyNegative` | Alerts in monitor mode. |
| `generateReport` | Actionable alerts and competitor comparison (paid add-on). |
| `proxyConfiguration` | Not needed; automatic datacenter-proxy fallback if a store blocks. |

### Measured quality

Measured on a **fixed test set of 59 real inputs** (`testset/cases.json`: 27 App Store cases in 13 countries, 27 Google Play cases in 7 languages and 3 devices, 2 cross-store, 3 nonexistent apps, keyword/version/date filters), run against the live stores on 4 Nov 2026:

| Metric | Result | Target |
|---|---|---|
| Successful runs | **100%** (59/59) | ≥ 99% |
| Results with all required fields | **100%** | ≥ 98% |
| Key fields correct (app name from the store, store, country, language, device, rating range, link) | **100%** | ≥ 99% |

The same test set is re-run before every release, and every change is listed in the [changelog](CHANGELOG.md).

### Migrating from other scrapers

Already using another App Store or Google Play review scraper? Paste your current input as is: these fields are understood (ours win if both are given). Set `outputFormat: "leader-compatible"` to keep the field names your integration expects.

| Their input | Here |
|---|---|
| `appIds: ["389801252"]` or `["com.whatsapp"]` | `apps` (both stores) |
| `startUrls: ["https://…"]` / `[{ "url": "https://…" }]`, `appIdOrUrl` | `apps` |
| `country: "us"` | `countries: ["us"]` |
| `language: "en"` | `languages: ["en"]` |
| `maxItems: 500` (whole run) | `maxTotalReviews: 500` (and `maxReviewsPerApp` if not set) |
| `maxReviews: -1` | `maxReviewsPerApp: 100000` |
| `sort: "NEWEST" / "HELPFULNESS" / "RATING"`, `sortBy: "mostRelevant"` | `sortBy: "newest" / "mostHelpful" / "rating"` |
| `rating: 1`, `ratingFilter: [1, 2]` | `minRating` / `maxRating` |
| `recentDays: 30`, `endDate: "2026-10-31"` | `reviewsSince: "30 days"`, `reviewsUntil: "2026-10-31"` |
| `appVersion: ["70.6"]`, `keywords` | `appVersions`, `keywords` |
| `deviceType: "tablet"` | `devices: ["tablet"]` |

| Their output field | Standard output | `leader-compatible` output |
|---|---|---|
| `id` | last part of `reviewId` | `id` |
| `userName` / `userImage` | `authorName` / `authorImageUrl` | `userName` / `userImage` |
| `score` / `scoreText` | `rating` | `score` / `scoreText` (+ `rating`) |
| `replyText` / `replyDate` | `developerReply.text` / `.date` | `replyText` / `replyDate` |
| `version` | `appVersion` | `version` |
| `criterias` | `criteriaRatings` | `criterias` |
| `title`, `text`, `date`, `url`, `thumbsUp`, `appId`, `country`, `language` | same name | same name |
| `userUrl` | not included: Apple's reviewer links do not open a page | — |

### Integrations and code examples

Bulk input is fine: up to 500 apps in one run, with low per-store concurrency, polite pauses, retries and your budget cap.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("cleanyield/app-store-google-play-reviews").call(run_input={
    "apps": ["https://apps.apple.com/us/app/instagram/id389801252", "com.instagram.android"],
    "countries": ["us"], "languages": ["en"], "maxReviewsPerApp": 100,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["store"], item["rating"], item["text"][:80])
```

#### JavaScript

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('cleanyield/app-store-google-play-reviews').call({
    apps: ['https://apps.apple.com/us/app/instagram/id389801252', 'com.instagram.android'],
    countries: ['us'], languages: ['en'], maxReviewsPerApp: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((r) => r.isNegative));
```

#### Sync mode (Clay, n8n, Make, HTTP)

One HTTP call returns the reviews directly (typically 2–5 s for 100 reviews of one app):

```
POST https://api.apify.com/v2/acts/cleanyield~app-store-google-play-reviews/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>
Content-Type: application/json

{ "apps": ["com.duolingo"], "languages": ["en"], "maxReviewsPerApp": 50 }
```

#### n8n, Make and Google Sheets

Ready-made templates in this Actor's repository: **n8n** "New negative app reviews → Slack" and "Weekly app reviews → Google Sheets" (appends the new reviews to a sheet after every scheduled run), **Make** "Negative reviews → Slack or email", **Clay** "Recent app review sentiment per company". Or use the official Apify node/app → **Run an Actor** → `cleanyield/app-store-google-play-reviews`.

#### For developers: use it inside your own Actor

Building your own Actor or app that needs app reviews? Call this one as your data source:

- **Fast and synchronous**: `run-sync-get-dataset-items` returns 100 reviews in a few seconds.
- **Stable schema**: every item has `schemaVersion`; fields are only added, never renamed ([changelog](CHANGELOG.md)). Validate against the dataset schema in `.actor/dataset_schema.json`.
- **Clear price**: pay per result only, no usage fees; your users' runs never pay for failed reviews.

```js
import { Actor } from 'apify';

await Actor.init();
const run = await Actor.call('cleanyield/app-store-google-play-reviews', {
    apps: ['com.duolingo', '570060128'], languages: ['en'], countries: ['us'], maxRating: 2, reviewsSince: '7 days',
});
const { items } = await Actor.apifyClient.dataset(run.defaultDatasetId).listItems();
// items[i].schemaVersion === 2
await Actor.exit();
```

### Use with AI agents / MCP

This Actor charges only per result (no usage fees), so AI agents can run and pay for it through the [Apify MCP server](https://mcp.apify.com) without an Apify account.

Example request to your assistant:

> "Get the 1 and 2 star reviews from the last 7 days for Duolingo on the App Store (US) and Google Play (English), and tell me which app version they mention most."

> "Monitor Calm's iOS and Android reviews every day with Pro mode and send the most urgent negative ones to my Slack."

Tips for agents: use `apps` with store URLs or ids (never app names), `maxReviewsPerApp` to cap cost, `maxRating: 2` for complaints, `reviewsSince` for recent reviews, and `proMode: true` only when topics, urgency or reply drafts are needed. The output is one JSON object per review with the same fields for both stores.

### Related Actors

- [AI Brand Visibility Tracker](https://apify.com/cleanyield/ai-brand-visibility-tracker) — your brand's reputation in AI assistants, next to its reputation in app stores.
- [Company Enrichment from Website](https://apify.com/cleanyield/company-enrichment-from-website) — who is behind an app: emails, socials, tech stack and hiring signals from its website.

### FAQ

#### Is this an official Apple or Google product?

No. Not affiliated with or endorsed by Apple, the App Store, Google or Google Play. The names are used only to describe where the public review data comes from.

#### Is it legal to scrape App Store and Google Play reviews?

The Actor reads only public reviews that anyone can see without logging in: Apple's public customer-reviews feed and lookup API, and the JSON that Google Play's own public app page uses to list reviews. It never logs in, solves captchas or bypasses blocks, and keeps a polite request rate. Reviewer names are omitted by default; only the public display name is available if you turn it on. You are responsible for how you use the data (e.g. GDPR if you store names).

#### What happens if a review fails or comes back incomplete?

It is retried automatically. If it still fails, it is skipped and **you are not charged**. Every run stores a `RUN_STATS` record (successes, failures by reason) in its key-value store, and `REVIEW_STATS` shows what each app returned per country/language.

#### Will the output format change?

Fields are never renamed or removed. New fields can be added; when that happens `schemaVersion` goes up (now `2`) and the [changelog](CHANGELOG.md) says what was added. Turn on `outputFormat: "leader-compatible"` if your integration expects the field names of other review scrapers.

#### Why is `country` null for Google Play and `language` null for the App Store?

Because that is what the stores really provide. Google Play serves reviews per language (the country parameter does not change them — we measured it), and Apple serves reviews per country storefront without a language. We never fill these fields with guesses.

#### How many App Store reviews can I get?

Apple's public feed exposes the ~500 most recent reviews per country storefront. Add more countries for more reviews, or use monitor mode to keep collecting every new review over time. Google Play has no fixed cap.

#### Why do I get fewer Google Play reviews than the store shows?

Google Play keeps separate review lists per language and per device. Add more `languages`, or `devices` such as `tablet` and `chromebook`. The total on the store page also counts star ratings without text, which are not reviews.

#### Do I need a proxy?

No. Both stores answer without one; if a store starts blocking, the Actor retries through Apify datacenter proxy automatically.

#### I found a bug / need a feature

Open an issue in the **Issues** tab. We usually answer within 24 hours.

# Changelog

This Actor's version history is a separate document: https://apify.com/cleanyield/app-store-google-play-reviews/changelog.md

# Actor input Schema

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

One app per line. Accepts App Store URLs (https://apps.apple.com/us/app/instagram/id389801252), App Store numeric ids (389801252), Google Play URLs (https://play.google.com/store/apps/details?id=com.instagram.android) and Android package names (com.instagram.android). To pass an iOS bundle id, prefix it: appstore:com.burbn.instagram. Both stores can be mixed in one run. For competitor comparison, list your own app first, then competitors. Not required if you use a saved list (savedListName). Inputs written for other popular review scrapers also work (appIds, startUrls, country, language, maxItems, sort): see 'Migrating from other scrapers' in the README.

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

Two-letter App Store country codes. App Store reviews are separate per country storefront, so each country is fetched separately (up to ~500 most recent reviews per country, an Apple limit). The first country is also used for the Google Play listing. Example: us, gb, ca, au, de, br, ar. Default if empty: us.

## `languages` (type: `array`):

Google Play groups reviews by language, not by country. Each language is fetched separately. Use codes like en, es, pt, de, fr, ja, pt-BR. Example: en, es. Default if empty: en.

## `devices` (type: `array`):

Google Play keeps separate reviews per device. phone (default) is what the store shows first. Add tablet, chromebook, watch, tv or car to also get reviews written on those devices (each device is fetched separately and every review has a device field). Example: phone, tablet. Default if empty: phone.

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

Stop after this many reviews for each app in each App Store country or Google Play language (counting only reviews that pass your filters). Caps your cost: you pay only for reviews returned. App Store gives at most ~500 recent reviews per country. In monitor mode this caps how many new reviews a run can return. Example: 200. Default if empty: 500.

## `sortBy` (type: `string`):

newest = most recent first (default, required for monitor mode and for date filters to stop early). mostHelpful = reviews with the most helpful votes first (on Google Play this is the store's 'Most relevant' order). For only 5-star or only 1-star reviews, use the star filters instead of a sort. rating = highest stars first (Google Play only; App Store falls back to newest). Default if empty: newest.

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

Only return reviews with at least this many stars (1–5). Leave empty for all. Example: 4 to collect positive reviews for testimonials.

## `maxRating` (type: `integer`):

Only return reviews with at most this many stars (1–5). Example: 2 to get only negative reviews (1–2★) for your support team. Use the same value in minimum and maximum to get exactly one star level (filtered server-side on Google Play).

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

Only return reviews written or edited on or after this date. Accepts a date (2026-10-01) or a relative period (7 days, 2 weeks, 3 months). With newest-first sorting the Actor stops paging as soon as it passes this date, so it is fast and cheap. Example: 30 days.

## `reviewsUntil` (type: `string`):

Only return reviews written or edited on or before this date (a date alone includes that whole day). Accepts 2026-10-31 or a relative period (7 days). Combine with 'Only reviews since' for a date range. Example: 2026-10-31.

## `keywords` (type: `array`):

Only return reviews whose title or text contains at least one of these words or phrases (case- and accent-insensitive). You pay only for matching reviews. Example: crash, refund, battery.

## `appVersions` (type: `array`):

Only return reviews written on these app versions. A version also matches its sub-versions: 2.1 matches 2.1.3. Reviews without a version are excluded when this is set. Example: 8.12, 8.13.

## `omitAuthorNames` (type: `boolean`):

On by default: authorName is always null, so the output contains no personal names (data minimization). Turn it off only if you need the public display name of each reviewer, for example to reply to them.

## `monitorMode` (type: `boolean`):

For scheduled runs: remember the reviews already seen and output only new ones (changeType=new) and reviews whose author edited the rating or text (changeType=changed). You pay only for those, plus a small per-app check fee. The first run saves a baseline. Schedule the Actor daily or weekly from the Schedules tab. Forces newest-first sorting.

## `monitorStateName` (type: `string`):

Optional name of the storage that keeps the reviews already seen. Use the same name across scheduled runs to compare against them. Leave empty to derive it automatically from the apps, countries, languages and star filters. Lowercase letters, numbers and hyphens only. Example: my-app-reviews-daily.

## `notifyWebhookUrl` (type: `string`):

Optional. When monitor mode finds new reviews, POST a JSON summary (title, lines, items with negative reviews first) to this URL. Works with Zapier, Make and n8n (use it to send emails too). Example: https://hooks.zapier.com/hooks/catch/123/abc

## `notifySlackWebhookUrl` (type: `string`):

Optional. Slack incoming-webhook URL. In monitor mode you get a message with the new 1–2★ reviews (stars, app, store, version, text and link) and the actionable alerts. Example: https://hooks.slack.com/services/T000/B000/XXXX

## `notifyOnlyNegative` (type: `boolean`):

If on, the webhook/Slack alert is sent only when the run found new 1–2★ reviews or an actionable alert. If off, you are notified whenever there are new reviews.

## `proMode` (type: `boolean`):

One checkbox for all extras. Each review gets: topics (crashes_bugs, payments_billing, login_account, ads, performance, ux_design, update_regression, customer_support, privacy_security, notifications, missing_feature, praise), priorityScore (0–100 urgency to sort your support queue), insight (one-sentence AI conclusion based only on the review), suggestedReply (AI reply draft for 1–3★ reviews, in the review's language) and appDetails (developer, developer website, category, store rating, reputation score 0–100 and rating trend). Also turns on the actionable alerts and competitor comparison below. The Pro fee is charged only for reviews that received the AI conclusion. Off by default: the basic run stays cheap and fast.

## `generateReport` (type: `boolean`):

Deterministic alerts per app: spike of 1–2★ reviews vs the previous week, average rating drop week over week, a new app version rated lower than the previous one, negative Google Play reviews left without a developer reply, and how your app (the first app of each store in the list) ranks against the competitors you listed. Stored as REPORT (JSON) and REPORT_MD (Markdown) and included in monitor alerts. Charged per app only when there is enough data to compare.

## `outputFormat` (type: `string`):

standard = this Actor's fields (recommended). leader-compatible = the field names used by other popular review scrapers (id, userName, date, score, scoreText, title, text, replyText, replyDate, version, thumbsUp, criterias, url, appId, country, language), plus reviewId, store, appName and schemaVersion, so you can switch without changing your integration.

## `maxTotalReviews` (type: `integer`):

Optional cap for the entire run across all apps, countries, languages and devices (maxReviewsPerApp caps each one). Leave empty for no total cap. Example: 1000.

## `savedListName` (type: `string`):

Optional. Give your list of apps a name (for example my-competitors) and the Actor remembers it: next runs with the same name use the saved apps plus any you add in 'apps', so scheduled runs need no edits. Lowercase letters, numbers and hyphens.

## `saveList` (type: `boolean`):

If on (default), apps typed in 'apps' are added to the saved list. Turn off to use the saved list without changing it.

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

Not needed: both stores answer without a proxy. If a store starts blocking, the Actor automatically retries through Apify datacenter proxy. Set a proxy here only if you want to force one.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128",
    "com.duolingo",
    "389801252"
  ],
  "countries": [
    "us",
    "gb",
    "ca"
  ],
  "languages": [
    "en",
    "es"
  ],
  "devices": [
    "phone",
    "tablet",
    "chromebook"
  ],
  "maxReviewsPerApp": 200,
  "sortBy": "newest",
  "minRating": 4,
  "maxRating": 2,
  "reviewsSince": "30 days",
  "reviewsUntil": "2026-10-31",
  "keywords": [
    "crash",
    "refund",
    "battery"
  ],
  "appVersions": [
    "8.12",
    "8.13"
  ],
  "omitAuthorNames": true,
  "monitorMode": false,
  "monitorStateName": "my-app-reviews-daily",
  "notifyWebhookUrl": "https://hooks.zapier.com/hooks/catch/123/abc",
  "notifyOnlyNegative": false,
  "proMode": false,
  "generateReport": false,
  "outputFormat": "standard",
  "maxTotalReviews": 1000,
  "savedListName": "my-competitors",
  "saveList": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All valid results of this run (only these are charged). JSON, CSV or Excel from the dataset.

## `runStats` (type: `string`):

RUN_STATS: results saved, failures by reason (not charged), retries and success rate.

## `dashboard` (type: `string`):

DASHBOARD: a shareable HTML page with charts and rankings for this run.

# 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/instagram/id389801252",
        "https://play.google.com/store/apps/details?id=com.instagram.android"
    ],
    "countries": [
        "us"
    ],
    "languages": [
        "en"
    ],
    "devices": [
        "phone"
    ],
    "maxReviewsPerApp": 100,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanyield/app-store-google-play-reviews").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/instagram/id389801252",
        "https://play.google.com/store/apps/details?id=com.instagram.android",
    ],
    "countries": ["us"],
    "languages": ["en"],
    "devices": ["phone"],
    "maxReviewsPerApp": 100,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("cleanyield/app-store-google-play-reviews").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/instagram/id389801252",
    "https://play.google.com/store/apps/details?id=com.instagram.android"
  ],
  "countries": [
    "us"
  ],
  "languages": [
    "en"
  ],
  "devices": [
    "phone"
  ],
  "maxReviewsPerApp": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call cleanyield/app-store-google-play-reviews --silent --output-dataset

```

## MCP server setup

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

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/L2KWIN4aef768wpr8/builds/QsDfrQHYPooaybQrX/openapi.json
