# App Store & Google Play Reviews Scraper by Adaptive Labs (`adaptive_labs/app-reviews-play-appstore`) Actor

Scrape app reviews from Google Play and the Apple App Store in one run with one normalised schema: rating, text, date, version, developer reply. Sort, date cutoff, only-new mode. No reviewer names.

- **URL**: https://apify.com/adaptive_labs/app-reviews-play-appstore.md
- **Developed by:** [Adaptive Labs](https://apify.com/adaptive_labs) (community)
- **Categories:** Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 review scrapeds

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 by Adaptive Labs

Collect publicly available **Google Play** reviews and a limited **Apple App Store** review sample in one run. Both stores use one normalised dataset. This is an Apify Actor for product research and feedback analysis.

Tested Apple output is **approximately 10** reviews embedded in the public app page for the selected country. Apple chooses that subset. Those reviews are **not necessarily the newest**, and they are not a complete history. A larger maximum does not remove that limit. Apple's RSS route is implemented, at most 10 pages, but deeper RSS retrieval has not been demonstrated.

### What does this Actor do?

It reads public review pages for the Google Play package names and App Store ids you provide. Google Play can page through a chosen maximum, sorted by newest, most relevant, or rating. The App Store path returns the tested page sample described above. It does not log into either store.

An empty app list runs the built-in Spotify example on both stores and, when no maximum was set, caps that example at 20 reviews.

### Why use it?

- One schema for Google Play and the App Store, including scheduling and the Apify dataset API
- Google Play sort: newest, most relevant, or rating
- Country and language selection. Language applies to Google Play
- A per-app maximum, so a run stays inside the window you set
- An optional date cutoff
- Optional saved-review filtering inside that fetched window
- No store login and a 512 MB memory cap

### What data can it extract?

| Field | What it is |
| --- | --- |
| store | `google_play` or `app_store` |
| appId | Package name or numeric App Store id |
| reviewId | Stable source review id |
| rating | 1 to 5 |
| title, text | Review copy. Title is empty for Google Play |
| date | Review time, when the source sends one |
| appVersion | Version string, when available |
| developerReply | Google Play reply text and date. App Store replies are not read |
| country, language | Storefront used for the request. App Store language is null |
| authorHash | Deterministic hash of the reviewer handle |
| url | Public page for the app or review |

Reviewer names are not a separate output field. `authorHash` is pseudonymisation, not a guarantee of anonymity. Review text and the hash can still be personal data. Handle them lawfully.

### How to scrape app reviews

1. Add Google Play package names or Play Store links, for example `com.spotify.music`.
2. Add App Store numeric ids or apps.apple.com links, for example `324684580`.
3. Choose country, language, Google Play sort, and the maximum reviews per app.
4. Optionally set a date cutoff, or turn on only-new and choose a monitor key.
5. Run the Actor and download the dataset as JSON, CSV, Excel, or HTML.

### How much does it cost?

Proposed Store pricing, approved by the publisher and **not confirmed active**:

- **$0.005** per run start, charged by the Apify platform as `apify-actor-start`
- **$0.0015** per saved review, charged by this Actor as `review`
- `apify-default-dataset-item` is not part of this design and stays off, so a saved review is not billed again as a dataset item

Activation has not been verified. Check the live Pricing tab before running. Apify platform usage charges may apply. No zero-cost run is promised.

A review is charged only when it is saved. Reviews skipped as already seen, duplicated in the same run, or older than the cutoff are not charged. Each saved review emits one `review` event.

### Input

See the input tab for every field.

- **App Store ids.** The current fallback returns approximately 10 Apple-selected reviews for tested apps and countries, not complete or necessarily newest history.
- **Max reviews per app.** This bounds how many reviews are fetched for each app. It does not extend the Apple page sample.
- **Only new.** Skip previously saved review ids for the same monitor key within the reviews fetched this run. This does not guarantee complete monitoring of all reviews.
- **Monitor key.** Separate saved state. Keys do not share seen reviews. Characters other than letters, numbers, hyphens, and underscores are stored as underscores, so two different names can land on one key.
- **Date cutoff.** A value such as `2026-10-01` is UTC midnight. A review at that exact instant is kept. An earlier review is skipped. A missing or unparseable review date is kept. An invalid cutoff fails the run before any fetch.

With Google Play sort set to newest, and for App Store results, scanning stops at the first review older than the cutoff. Most relevant and rating sorts keep scanning the fetched window, so a later review that is still new enough is not hidden.

```json
{
  "googlePlayAppIds": ["com.spotify.music"],
  "appStoreAppIds": ["324684580"],
  "country": "gb",
  "language": "en",
  "maxReviewsPerApp": 100,
  "playSort": "newest",
  "reviewsNewerThan": "2026-10-01",
  "onlyNew": false,
  "monitorKey": "default"
}
```

### Only new since last run

When only-new is on, the Actor still fetches up to the per-app maximum. One seen review does not end that window. Later rows can be new, including after a relevant or rating sort and inside Apple's selected page.

Reviews saved by this version are remembered as store, app id, country, and review id. The same source id on another app, store, or country is kept. Language is not part of the identity. The same review fetched again for the same app and country is skipped and is not charged again.

State written by earlier builds stored only `store:reviewId`. Those older entries still count as seen for that store and review id, including another app that reuses the id, so a previously saved review is not saved or charged again.

The list keeps at most 100,000 identities. Entries that fall off that cap can be saved again on a later run.

Only-new off still drops duplicate ids inside the current run. It does not read or replace the saved monitor list.

### Output

You can download the dataset as JSON, HTML, CSV, or Excel.

```json
{
  "store": "google_play",
  "appId": "com.spotify.music",
  "reviewId": "6dc10ff4-2dc6-49a7-8dd5-a881e4ebf5ca",
  "country": "gb",
  "language": "en",
  "rating": 3,
  "title": null,
  "text": "Good app but very limited if you do not have premium...",
  "date": "2026-11-04T08:47:43.000Z",
  "appVersion": null,
  "helpfulVotes": 0,
  "developerReply": null,
  "developerReplyDate": null,
  "authorHash": "a3681958d3e21788",
  "url": "https://play.google.com/store/apps/details?id=com.spotify.music&reviewId=6dc10ff4-2dc6-49a7-8dd5-a881e4ebf5ca",
  "scrapedAt": "2026-11-05T12:00:00.000Z"
}
```

A source that throws is logged and the other sources still run. If at least one source finishes, the run succeeds. It can contain a partial set or zero reviews. The run fails only when every source throws. Read the per-store lines in the log. A successful run does not prove both stores returned reviews.

### Tips

- Use a monitor key per country when you want separate only-new lists. Country is already part of each saved identity.
- Keep the maximum at the volume you actually need. Google Play stops once that many reviews have been fetched.
- Do not treat the Apple sample as a history export or as the latest reviews.

### FAQ, disclaimer, and support

This Actor reads publicly available reviews. Review text may contain personal data. Use the data in line with each store's terms and with data protection law, including UK GDPR. You need a lawful basis for any personal data in the output. Not affiliated with Google or Apple.

Questions, bugs, or feature requests: support@adaptivelabs.app, or the Issues tab on the Actor.

### Local checks

Offline tests do not call Apify, do not use a token, and do not start a paid run.

```bash
npm test
npm run typecheck
```

# Actor input Schema

## `googlePlayAppIds` (type: `array`):

Package names or Google Play app URLs, for example com.spotify.music or https://play.google.com/store/apps/details?id=com.spotify.music

## `appStoreAppIds` (type: `array`):

Numeric App Store IDs or apps.apple.com URLs, for example 324684580. The current fallback returns approximately 10 Apple-selected reviews for tested apps and countries, not complete or necessarily newest history.

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

Two-letter country code for the store, e.g. gb, us, de.

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

Two-letter language code for Google Play reviews.

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

Upper limit of reviews fetched per app and store. Google Play pages until this limit. The App Store fallback still returns approximately 10 Apple-selected reviews; a higher maximum does not fetch reviews beyond that page.

## `playSort` (type: `string`):

How Google Play orders reviews. The date cutoff stops early only with Newest first. Only-new mode still scans the rest of the fetched window after a seen review.

## `reviewsNewerThan` (type: `string`):

Optional date (YYYY-MM-DD), inclusive of that UTC midnight. Earlier reviews are skipped. A missing review date is kept. Newest-first and App Store scans stop at the first earlier review; other Google Play sorts keep scanning the fetched window.

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

Skip previously saved review IDs for the same monitor key within the reviews fetched this run. This does not guarantee complete monitoring of all reviews.

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

Name of the saved only-new state. Different keys do not share seen reviews. Characters other than letters, numbers, hyphens and underscores are stored as underscores.

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

Optional proxy settings.

## Actor input object example

```json
{
  "googlePlayAppIds": [
    "com.spotify.music"
  ],
  "appStoreAppIds": [
    "324684580"
  ],
  "country": "gb",
  "language": "en",
  "maxReviewsPerApp": 20,
  "playSort": "newest",
  "onlyNew": false,
  "monitorKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Default dataset of scraped reviews (overview view).

# 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 = {
    "googlePlayAppIds": [
        "com.spotify.music"
    ],
    "appStoreAppIds": [
        "324684580"
    ],
    "maxReviewsPerApp": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("adaptive_labs/app-reviews-play-appstore").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 = {
    "googlePlayAppIds": ["com.spotify.music"],
    "appStoreAppIds": ["324684580"],
    "maxReviewsPerApp": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("adaptive_labs/app-reviews-play-appstore").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 '{
  "googlePlayAppIds": [
    "com.spotify.music"
  ],
  "appStoreAppIds": [
    "324684580"
  ],
  "maxReviewsPerApp": 20
}' |
apify call adaptive_labs/app-reviews-play-appstore --silent --output-dataset

```

## MCP server setup

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

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/czH4gM7ay4GdVq9Zo/builds/590EyPuDqYBIg6dS0/openapi.json
