# App Reviews API (App Store & Google Play) (`headlessagent/app-reviews-api`) Actor

Scrape and unify reviews and ratings from Apple App Store and Google Play into one standardized dataset schema. HTTP-only, fast, zero browser overhead.

- **URL**: https://apify.com/headlessagent/app-reviews-api.md
- **Developed by:** [Headless Agent](https://apify.com/headlessagent) (community)
- **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 Reviews API (App Store & Google Play)

Unified Apify Actor for customer reviews and ratings from both the **Apple App Store** and the **Google Play Store**, delivered in a single standardized dataset schema.

***

### Features

- **Unified Schema**: Normalizes Apple App Store and Google Play reviews into identical column shapes with matching types and conventions.
- **Multi-Store & Multi-Country**: Query iOS and Android apps simultaneously across any ISO 3166-1 alpha-2 country store (e.g. `us`, `ca`, `gb`, `de`, `au`, `jp`).
- **Flexible App Identifiers**: Accepts numeric App Store IDs, iOS bundle identifiers, Google Play package names, or direct store URLs.
- **Denormalized App Metadata**: Optionally attaches app-level title, overall rating score, and rating count directly onto each review record.
- **Smart Deduplication**: Automatically deduplicates reviews by `store + country + reviewId`.
- **Date Filtering (`since`)**: Stops pagination and omits reviews older than an ISO 8601 date cutoff.
- **Reliable runs**: Temporary errors are retried, and a failure on one app does not stop the rest.
- **Strict Hard Cap**: `maxReviews` is strictly enforced per app across all specified countries.

***

### Input Parameters

| Field | Type | Default | Description |
|---|---|---|---|
| `apps` | `array` | *Required* | Array of objects `{ store: "apple" \| "google", id: string, country?: string }`. `id` can be a bundle ID, numeric ID, or store URL. |
| `countries` | `array` | `["us"]` | Default ISO country codes to scrape when not specified on the app object. |
| `sort` | `string` | `"recent"` | Sort order: `"recent"` (most recent reviews) or `"helpful"` (most helpful / upvoted). |
| `since` | `string` | `undefined` | Optional ISO 8601 date cutoff (e.g. `"2026-01-01"`). Reviews older than this date are omitted and pagination stops. |
| `maxReviews` | `integer` | `200` | Maximum reviews to retrieve per app across all target countries (min: 1, max: 5000). |
| `includeAppInfo` | `boolean` | `true` | When true, fetches app metadata (title, average rating, rating count) and denormalizes it onto each review item. |

#### Example Input

```json
{
  "apps": [
    {
      "store": "apple",
      "id": "570060128",
      "country": "ca"
    },
    {
      "store": "apple",
      "id": "https://apps.apple.com/gb/app/subway-surfers/id512939461"
    },
    {
      "store": "google",
      "id": "com.whatsapp",
      "country": "us"
    },
    {
      "store": "google",
      "id": "https://play.google.com/store/apps/details?id=com.spotify.music"
    }
  ],
  "countries": [
    "us"
  ],
  "sort": "recent",
  "maxReviews": 50,
  "includeAppInfo": true
}
```

***

### Output Fields

Each dataset item represents a single customer review with the following normalized structure:

| Field | Type | Description |
|---|---|---|
| `store` | `string` | `"apple"` or `"google"`. |
| `appId` | `string` | App identifier (numeric ID for Apple, package name for Google). |
| `country` | `string` | ISO 3166-1 alpha-2 country code of the store. |
| `reviewId` | `string` | Unique review identifier assigned by the store. |
| `rating` | `integer \| null` | Rating given by the user (1 to 5 stars). |
| `title` | `string \| null` | Review headline (provided by Apple; `null` for Google Play). |
| `text` | `string \| null` | Main body text of the review. |
| `author` | `string \| null` | Reviewer's display name or nickname. |
| `version` | `string \| null` | App version reported when review was posted. |
| `updatedAt` | `string \| null` | ISO 8601 timestamp when the review was written or updated. |
| `helpfulCount` | `integer \| null` | Number of helpful votes / upvotes. |
| `responseText` | `string \| null` | Developer reply text when the store provides it, otherwise `null`. |
| `appTitle` | `string \| null` | Denormalized app display name. |
| `appRatingAverage` | `number \| null` | Denormalized overall average rating of the app. |
| `appRatingCount` | `integer \| null` | Denormalized total rating count of the app. |
| `fetchedAt` | `string` | ISO 8601 timestamp when the record was extracted. |

***

### Real Run Output Examples

#### Apple App Store Review Example

```json
{
  "store": "apple",
  "appId": "570060128",
  "country": "ca",
  "reviewId": "14712236571",
  "rating": 1,
  "title": "I think ai may be destroying this",
  "text": "Not really sure what happened, but the last while when going into a lot of the story parts of Duolingo, there have been a lot of new words differently appearing in the story, instead of learning the words in the lessons and then using them in the stories. It feels like a glitch, I have a feeling they are using ai a lot now and it's not really building on words learned before.",
  "author": "Get out 55",
  "version": "7.145.1",
  "updatedAt": "2026-10-26T04:05:08.000Z",
  "helpfulCount": 0,
  "responseText": null,
  "appTitle": "Duolingo: Language & Chess",
  "appRatingAverage": 4.63335,
  "appRatingCount": 674388,
  "fetchedAt": "2026-10-27T08:18:44.805Z"
}
```

#### Google Play Store Review Example

```json
{
  "store": "google",
  "appId": "com.whatsapp",
  "country": "us",
  "reviewId": "3395f87f-8464-4f0b-8bc3-8e8722d909a2",
  "rating": 5,
  "title": null,
  "text": "super",
  "author": "Praveen Kumar",
  "version": "2.26.40.76",
  "updatedAt": "2026-10-26T08:18:28.000Z",
  "helpfulCount": 0,
  "responseText": null,
  "appTitle": "WhatsApp",
  "appRatingAverage": 4.6240306,
  "appRatingCount": 246488947,
  "fetchedAt": "2026-10-27T08:18:46.582Z"
}
```

***

### License

Apache-2.0

# Actor input Schema

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

List of apps to scrape reviews for. Each object must contain 'store' ('apple' or 'google') and 'id' (bundle id, numeric App Store id, or store URL). Optionally provide 'country' to override default countries for this app.

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

Array of ISO 3166-1 alpha-2 country codes to fetch reviews from when not explicitly specified per app.

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

Sort order for reviews: 'recent' (default) or 'helpful' when supported by the store.

## `since` (type: `string`):

Optional ISO 8601 date cutoff (e.g. '2026-01-01'). Reviews older than this date are omitted and pagination stops.

## `maxReviews` (type: `integer`):

Maximum number of reviews to collect per app across all target countries (hard cap).

## `includeAppInfo` (type: `boolean`):

Include app metadata (title, average rating, rating count) denormalized onto each review item.

## Actor input object example

```json
{
  "apps": [
    {
      "store": "apple",
      "id": "570060128",
      "country": "ca"
    },
    {
      "store": "google",
      "id": "com.whatsapp",
      "country": "us"
    }
  ],
  "countries": [
    "us"
  ],
  "sort": "recent",
  "maxReviews": 200,
  "includeAppInfo": true
}
```

# Actor output Schema

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

Dataset containing unified reviews and ratings from Apple App Store and Google Play.

# 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": [
        {
            "store": "apple",
            "id": "570060128",
            "country": "ca"
        },
        {
            "store": "google",
            "id": "com.whatsapp",
            "country": "us"
        }
    ],
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("headlessagent/app-reviews-api").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": [
        {
            "store": "apple",
            "id": "570060128",
            "country": "ca",
        },
        {
            "store": "google",
            "id": "com.whatsapp",
            "country": "us",
        },
    ],
    "countries": ["us"],
}

# Run the Actor and wait for it to finish
run = client.actor("headlessagent/app-reviews-api").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": [
    {
      "store": "apple",
      "id": "570060128",
      "country": "ca"
    },
    {
      "store": "google",
      "id": "com.whatsapp",
      "country": "us"
    }
  ],
  "countries": [
    "us"
  ]
}' |
apify call headlessagent/app-reviews-api --silent --output-dataset

```

## MCP server setup

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

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/kPrrIk4TcC29KHkl1/builds/G2LWI0w191dOyaA7y/openapi.json
