# App Store & Google Play Reviews Scraper: Ratings, Text (`actose/app-store-reviews`) Actor

App Store and Google Play reviews in one actor: rating, title, text, app version, date, author, helpful votes, developer reply. 150+ countries, only-new mode, Markdown for LLMs. No API key, no login. MCP-ready for AI agents. Errors and empty results are free. $0.20 per 1,000 reviews.

- **URL**: https://apify.com/actose/app-store-reviews.md
- **Developed by:** [ABM](https://apify.com/actose) (community)
- **Categories:** AI
- **Stats:** 4 total users, 2 monthly users, 97.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.17 / 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

**$0.20 per 1,000 reviews. Pay only for reviews delivered: apps not found, errors and empty results are free.**

Scrape app reviews from the **Apple App Store** (iPhone, iPad, Mac) and **Google Play** (Android) in one actor. Paste app IDs or store URLs and get every review as clean JSON: **star rating, title, text, app version, date, author, helpful votes and the developer's reply**, plus the app's name and average rating. Works in 150+ countries, keeps only new reviews on scheduled runs, and gives every review in Markdown so you can pass it straight to an LLM.

- Both stores in one run, with the same output fields
- No API key, no login, no browser, no proxy needed
- Filters: star ratings (e.g. only 1★ and 2★), date ("since"), sort by newest or most relevant
- `onlyNew` mode for daily and weekly monitoring
- Export to JSON, CSV, Excel, or use it from the API, Make, Zapier, n8n, Google Sheets or MCP clients (Claude, Cursor, ChatGPT)

### How much does it cost?

You pay **$0.0002 per review** ($0.20 per 1,000), with no monthly rental.

| You scrape | You pay |
|---|---|
| 100 reviews of 1 app | $0.02 |
| 1,000 reviews (e.g. 10 apps × 100) | $0.20 |
| 10,000 reviews | $2.00 |
| 500 new reviews a day for a month (scheduled `onlyNew` run) | about $3.00 |

You are not charged for apps that don't exist, store errors, duplicates, reviews outside your filters, or reviews already delivered in `onlyNew` mode. When your maximum cost per run is reached, the actor stops cleanly and doesn't deliver anything past it.

### Input

| Field | What it does |
|---|---|
| `apps` | App Store IDs/URLs and Google Play package names/URLs, mixed freely |
| `countries` | 2-letter codes (`us`, `gb`, `fr`, `de`, `jp`…). Each app is scraped in each country |
| `maxReviewsPerApp` | Maximum reviews per app and country (default 100) |
| `sort` | `newest` (default) or `mostRelevant` |
| `ratings` | Keep only these ratings, e.g. `[1, 2]` |
| `sinceDate` | Only reviews posted on or after this date (`YYYY-MM-DD`) |
| `language` | Google Play review language (default: main language of the country) |
| `onlyNew` | Skip reviews delivered by previous runs |
| `store` | `appStore` to treat `com.company.app` IDs as iOS bundle IDs |

Accepted app formats:

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

### Output

One dataset item per review. Here is an App Store review:

```json
{
  "store": "appStore",
  "appId": "389801252",
  "appName": "Instagram",
  "appUrl": "https://apps.apple.com/us/app/instagram/id389801252",
  "bundleId": "com.burbn.instagram",
  "country": "us",
  "language": null,
  "reviewId": "14664979832",
  "rating": 5,
  "title": "A good app",
  "text": "I use it every day to keep up with friends…",
  "version": "451.0.0",
  "date": "2026-10-15T17:42:10Z",
  "userName": "Happy slayer 101",
  "userUrl": "https://itunes.apple.com/us/reviews/id1234567890",
  "userImageUrl": null,
  "thumbsUpCount": null,
  "voteSum": 0,
  "voteCount": 0,
  "developerReply": null,
  "developerReplyDate": null,
  "sortBy": "newest",
  "appRating": 4.691,
  "appRatingCount": 29598865,
  "developer": "Instagram, Inc.",
  "currentAppVersion": "451.0.0",
  "input": "389801252",
  "markdown": "### ★★★★★ A good app\n\nInstagram · App Store · US · 2026-10-15T17:42:10Z · v451.0.0 · Happy slayer 101\n\nI use it every day…",
  "scrapedAt": "2026-10-16T08:00:03Z"
}
```

And a Google Play review, which also has helpful votes and the developer's reply:

```json
{
  "store": "googlePlay",
  "appId": "com.duolingo",
  "appName": "Duolingo: Language Lessons",
  "appUrl": "https://play.google.com/store/apps/details?id=com.duolingo",
  "country": "us",
  "language": "en",
  "reviewId": "a1b2c3d4-…",
  "rating": 2,
  "title": null,
  "text": "Since the last update I lose my streak when…",
  "version": "6.42.3",
  "date": "2026-10-14T21:05:44Z",
  "userName": "Jane D.",
  "userImageUrl": "https://play-lh.googleusercontent.com/…",
  "thumbsUpCount": 37,
  "developerReply": "Hi Jane, sorry about that! Please contact us at…",
  "developerReplyDate": "2026-10-15T09:12:00Z",
  "sortBy": "newest"
}
```

The **SUMMARY** record in the key-value store lists, for each app and country, how many reviews were delivered and whether the app was not found or failed.

### Use cases

#### 1. Daily 1★ and 2★ alerts for a mobile product team

**Who:** product manager or mobile QA lead.
Every morning a scheduled run collects the new negative reviews of the team's app on both stores and sends them to Slack, so a broken release gets spotted within hours instead of in the next sprint review.

Input:

```json
{
  "apps": ["https://apps.apple.com/us/app/duolingo-language-lessons/id570060128", "com.duolingo"],
  "countries": ["us", "gb"],
  "ratings": [1, 2],
  "sort": "newest",
  "maxReviewsPerApp": 200,
  "onlyNew": true
}
```

Output (excerpt):

```json
[{"store": "googlePlay", "rating": 1, "version": "6.42.3", "text": "App crashes on launch since the update", "date": "2026-10-15T22:10:03Z"}]
```

Next: a Make or n8n scenario posts each item to `#app-reviews`, grouped by `version`. A typical consumer app produces 20–80 negative reviews a day, which costs under $0.50 a month.

#### 2. Competitor benchmark for an app marketing agency

**Who:** ASO consultant or app marketing agency.
Pull the last 500 reviews of 4 music apps in 3 markets (6,000 reviews, $1.20), then have an LLM label the complaints (pricing, bugs, ads, missing features) from the `markdown` field.

```json
{
  "apps": ["324684580", "com.spotify.music", "deezer.android.app", "com.soundcloud.android"],
  "countries": ["us", "fr", "de"],
  "maxReviewsPerApp": 500,
  "sort": "newest"
}
```

Output (excerpt):

```json
[{"appName": "Spotify: Music and Podcasts", "country": "fr", "rating": 2, "title": "Trop de pubs", "text": "Depuis la mise à jour…"}]
```

Next: export to CSV and build a pivot (complaint theme × app × country) for the client deck.

#### 3. Release follow-up in Google Sheets for a support team

**Who:** customer support manager.
After each release, collect every review mentioning the new version since release day and track which ones got a developer reply.

```json
{
  "apps": ["com.whatsapp", "310633997"],
  "countries": ["us"],
  "sinceDate": "2026-10-01",
  "maxReviewsPerApp": 1000
}
```

Output (excerpt):

```json
[{"store": "googlePlay", "version": "2.26.21.7", "rating": 3, "developerReply": null, "thumbsUpCount": 112}]
```

Next: Google Sheets integration → filter `developerReply = empty` and sort by `thumbsUpCount` to answer the most-upvoted reviews first.

### Use with the API

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("<username>/app-store-reviews").call(run_input={
    "apps": ["389801252", "com.instagram.android"],
    "countries": ["us"],
    "maxReviewsPerApp": 200,
    "ratings": [1, 2],
})
for review in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(review["store"], review["rating"], review["text"])
```

**JavaScript**

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

const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('<username>/app-store-reviews').call({
    apps: ['389801252', 'com.instagram.android'],
    countries: ['us'],
    maxReviewsPerApp: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'reviews');
```

**One HTTP call** (run and get the reviews in a single request):

```
POST https://api.apify.com/v2/acts/<username>~app-store-reviews/run-sync-get-dataset-items?token=<APIFY_TOKEN>
{"apps": ["com.duolingo"], "maxReviewsPerApp": 50}
```

### Automation (Make, n8n, Zapier, Google Sheets)

Example n8n workflow: **Schedule Trigger** (every day at 7:00) → **Apify node** "Run actor and get dataset" with `{"apps": ["com.yourapp"], "ratings": [1,2], "onlyNew": true}` → **IF** `rating <= 2` → **Slack** message `{{$json.rating}}★ v{{$json.version}}: {{$json.text}}`.
In Make or Zapier, use the Apify "Run an Actor" and "Get dataset items" modules the same way. For Google Sheets, add the Google Sheets integration on the actor's task: every run appends the new reviews as rows.

### Use with AI agents (MCP)

The actor works as a tool in the Apify MCP server, so Claude, Cursor, ChatGPT or any MCP client can call it, e.g. *"Get the 100 latest 1-star reviews of com.duolingo and summarize the top 5 complaints"*. Add it with `https://mcp.apify.com?tools=<username>/app-store-reviews`. Each review has a `markdown` field (stars, app, country, date, version, text, developer reply) that you can pass straight to an LLM or a RAG index.

### FAQ

**Do I need an Apple or Google account, or an API key?** No. The actor only reads public review data, the same reviews anyone can see in the stores.

**How many reviews can I get?** On Google Play, as many as the store shows (thousands for popular apps). On the App Store, Apple's public feed gives up to 500 reviews per country and sort order. If you ask for more than 500, the actor combines both sort orders and gets up to about 1,000 per country. To go further, add more countries.

**Why are App Store reviews different from one country to another?** Apple keeps a separate set of reviews for each country's store. Add every market you care about in `countries`.

**Why do I get few Google Play reviews in some countries?** Google Play returns reviews written in one language at a time. Set `language` (e.g. `"es"`) or leave it empty to use the main language of each country.

**Is the developer reply included?** Yes for Google Play (`developerReply`, `developerReplyDate`). Apple's public feed doesn't include developer replies, so these fields are `null` for the App Store.

**What happens with a wrong ID?** The app is skipped with a clear message in the log and in the `SUMMARY` record, and you aren't charged. Other apps carry on as normal.

**How does `onlyNew` work?** The IDs of the reviews already delivered are kept for each app and country in a key-value store named `app-store-reviews-state` on your account. On the next run they are skipped and not charged.

### Limits

- App Store: 500 reviews per country and sort order (about 1,000 with both orders). This is a limit of Apple's public feed.
- App Store reviews have no developer reply, avatar or "helpful" count. Google Play reviews have no title.
- Google Play returns one review language per run (set with `language`).
- Ratings without text (star-only ratings) aren't published by either store, so they are not included. `appRating` and `appRatingCount` give the overall score for App Store apps.
- The stores can change their public endpoints. If one does, the affected apps fail with a clear error and nothing is charged.

# Actor input Schema

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

One app per line, from either store. App Store: numeric ID (389801252), id389801252 or a URL like https://apps.apple.com/us/app/instagram/id389801252. Google Play: package name (com.instagram.android) or a URL like https://play.google.com/store/apps/details?id=com.instagram.android.

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

2-letter country codes (us, gb, fr, de, jp...). Each app is scraped in every country listed. Reviews are separate per App Store country. If empty, the country in the App Store URL is used, otherwise 'us'.

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

Stop after this many reviews for each app/country. The public App Store feed gives up to 500 reviews per sort order and country (up to about 1,000 when you ask for more than 500 and both sort orders are combined). Google Play has no such limit.

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

Newest first (best for monitoring) or most relevant / most helpful first.

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

Keep only reviews with these ratings, e.g. \[1, 2] for negative reviews. Empty = all ratings. Reviews filtered out are not charged.

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

Date YYYY-MM-DD (or ISO date-time). Older reviews are skipped and not charged. With 'Newest first', scraping stops as soon as older reviews are reached.

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

Google Play returns reviews written in one language at a time (en, fr, de, es, pt, ja...). Empty = the main language of each country (us→en, fr→fr, de→de, jp→ja...). Ignored for the App Store, whose reviews are per country.

## `store` (type: `string`):

Numeric IDs and apps.apple.com URLs always go to the App Store, play.google.com URLs to Google Play. A package-style ID such as com.burbn.instagram goes to Google Play by default; choose 'App Store' to treat it as an iOS bundle ID.

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

For scheduled runs: skip reviews already delivered by a previous run of this actor on your account (remembered per app and country in the key-value store 'app-store-reviews-state'). Skipped reviews are not charged.

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

How many app/country combinations are scraped at the same time. Keep it low to stay polite with the stores.

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

Not needed in most cases: both stores answer directly. If a store starts rate-limiting, the actor switches to the Apify datacenter proxy by itself. Set a proxy here only if you want to force one.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/instagram/id389801252",
    "com.instagram.android"
  ],
  "countries": [
    "us"
  ],
  "maxReviewsPerApp": 50,
  "sort": "newest",
  "ratings": [],
  "store": "auto",
  "onlyNew": false,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One item per review

## `reviewsFull` (type: `string`):

No description

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

Reviews delivered per app, apps not found, traffic

# 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",
        "com.instagram.android"
    ],
    "countries": [
        "us"
    ],
    "maxReviewsPerApp": 50,
    "ratings": [],
    "language": "",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("actose/app-store-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",
        "com.instagram.android",
    ],
    "countries": ["us"],
    "maxReviewsPerApp": 50,
    "ratings": [],
    "language": "",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("actose/app-store-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",
    "com.instagram.android"
  ],
  "countries": [
    "us"
  ],
  "maxReviewsPerApp": 50,
  "ratings": [],
  "language": "",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call actose/app-store-reviews --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,actose/app-store-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/FZe5bMDYniesvpqKk/builds/zFGLgWVhgbyxie20t/openapi.json
