# App Store & Google Play Review Monitor (`hamzatrq/app-review-monitor`) Actor

Monitor app reviews in one feed: scrape Apple App Store reviews with version tags, derived sentiment, and new-review alerts. For product, ASO, and competitor tracking, with a hard cost cap. Google Play planned as a follow-on.

- **URL**: https://apify.com/hamzatrq/app-review-monitor.md
- **Developed by:** [Hamza Tariq](https://apify.com/hamzatrq) (community)
- **Categories:** Developer tools, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

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

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


# README

## App Store & Google Play Review Monitor — reviews in one feed with alerts

Monitor **app store reviews** across the **Apple App Store and Google Play** and catch new
ones the moment they land. An app review **scraper / extractor / data API / monitor / feed**
that reads each store's public reviews, normalizes every review to one schema with a star
rating, version tag, country, app name, and a rating-based sentiment label, and turns repeated
runs into a **new-review monitor** — for product teams, ASO agencies, and competitor-review
watchers.

> **Quick Start:** click **Start** with the default input. With zero configuration it pulls a
> well-known public app's most recent App Store reviews and returns clean rows — rating,
> rating sentiment, title, body, app version, country, author, and date. To track your own
> apps, paste numeric **Apple App Store app IDs** into **appIds** (e.g. `389801252` — the
> number after `id` in an App Store URL) and/or **Google Play package IDs** into
> **googlePlayAppIds** (e.g. `com.spotify.music`). First useful results in under two minutes.

### What it does

Both stores expose public reviews without a login, so the data is clean and easy to extract —
no browser, no keys. This actor:

- pulls an app's **most recent reviews** with rating, title, body, app version, and author —
  from the **Apple App Store** (public customer-reviews JSON feed) and **Google Play** (the
  public Play Store reviews endpoint), normalized into one feed;
- enriches Apple reviews with the **app name** via the iTunes Lookup API (once per app);
- labels each review with a **rating sentiment** (positive / neutral / negative) derived
  **purely from the star rating** — not from analyzing the review text (see the note below);
- filters by **rating range** and **keywords** so you can watch, e.g., only 1–2 star reviews
  mentioning `crash` or `refund`;
- on a schedule, becomes a **monitor** — emitting (and charging for) only reviews that are
  **new** or whose text changed (e.g. an edited review) since the last run.

> **About `ratingSentiment`:** it is a label derived only from the number of stars
> (4–5 = positive, 3 = neutral, 1–2 = negative). It is **not** natural-language sentiment of
> the review text — a 5-star review with an angry message is still labelled "positive". Use
> the raw `rating` field when you need the exact star count.

### Who it's for

App product teams watching feedback per release, ASO agencies, reputation managers, and teams
tracking competitor reviews across both stores.

### Sources

- **Apple App Store** — the public, no-auth customer-reviews JSON feed (with iTunes Lookup for
  the app name).
- **Google Play** — the public Play Store reviews endpoint the Store details page itself uses
  to load its review list (no login, no browser). Supply one or more package IDs in
  `googlePlayAppIds`. Note: Google Play reviews have no separate title, so `title` is empty for
  Google Play rows; everything else (rating, body, version, author, date, votes) is populated.

### Input

Runs **zero-config** (App Store demo app). Optional fields:

- `appIds` — numeric Apple App Store app IDs to monitor (the number after `id` in an App Store
  URL). Leave empty for a demo run on a known public app.
- `googlePlayAppIds` — Google Play package IDs to monitor (e.g. `com.spotify.music`).
- `country` — two-letter storefront country code (e.g. `us`, `gb`). Applies to both stores.
- `countries` — multiple storefront country codes (one feed per country, merged). Overrides
  `country` when set.
- `minRating` / `maxRating` — keep only reviews within a star-rating band (1–5).
- `keywords` — keep only reviews whose title or body contains any of these terms.
- `maxItems` — cap on reviews returned per run (keeps runs cheap). The budget is shared
  round-robin across every requested app and storefront, so each gets coverage rather than the
  first app draining it all. Note: Apple's public feed serves only its most recent reviews per
  app (~the latest few hundred), so very large `maxItems` values may return fewer Apple rows.
- `maxCostPerRunUsd` — hard ceiling on spend per run (default $5).

### Output

One `AppReview` per row: `store`, `appId`, `appName`, `rating`, `ratingSentiment`, `title`,
`body`, `version`, `country`, `author`, `date`, and the review `sourceUrl`, plus a stable
store-scoped `id`. See `.actor/dataset_schema.json`.

### Pricing

Pay-per-event: a small start fee plus a per-result charge, with a hard per-run cost cap on by
default — so the bill is never a surprise. In monitor mode you pay only for new or changed
reviews. Built on each store's public reviews endpoints, so it is low-maintenance — fixes
within 24h.

# Actor input Schema

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

One or more numeric Apple App Store app IDs to monitor reviews for. The numeric id is the number in an App Store URL, e.g. 389801252 in apps.apple.com/app/id389801252. Leave empty for a zero-config demo run on a known public app.
## `googlePlayAppIds` (type: `array`):

One or more Google Play package IDs (the part after id= in a Play Store URL, e.g. com.spotify.music). Reviews are pulled from the public Play Store reviews endpoint and normalized into the same feed as the App Store reviews. Leave empty to monitor only the App Store.
## `country` (type: `string`):

Two-letter storefront country code whose reviews to read (e.g. us, gb, de). Applies to both stores. Defaults to us. For multiple storefronts use the countries field instead.
## `countries` (type: `array`):

Two-letter storefront country codes to read reviews from, one feed per country merged into the output (e.g. us, gb, de). Overrides the single country field when set. Leave empty to use country.
## `minRating` (type: `integer`):

Keep only reviews with at least this star rating (1-5). Combine with maxRating to target a band — e.g. minRating 1 and maxRating 2 to monitor only angry reviews. Leave empty for all ratings.
## `maxRating` (type: `integer`):

Keep only reviews with at most this star rating (1-5). Leave empty for all ratings.
## `keywords` (type: `array`):

Keep only reviews whose title or body contains any of these terms (case-insensitive), e.g. crash, refund, login. Leave empty to keep all reviews.
## `maxItems` (type: `integer`):

Upper bound on the number of reviews returned per run (across all apps). The budget is shared round-robin across every requested app and storefront, so each gets coverage. Note: Apple's public feed serves only its most recent reviews per app (~the latest few hundred), so very large maxItems values may return fewer Apple rows. Lower it to keep runs cheap; remove it to fetch all recent reviews Apple serves.
## `maxCostPerRunUsd` (type: `integer`):

Hard ceiling on spend per run — the run stops before exceeding it, so you are never surprised by the bill.
## `fullSync` (type: `boolean`):

By default this runs as an incremental monitor: a repeat run emits only NEW or CHANGED records (and bills only for those). Turn this on to re-emit the entire current dataset every run — the one-off scraper / full-export use case.

## Actor input object example

```json
{
  "appIds": [
    "389801252",
    "310633997"
  ],
  "googlePlayAppIds": [
    "com.spotify.music",
    "com.instagram.android"
  ],
  "country": "us",
  "countries": [
    "us",
    "gb",
    "de"
  ],
  "minRating": 1,
  "maxRating": 2,
  "keywords": [
    "crash",
    "refund",
    "login"
  ],
  "maxItems": 500,
  "maxCostPerRunUsd": 5,
  "fullSync": false
}
````

# Actor output Schema

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

Monitor app reviews in one feed: scrape Apple App Store and Google Play reviews with version tags, a rating-derived sentiment label, and new-review alerts. For product, ASO, and competitor tracking, with a hard cost cap.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "appIds": [],
    "googlePlayAppIds": [],
    "country": "us",
    "countries": [],
    "keywords": [],
    "maxItems": 500,
    "maxCostPerRunUsd": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("hamzatrq/app-review-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "appIds": [],
    "googlePlayAppIds": [],
    "country": "us",
    "countries": [],
    "keywords": [],
    "maxItems": 500,
    "maxCostPerRunUsd": 5,
}

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

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

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

```

## CLI example

```bash
echo '{
  "appIds": [],
  "googlePlayAppIds": [],
  "country": "us",
  "countries": [],
  "keywords": [],
  "maxItems": 500,
  "maxCostPerRunUsd": 5
}' |
apify call hamzatrq/app-review-monitor --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "App Store & Google Play Review Monitor",
        "description": "Monitor app reviews in one feed: scrape Apple App Store reviews with version tags, derived sentiment, and new-review alerts. For product, ASO, and competitor tracking, with a hard cost cap. Google Play planned as a follow-on.",
        "version": "0.0",
        "x-build-id": "F58TRTdhcB8TlebCa"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/hamzatrq~app-review-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-hamzatrq-app-review-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/hamzatrq~app-review-monitor/runs": {
            "post": {
                "operationId": "runs-sync-hamzatrq-app-review-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/hamzatrq~app-review-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-hamzatrq-app-review-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "appIds": {
                        "title": "Apple App Store app IDs",
                        "type": "array",
                        "description": "One or more numeric Apple App Store app IDs to monitor reviews for. The numeric id is the number in an App Store URL, e.g. 389801252 in apps.apple.com/app/id389801252. Leave empty for a zero-config demo run on a known public app.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "googlePlayAppIds": {
                        "title": "Google Play package IDs",
                        "type": "array",
                        "description": "One or more Google Play package IDs (the part after id= in a Play Store URL, e.g. com.spotify.music). Reviews are pulled from the public Play Store reviews endpoint and normalized into the same feed as the App Store reviews. Leave empty to monitor only the App Store.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "country": {
                        "title": "Storefront country",
                        "type": "string",
                        "description": "Two-letter storefront country code whose reviews to read (e.g. us, gb, de). Applies to both stores. Defaults to us. For multiple storefronts use the countries field instead.",
                        "default": "us"
                    },
                    "countries": {
                        "title": "Storefront countries (multi)",
                        "type": "array",
                        "description": "Two-letter storefront country codes to read reviews from, one feed per country merged into the output (e.g. us, gb, de). Overrides the single country field when set. Leave empty to use country.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "minRating": {
                        "title": "Minimum star rating",
                        "type": "integer",
                        "description": "Keep only reviews with at least this star rating (1-5). Combine with maxRating to target a band — e.g. minRating 1 and maxRating 2 to monitor only angry reviews. Leave empty for all ratings."
                    },
                    "maxRating": {
                        "title": "Maximum star rating",
                        "type": "integer",
                        "description": "Keep only reviews with at most this star rating (1-5). Leave empty for all ratings."
                    },
                    "keywords": {
                        "title": "Keyword filter",
                        "type": "array",
                        "description": "Keep only reviews whose title or body contains any of these terms (case-insensitive), e.g. crash, refund, login. Leave empty to keep all reviews.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxItems": {
                        "title": "Max reviews per run",
                        "type": "integer",
                        "description": "Upper bound on the number of reviews returned per run (across all apps). The budget is shared round-robin across every requested app and storefront, so each gets coverage. Note: Apple's public feed serves only its most recent reviews per app (~the latest few hundred), so very large maxItems values may return fewer Apple rows. Lower it to keep runs cheap; remove it to fetch all recent reviews Apple serves.",
                        "default": 500
                    },
                    "maxCostPerRunUsd": {
                        "title": "Max cost per run (USD)",
                        "type": "integer",
                        "description": "Hard ceiling on spend per run — the run stops before exceeding it, so you are never surprised by the bill.",
                        "default": 5
                    },
                    "fullSync": {
                        "title": "Full sync (re-scrape everything)",
                        "type": "boolean",
                        "description": "By default this runs as an incremental monitor: a repeat run emits only NEW or CHANGED records (and bills only for those). Turn this on to re-emit the entire current dataset every run — the one-off scraper / full-export use case.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
