# App Review Intelligence - Play & App Store → AI Insights (`yasaslive/app-review-intel`) Actor

Collects public reviews of a mobile app from Google Play and the Apple App Store, then produces an AI report: clustered bugs, clustered feature requests, sentiment by app version, and a release-over-release diff.

- **URL**: https://apify.com/yasaslive/app-review-intel.md
- **Developed by:** [Eonix Pvt Ltd](https://apify.com/yasaslive) (community)
- **Categories:** Agents, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 60.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00005 / actor start

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 web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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 Review Intelligence — Play & App Store → AI Insights

Pull public reviews of any mobile app from **Google Play** and the **Apple App Store**, then turn them into a product-team-ready AI report: **clustered bug complaints**, **clustered feature requests**, **sentiment split by app version**, and a **release-over-release diff** (new issues / resolved issues / regressions).

Built for product managers, mobile developers, ASO/UX researchers, and support leads who want the "what is actually going wrong and what do users want" answer without reading 3,000 reviews by hand.

### How it works

1. **Collect** — newest-first reviews from Google Play (paginated) and/or the App Store (RSS feed), normalized into one shape and pushed to the dataset as `kind: "review"` records. Each store is isolated: if one fails, you still get the other plus a warning — never a dead run.
2. **Analyze** (only when you provide an `openaiApiKey`) — a map-reduce pass:
   - **MAP:** reviews are classified in batches of ~80 per LLM call (type `bug` / `feature_request` / `praise` / `complaint` / `question`, severity 1–3, and short reusable theme labels) using OpenAI Structured Outputs.
   - **REDUCE:** deterministic aggregation into top-10 bug clusters, top-10 feature-request clusters (each with counts, example quotes, affected versions), per-version sentiment, and a diff of the two most recent versions.
3. **Deliver** — `REPORT.md` (executive summary first) and `REPORT.json` in the key-value store, plus a `kind: "report"` dataset record with the JSON and artifact URLs.

Without an OpenAI key the actor stops after step 1 and you get the raw reviews only.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `googlePlayAppId` | string | – | Package name, e.g. `com.spotify.music` (a full Play Store URL also works). **At least one of the two app IDs is required.** |
| `appStoreAppId` | string | – | Numeric ID from the App Store URL, e.g. `324684580` (`id324684580` or the full URL also work). |
| `country` | string | `us` | Two-letter storefront country. Reviews differ per storefront. |
| `maxReviews` | integer | `1000` | Newest-first cap **per store** (1–5000). Note Apple's feed serves at most ~500 (see limits). |
| `sinceDate` | string | – | ISO date, e.g. `2026-06-01`. Collection stops at older reviews — great for release-scoped runs. |
| `openaiApiKey` | string (secret) | – | Enables the AI report. Sent only to `api.openai.com`, never logged or stored. |
| `model` | string | `gpt-4o-mini` | Any Chat Completions model supporting Structured Outputs. |
| `proxyConfiguration` | object | Apify Proxy | Proxy for store requests — recommended on the platform if Google Play rate-limits. |

### Output

#### Dataset — review records (real sample from a local run)

```json
{
    "kind": "review",
    "store": "google-play",
    "reviewId": "e9149ba3-5cc4-481e-a73b-5ffb932aef95",
    "date": "2026-08-12T20:38:02.638Z",
    "rating": 4,
    "text": "Still a lot of work.",
    "version": "6.91.4",
    "helpfulVotes": 0
}
```

```json
{
    "kind": "review",
    "store": "app-store",
    "reviewId": "14419478447",
    "date": "2026-08-12T17:25:24.000Z",
    "rating": 2,
    "text": "i hate this — IT WAS A GREAT DAY AND I RAN OUTTA ENERGY AND I PAID 600 GEMS FOR A RESTARTED LESSON FAH",
    "version": "7.135.0",
    "helpfulVotes": null
}
```

Both records above are copy-pasted from a real run against Duolingo (`com.duolingo` + `570060128`), which collected **799 reviews** with the default sample input. App Store titles are merged into `text` (`"title — body"`); `helpfulVotes` is Google Play only (Apple's feed has no equivalent).

#### Key-value store — `REPORT.json` (abridged) and `REPORT.md`

The sample below was produced by this actor's actual report pipeline from a small demonstration fixture (a fictional app), because a real AI run needs your OpenAI key — the structure is exactly what real runs produce:

```json
{
    "generatedAt": "2026-08-14T00:00:00.000Z",
    "app": { "googlePlayAppId": "com.example.app", "appStoreAppId": "123456789", "country": "us" },
    "model": "gpt-4o-mini",
    "coverage": {
        "totalReviews": 11,
        "byStore": { "googlePlay": 10, "appStore": 1 },
        "classifiedReviews": 11,
        "dateRange": { "from": "2026-06-01T00:00:00.000Z", "to": "2026-08-10T00:00:00.000Z" },
        "overallAvgRating": 2.91
    },
    "sentimentByVersion": [
        {
            "version": "2.1.0",
            "reviewCount": 4,
            "classifiedCount": 4,
            "avgRating": 2.25,
            "typeBreakdown": { "bug": 3, "feature_request": 0, "praise": 1, "complaint": 0, "question": 0 },
            "topThemes": [ { "title": "Crash on start", "count": 2 }, { "title": "Login failure", "count": 1 } ]
        }
    ],
    "bugClusters": [
        {
            "title": "Crash on start",
            "count": 2,
            "share": 0.18,
            "avgSeverity": 3,
            "exampleQuotes": [ "App crashes on start every single time", "Crash — crashes when I open it" ],
            "affectedVersions": [ "2.1.0" ]
        }
    ],
    "featureRequests": [
        {
            "title": "Dark mode request",
            "count": 2,
            "share": 0.18,
            "avgSeverity": 1,
            "exampleQuotes": [ "Please add dark mode", "dark mode please!" ],
            "affectedVersions": [ "2.0.0", "1.9.0" ]
        }
    ],
    "releaseDiff": {
        "latestVersion": "2.1.0",
        "previousVersion": "2.0.0",
        "ratingDelta": -1.08,
        "newIssues": [ { "title": "Crash on start", "count": 2 } ],
        "resolvedIssues": [ { "title": "Sync error", "count": 1 } ],
        "regressions": [ { "title": "Login failure", "count": 1 } ]
    },
    "warnings": []
}
```

And the matching `REPORT.md` opens like this (same pipeline, same fixture):

> ## Executive summary
>
> Analyzed **11 reviews** of Google Play "com.example.app" and App Store 123456789 (country "us") — 10 from Google Play, 1 from the App Store, spanning 2026-06-01 to 2026-08-10. Overall average rating: **2.91★**. The dominant bug cluster is **Crash on start** (2 reviews). The most requested feature is **Dark mode request** (2 reviews). Version **2.1.0** vs **2.0.0**: average rating down 1.08★, 1 new issue(s), 1 resolved, 1 regression(s).

### Use cases

1. **Release triage.** You shipped 8.3.0 on Monday. Run with `sinceDate` = release day: the release diff tells you which bug themes are new, which shipped fixes actually landed ("resolved"), and which old bugs came back ("regressions") — before the support queue does.
2. **Roadmap prioritization with receipts.** "Dark mode, 214 requests this quarter, here are three verbatim quotes" lands better in a planning meeting than a hunch. Feature-request clusters give you the counts and the quotes.
3. **Competitor teardown.** Point it at a competitor's app IDs. Their top bug clusters are your reliability pitch; their top feature requests are your roadmap ideas.

### Pricing (pay-per-event)

| Event name | Event title | Description | Charged when | Suggested price |
|---|---|---|---|---|
| `review-scraped` | Review collected | One charge per normalized review — no rounding up to blocks, so 1,001 reviews never costs double 1,000. | After the reviews are pushed to the dataset | **$0.0005** ($0.50 per 1,000) |
| `ai-report` | AI insight report | The full analysis: bug clusters, feature-request clusters, sentiment by version, release diff. Never charged on runs without an `openaiApiKey`. | Once, after `REPORT.md` + `REPORT.json` are persisted to the key-value store | **$2.00** |

Console setup (Actor → Publication → Monetization → pay-per-event) — the same two events in the API's `actorChargeEvents` shape:

```json
{
    "review-scraped": {
        "eventTitle": "Review collected",
        "eventDescription": "Charged once per normalized review, only after the reviews are pushed to the dataset. Failed collection is never charged.",
        "eventPriceUsd": 0.0005
    },
    "ai-report": {
        "eventTitle": "AI insight report",
        "eventDescription": "Charged once per run, only after REPORT.md and REPORT.json are persisted to the key-value store. Runs without an OpenAI API key never charge this event.",
        "eventPriceUsd": 2.00
    }
}
```

You are **never charged for failed work**: reviews are charged only after they are in the dataset, the report only after the artifact exists. Example: 2,300 reviews + AI report = (2,300 × $0.0005) + $2.00 = **$3.15**. Platform usage (compute, proxy) is included — you pay only the events above.

| Typical run | Cost |
|---|---|
| 400 reviews, no AI report | $0.20 |
| 1,000 reviews + AI report | $2.50 |
| 5,000 reviews, no AI report | $2.50 |
| 5,000 reviews + AI report | $4.50 |

**OpenAI cost (your own key):** analysis runs on your `openaiApiKey`, roughly **$0.03–$0.07 per 1,000 reviews** with `gpt-4o-mini` (≈13 calls per 1,000 reviews; review text is truncated to 600 chars per review). LLM calls are hard-capped at 75 per run, so worst-case spend stays bounded.

### Honest limits

- **App Store: ~500 recent reviews max.** Apple's public RSS feed serves at most 10 pages × 50 reviews per storefront. There is no supported way around this — actors claiming otherwise are scraping something else. Google Play has no such cap (up to `maxReviews`, max 5,000).
- **Apple throttles that feed per IP, so cloud runs get less than local runs.** A throttled request returns an empty page rather than an error, which a naive scraper reads as "no more reviews" and silently under-delivers. This actor backs off and re-asks before accepting the end of the feed. Measured on the Apify platform: ~50 reviews without the backoff, 200+ with it; runs repeated back-to-back against the same app get throttled harder, so expect **roughly 50–300 App Store reviews per cloud run** rather than the full ~500. When a run does stop short, it says so in the run log and in the report's `warnings` — the counts are never quietly short. Google Play showed no such throttling (600 reviews in 4 pages from the platform).
- **Unofficial APIs.** Both collectors use community scrapers ([google-play-scraper](https://github.com/facundoolano/google-play-scraper), pinned 10.1.3; app-store-scraper, pinned 0.18.0) against endpoints Google/Apple can change at any time. Requests are retried with backoff; if a store breaks anyway, the run degrades to partial results with a clear "collection degraded — open an issue" warning instead of dying.
- **Version data is imperfect.** Some reviews carry no app version (grouped as `"unknown"`, excluded from the release diff). Low per-version counts make the diff directional, not statistical — the report says so.
- The actor calls two fixed public endpoints politely (sequential pagination, delays, retries with backoff, per-run caps); it does not crawl arbitrary sites.

### Use via MCP

This actor is a natural tool for AI agents: give an agent an app ID, get back structured complaints and requests. Apify exposes any actor as an MCP tool — point your MCP client at [Apify's MCP server](https://mcp.apify.com) and allow this actor:

```json
{
    "mcpServers": {
        "apify": {
            "url": "https://mcp.apify.com/?tools=<your-username>/app-review-intel"
        }
    }
}
```

The input schema above becomes the tool's parameters automatically — no extra code. See the [Apify MCP docs](https://docs.apify.com/platform/integrations/mcp) for client setup (Claude, Cursor, etc.).

### Run locally

```bash
npm install
npm test        # unit tests (validation, map with mocked LLM, reduce fixtures)
apify run       # uses storage/key_value_stores/default/INPUT.json (Duolingo sample)
```

### FAQ

**Do I need an OpenAI key?**
No — without one you get the full normalized review dataset (and only pay `review-scraped`). The key unlocks the report.

**Is my OpenAI key safe?**
It is a secret input (encrypted by Apify, masked in the UI), sent only to `api.openai.com`, and never logged, stored, or pushed to the dataset.

**Can I use a model other than gpt-4o-mini?**
Yes — any OpenAI Chat Completions model that supports Structured Outputs. If a model rejects the request, the run fails fast with the exact reason instead of burning your budget.

**Why did I get fewer reviews than the app has?**
Reviews are per-storefront (`country`), Apple caps at ~500 recent, `sinceDate` cuts older reviews, and collection is newest-first up to `maxReviews` per store.

**What happens when one store fails or an app ID is wrong?**
A wrong app ID fails fast with an actionable message. If one of two configured stores breaks mid-run, you get the other store's reviews plus a warning (and you're only charged for what was delivered). Only when every configured store fails does the run fail.

**What if Google changes their internals?**
The scraper version is pinned and every request retries with backoff. If Google changes the private endpoint anyway, the run reports "collection degraded — please open an issue on this actor" so you know it's the API, not your input.

**Am I charged if the AI step fails?**
No. `ai-report` is charged only after `REPORT.md` exists. If your key is invalid, the run fails with a clear message — you keep (and pay for) only the collected reviews.

**Is scraping reviews allowed?**
The actor reads the same public review data anyone sees in a browser, politely and rate-limited, from two fixed endpoints. You are responsible for complying with the stores' terms and your local regulations for your use case.

### Changelog

#### 0.1.0 (2026-08-14)

- Initial release: Google Play + App Store collection, normalized dataset, PPE monetization, OpenAI map-reduce analysis, `REPORT.md`/`REPORT.json` artifacts, release diff.

# Actor input Schema

## `googlePlayAppId` (type: `string`):

Package name from the Play Store URL, e.g. <code>com.spotify.music</code>. A full Play Store URL is also accepted. At least one of the two app IDs is required.

## `appStoreAppId` (type: `string`):

Numeric ID from the App Store URL, e.g. <code>324684580</code> (the digits after <code>/id</code>). <code>id324684580</code> or a full App Store URL are also accepted. At least one of the two app IDs is required.

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

Two-letter store country code — reviews differ per storefront.

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

Newest-first cap applied to each store separately. Note: Apple's RSS feed serves at most ~500 recent reviews regardless of this value.

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

ISO date (e.g. <code>2026-06-01</code>). Collection stops once older reviews are reached — faster and cheaper runs.

## `openaiApiKey` (type: `string`):

Enables the AI analysis (clusters, sentiment by version, release diff). Without it the actor stops after collecting raw reviews. Sent only to api.openai.com, never logged or stored.

## `model` (type: `string`):

Any Chat Completions model that supports Structured Outputs. The default keeps analysis of 1,000 reviews at roughly $0.03–$0.07 of OpenAI usage.

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

Proxies used for store requests. Recommended on the Apify platform if Google Play starts rate-limiting datacenter IPs.

## Actor input object example

```json
{
  "googlePlayAppId": "com.duolingo",
  "appStoreAppId": "570060128",
  "country": "us",
  "maxReviews": 200,
  "sinceDate": "2026-06-01",
  "model": "gpt-4o-mini",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All collected reviews as kind:"review" records, newest first, plus one kind:"report" record when AI analysis ran.

## `reportMarkdown` (type: `string`):

Executive-summary-first insight report. Present only when an openaiApiKey was provided.

## `reportJson` (type: `string`):

Machine-readable bug clusters, feature-request clusters, sentiment by version, and release diff.

# 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 = {
    "googlePlayAppId": "com.duolingo",
    "appStoreAppId": "570060128",
    "country": "us",
    "maxReviews": 200,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yasaslive/app-review-intel").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 = {
    "googlePlayAppId": "com.duolingo",
    "appStoreAppId": "570060128",
    "country": "us",
    "maxReviews": 200,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yasaslive/app-review-intel").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 '{
  "googlePlayAppId": "com.duolingo",
  "appStoreAppId": "570060128",
  "country": "us",
  "maxReviews": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yasaslive/app-review-intel --silent --output-dataset

```

## MCP server setup

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

```

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/kaapJjKfDkTuN1Okb/builds/sd3KSqOqqKVfSg5sr/openapi.json
