# App Review Intelligence Scraper (`winterly_fly/app-review-intelligence-scraper`) Actor

Scrape public Apple App Store and Google Play reviews, then generate statistics and DeepSeek AI product intelligence reports.

- **URL**: https://apify.com/winterly\_fly/app-review-intelligence-scraper.md
- **Developed by:** [Winterly Fly](https://apify.com/winterly_fly) (community)
- **Categories:** Developer tools, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.20 / 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.

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 Review Intelligence Scraper

Scrape public Apple App Store and Google Play reviews, normalize the fields, and generate ready-to-read product intelligence reports.

This Actor is built for product, growth, support, and competitive intelligence teams that need more than a raw review export. Every run writes normalized review data plus a Markdown report, HTML report, JSON statistics, JSON AI analysis, and CSV/JSON review exports.

### What It Does

- Scrapes Apple App Store public reviews by App Store URL or numeric Apple App ID.
- Scrapes Google Play public reviews by Google Play URL or package name.
- Supports one app or many apps in the same run.
- Supports country/region, language, max review count, date filters, rating filters, keyword filters, negative-review-only mode, and deduplication.
- Supports recent-review shortcuts, unanswered-review queues, helpful-count filters, issue tagging, and priority scoring for support/operations workflows.
- Captures platform-available metadata such as developer replies, app version, helpful/upvote count, review URL, reviewer name, country, and language.
- Generates deterministic statistics without AI.
- Optionally calls DeepSeek to produce an AI product intelligence report.
- Keeps scraping and base statistics successful when DeepSeek is unavailable or returns an error.
- Supports pay-per-event monetization: one `review` event per output review and one `ai-report` or `competitor-ai-report` event for successful AI analysis.

### Input

The main input field is `apps`.

```json
{
  "apps": [
    {
      "platform": "apple",
      "appId": "310633997",
      "label": "WhatsApp iOS"
    },
    {
      "platform": "google",
      "packageName": "com.duolingo",
      "label": "Duolingo Android"
    }
  ],
  "countries": ["us", "gb", "ca"],
  "language": "en",
  "maxReviewsPerApp": 100,
  "maxRawReviewsPerApp": 500,
  "sinceDays": 30,
  "onlyNegative": false,
  "ratings": [],
  "unansweredOnly": false,
  "minHelpfulCount": 0,
  "keywords": [],
  "deduplicate": true,
  "enableAiAnalysis": true,
  "aiReportLanguage": "English"
}
````

You can also use compatibility fields such as `appUrl`, `appUrls`, `appleAppId`, `appleAppIds`, `googlePlayUrl`, `googlePlayUrls`, `googlePlayPackageName`, and `googlePackageNames`.

`maxRawReviewsPerApp` is optional. If you leave it empty, the Actor fetches roughly the target output count for normal runs and automatically expands to 5x the target when strict filters such as date, rating, keyword, unanswered-only, or helpful-count filters are enabled.

#### DeepSeek API Key

This Actor is designed for platform-provided AI analysis. Configure `DEEPSEEK_API_KEY` as an Apify secret/environment variable on the Actor. The public input schema does not ask users to paste their own DeepSeek key, model, or API base URL. DeepSeek requests default to `https://api.deepseek.com`; owner-only overrides must be set through Actor environment variables.

### Output

Dataset: one normalized review per item.

Key-value store records:

- `REPORT.md`: Markdown report.
- `REPORT.html`: HTML report.
- `analysis.json`: AI status and AI/fallback analysis.
- `statistics.json`: deterministic statistics.
- `reviews.json`: normalized review array.
- `reviews.csv`: normalized review CSV.
- `negative-reviews.json` / `negative-reviews.csv`: negative review subset.
- `unanswered-reviews.json` / `unanswered-reviews.csv`: reviews without captured developer replies.
- `priority-reviews.json` / `priority-reviews.csv`: highest-priority support/operations queue.
- `run-summary.json`: compact run summary.

#### Normalized Review Fields

| Field | Description |
| --- | --- |
| `platform` | `apple` or `google` |
| `appKey` | Stable internal app key, e.g. `apple:310633997` |
| `appName` | Store app name when available |
| `appStoreId` | Apple numeric ID |
| `packageName` | Google Play package name |
| `country` | Country/region code used for the request |
| `language` | Requested language |
| `reviewId` | Platform review ID or stable fallback hash |
| `rating` | 1-5 star score |
| `sentimentBucket` | `negative`, `neutral`, or `positive` |
| `isNegative` | Whether the rating is in `negativeRatings` |
| `title` | Review title when available |
| `text` | Review body |
| `version` | App version when available |
| `date` | ISO review date |
| `dateOnly` | Date-only value |
| `daysSinceReview` | Age of the review in days |
| `developerReplyText` | Developer reply when available |
| `developerReplyDate` | Developer reply date when available |
| `helpfulCount` | Platform helpful/upvote count when available |
| `priorityScore` | 0-100 support/operations priority score |
| `priorityReasons` | Signals behind the priority score |
| `issueCategories` | Rule-based issue categories such as crash, login, payment, ads, performance |
| `criteriaRatings` | Google Play feature criteria ratings when available |
| `reviewUrl` | Platform review URL when available |
| `keywordMatches` | Matched filter keywords |

### AI Report Sections

When `enableAiAnalysis` is true and `DEEPSEEK_API_KEY` is available, the AI report includes:

- Management summary
- Main negative issues
- Main positive points
- Bug/crash/login/payment/ads/performance category analysis
- Version impact
- Priority review queue
- Google Play feature criteria when available
- Country/region differences
- Competitive comparison when multiple apps are provided
- Product improvement recommendations
- Customer support / operations reply priorities
- Action items

If DeepSeek fails, the Actor writes a fallback analysis based on deterministic statistics and marks `analysis.json.status` as `failed` or `missing_api_key`. If no reviews remain after filters, the Actor skips DeepSeek and marks the status as `skipped_no_reviews`.

### Local Development

```bash
npm install
npm test
npm run lint
npm run test:live -- apple-20 google-20
```

For local PPE simulation:

```bash
ACTOR_TEST_PAY_PER_EVENT=true ACTOR_USE_CHARGING_LOG_DATASET=true npm start
```

The live scenario runner writes isolated storage under:

```text
D:\Codex\apify-app-review-intelligence\storage\scenario-runs
```

### Pay-Per-Event Design

Configured launch events:

| Event | Launch price |
| --- | --- |
| `review` | `$0.0002` per output review |
| `ai-report` | `$0.15` per successful single-app AI report |
| `competitor-ai-report` | `$0.35` per successful multi-app competitive AI report |

The Actor calls:

- `Actor.pushData(review, 'review')` for each output review.
- `Actor.charge({ eventName: 'ai-report' })` after a successful single-app AI report.
- `Actor.charge({ eventName: 'competitor-ai-report' })` after a successful multi-app AI report.

These PPE events are configured on the private Actor. The Actor remains private until Store publication is explicitly approved.

### Limitations

- Apple reviews use the current public Apple web reviews API first, with legacy public RSS as a fallback.
- Apple web reviews currently do not expose version, developer reply, or helpful vote fields. Those Apple fields are filled only when the RSS fallback provides them.
- Apple and Google Play public endpoints can change over time. The Actor uses retries, hard timeouts, fallback diagnostics, and a current Google Play review data RPC with legacy fallback.
- Google Play review retrieval is capped at 1000 reviews per app/country in this version.
- No private, authenticated, or paid store data is scraped.
- DeepSeek costs depend on review sample size. Use `aiMaxReviews` to control token usage.
- DeepSeek generation can take longer than store page requests. Use `aiRequestTimeoutMs` if a larger report needs more time.

### Example Inputs

See:

- `examples/apple-20.json`
- `examples/google-20.json`
- `examples/batch-competitive-ai.json`
- `examples/filters-negative-keyword.json`

# Actor input Schema

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

Apps to scrape. Use platform auto/apple/google plus url, appId, or packageName. App Store URLs, Apple numeric IDs, Google Play URLs, and package names are supported.

## `appUrl` (type: `string`):

Optional convenience field for one App Store or Google Play URL. You can use this instead of the Apps JSON field for a single app.

## `appUrls` (type: `array`):

Optional convenience field for multiple App Store or Google Play URLs.

## `appleAppId` (type: `string`):

Optional numeric Apple App Store ID, e.g. 570060128.

## `appleAppIds` (type: `array`):

Optional list of numeric Apple App Store IDs.

## `googlePlayUrl` (type: `string`):

Optional Google Play app URL. You can use this instead of the Apps JSON field for a single Android app.

## `googlePlayUrls` (type: `array`):

Optional list of Google Play app URLs.

## `googlePlayPackageName` (type: `string`):

Optional Android package name, e.g. com.duolingo.

## `googlePackageNames` (type: `array`):

Optional list of Android package names.

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

Two-letter country or region codes. The Actor tries each country for each app.

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

Two-letter language code. Google Play uses it directly. Apple web reviews use it as part of the requested locale when supported.

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

Maximum number of output reviews per app after filters and deduplication.

## `maxRawReviewsPerApp` (type: `integer`):

Maximum raw reviews to fetch before filters. Leave empty for automatic sizing: target count for normal runs, 5x target count for strict filters.

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

Review ordering requested from the platform when supported.

## `startDate` (type: `string`):

Optional inclusive start date in YYYY-MM-DD format.

## `endDate` (type: `string`):

Optional inclusive end date in YYYY-MM-DD format.

## `sinceDays` (type: `integer`):

Optional shortcut for recent reviews. For example, 30 keeps reviews from the last 30 days unless startDate is also set.

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

Optional list of star ratings to keep, e.g. \[1,2]. Empty means all ratings.

## `onlyNegative` (type: `boolean`):

Keep only negative reviews. Defaults to 1-2 star reviews unless negativeRatings is changed.

## `negativeRatings` (type: `array`):

Ratings treated as negative when onlyNegative is enabled and in the statistics.

## `unansweredOnly` (type: `boolean`):

Keep only reviews that do not have a captured developer reply.

## `minHelpfulCount` (type: `integer`):

Keep only reviews with at least this many helpful/upvote counts when the platform provides that field.

## `keywords` (type: `array`):

Optional keyword filters. Reviews are matched against title and text.

## `keywordMode` (type: `string`):

Match any keyword or all keywords.

## `deduplicate` (type: `boolean`):

Remove duplicate review IDs and fallback duplicate text/date/rating combinations.

## `enableAiAnalysis` (type: `boolean`):

Generate the AI product intelligence report. If DeepSeek fails, scraping and basic statistics still succeed.

## `aiReportLanguage` (type: `string`):

Language for AI-generated report sections.

## `reportTopReviewCount` (type: `integer`):

Number of highest-priority reviews to show in the Markdown/HTML report.

## `aiMaxReviews` (type: `integer`):

Caps review samples sent to DeepSeek to control token cost.

## `aiRequestTimeoutMs` (type: `integer`):

Timeout for the DeepSeek analysis request. Increase this if larger reports need more generation time.

## `requestDelayMs` (type: `integer`):

Delay between platform review page requests.

## `requestTimeoutMs` (type: `integer`):

Hard timeout for individual platform requests.

## Actor input object example

```json
{
  "apps": [
    {
      "platform": "apple",
      "appId": "310633997",
      "label": "WhatsApp iOS"
    },
    {
      "platform": "google",
      "packageName": "com.duolingo",
      "label": "Duolingo Android"
    }
  ],
  "appUrls": [],
  "appleAppIds": [],
  "googlePlayUrls": [],
  "googlePackageNames": [],
  "countries": [
    "us"
  ],
  "language": "en",
  "maxReviewsPerApp": 100,
  "sort": "recent",
  "ratings": [],
  "onlyNegative": false,
  "negativeRatings": [
    1,
    2
  ],
  "unansweredOnly": false,
  "minHelpfulCount": 0,
  "keywords": [],
  "keywordMode": "any",
  "deduplicate": true,
  "enableAiAnalysis": true,
  "aiReportLanguage": "English",
  "reportTopReviewCount": 10,
  "aiMaxReviews": 300,
  "aiRequestTimeoutMs": 120000,
  "requestDelayMs": 500,
  "requestTimeoutMs": 30000
}
```

# Actor output Schema

## `htmlReport` (type: `string`):

Ready-to-read product intelligence report with statistics, AI analysis when enabled, priority reviews, issue categories, and recommendations.

## `normalizedReviews` (type: `string`):

Main dataset with one normalized Apple App Store or Google Play review per item. The default overview view highlights app, market, rating, sentiment, priority, issues, text, and links.

## `supportQueue` (type: `string`):

Dataset view focused on high-priority customer support and operations signals.

## `markdownReport` (type: `string`):

Markdown version of the intelligence report for copying into docs, tickets, or AI tools.

## `aiAnalysis` (type: `string`):

Machine-readable AI analysis status and sections. If DeepSeek is unavailable, this contains deterministic fallback analysis and the status reason.

## `statistics` (type: `string`):

Deterministic review statistics, filters, platform/app/country summaries, rating distribution, issue categories, priority reviews, diagnostics, and raw/filtered counts.

## `reviewsCsv` (type: `string`):

CSV export of all normalized output reviews.

## `negativeReviewsCsv` (type: `string`):

CSV export of reviews classified as negative according to the selected negative rating settings.

## `unansweredReviewsCsv` (type: `string`):

CSV export of reviews without a captured developer reply.

## `priorityReviewsCsv` (type: `string`):

CSV export of the highest-priority reviews for support, operations, and product teams.

## `runSummary` (type: `string`):

Compact run summary with total review count, AI status, and generated output record names.

# 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": [
        {
            "platform": "apple",
            "appId": "310633997",
            "label": "WhatsApp iOS"
        },
        {
            "platform": "google",
            "packageName": "com.duolingo",
            "label": "Duolingo Android"
        }
    ],
    "appUrls": [],
    "appleAppIds": [],
    "googlePlayUrls": [],
    "googlePackageNames": [],
    "countries": [
        "us"
    ],
    "ratings": [],
    "negativeRatings": [
        1,
        2
    ],
    "keywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("winterly_fly/app-review-intelligence-scraper").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": [
        {
            "platform": "apple",
            "appId": "310633997",
            "label": "WhatsApp iOS",
        },
        {
            "platform": "google",
            "packageName": "com.duolingo",
            "label": "Duolingo Android",
        },
    ],
    "appUrls": [],
    "appleAppIds": [],
    "googlePlayUrls": [],
    "googlePackageNames": [],
    "countries": ["us"],
    "ratings": [],
    "negativeRatings": [
        1,
        2,
    ],
    "keywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("winterly_fly/app-review-intelligence-scraper").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 '{
  "apps": [
    {
      "platform": "apple",
      "appId": "310633997",
      "label": "WhatsApp iOS"
    },
    {
      "platform": "google",
      "packageName": "com.duolingo",
      "label": "Duolingo Android"
    }
  ],
  "appUrls": [],
  "appleAppIds": [],
  "googlePlayUrls": [],
  "googlePackageNames": [],
  "countries": [
    "us"
  ],
  "ratings": [],
  "negativeRatings": [
    1,
    2
  ],
  "keywords": []
}' |
apify call winterly_fly/app-review-intelligence-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "App Review Intelligence Scraper",
        "description": "Scrape public Apple App Store and Google Play reviews, then generate statistics and DeepSeek AI product intelligence reports.",
        "version": "0.1",
        "x-build-id": "C6jVJPrfY40XZaoxs"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/winterly_fly~app-review-intelligence-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-winterly_fly-app-review-intelligence-scraper",
                "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/winterly_fly~app-review-intelligence-scraper/runs": {
            "post": {
                "operationId": "runs-sync-winterly_fly-app-review-intelligence-scraper",
                "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/winterly_fly~app-review-intelligence-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-winterly_fly-app-review-intelligence-scraper",
                "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": {
                    "apps": {
                        "title": "Apps",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Apps to scrape. Use platform auto/apple/google plus url, appId, or packageName. App Store URLs, Apple numeric IDs, Google Play URLs, and package names are supported."
                    },
                    "appUrl": {
                        "title": "Single App Store or Google Play URL",
                        "maxLength": 2048,
                        "type": "string",
                        "description": "Optional convenience field for one App Store or Google Play URL. You can use this instead of the Apps JSON field for a single app."
                    },
                    "appUrls": {
                        "title": "Multiple App Store or Google Play URLs",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Optional convenience field for multiple App Store or Google Play URLs.",
                        "items": {
                            "type": "string",
                            "maxLength": 2048
                        }
                    },
                    "appleAppId": {
                        "title": "Single Apple App ID",
                        "maxLength": 32,
                        "type": "string",
                        "description": "Optional numeric Apple App Store ID, e.g. 570060128."
                    },
                    "appleAppIds": {
                        "title": "Multiple Apple App IDs",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Optional list of numeric Apple App Store IDs.",
                        "items": {
                            "type": "string",
                            "maxLength": 32
                        }
                    },
                    "googlePlayUrl": {
                        "title": "Single Google Play URL",
                        "maxLength": 2048,
                        "type": "string",
                        "description": "Optional Google Play app URL. You can use this instead of the Apps JSON field for a single Android app."
                    },
                    "googlePlayUrls": {
                        "title": "Multiple Google Play URLs",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Optional list of Google Play app URLs.",
                        "items": {
                            "type": "string",
                            "maxLength": 2048
                        }
                    },
                    "googlePlayPackageName": {
                        "title": "Single Google Play package name",
                        "maxLength": 100,
                        "type": "string",
                        "description": "Optional Android package name, e.g. com.duolingo."
                    },
                    "googlePackageNames": {
                        "title": "Multiple Google Play package names",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Optional list of Android package names.",
                        "items": {
                            "type": "string",
                            "maxLength": 100
                        }
                    },
                    "countries": {
                        "title": "Countries / regions",
                        "maxItems": 25,
                        "type": "array",
                        "description": "Two-letter country or region codes. The Actor tries each country for each app.",
                        "items": {
                            "type": "string",
                            "maxLength": 10
                        }
                    },
                    "language": {
                        "title": "Language",
                        "type": "string",
                        "description": "Two-letter language code. Google Play uses it directly. Apple web reviews use it as part of the requested locale when supported.",
                        "default": "en"
                    },
                    "maxReviewsPerApp": {
                        "title": "Max reviews per app",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Maximum number of output reviews per app after filters and deduplication.",
                        "default": 100
                    },
                    "maxRawReviewsPerApp": {
                        "title": "Max raw reviews per app (optional)",
                        "minimum": 1,
                        "maximum": 20000,
                        "type": "integer",
                        "description": "Maximum raw reviews to fetch before filters. Leave empty for automatic sizing: target count for normal runs, 5x target count for strict filters."
                    },
                    "sort": {
                        "title": "Review sort",
                        "enum": [
                            "recent",
                            "helpful",
                            "rating"
                        ],
                        "type": "string",
                        "description": "Review ordering requested from the platform when supported.",
                        "default": "recent"
                    },
                    "startDate": {
                        "title": "Start date",
                        "type": "string",
                        "description": "Optional inclusive start date in YYYY-MM-DD format."
                    },
                    "endDate": {
                        "title": "End date",
                        "type": "string",
                        "description": "Optional inclusive end date in YYYY-MM-DD format."
                    },
                    "sinceDays": {
                        "title": "Since days",
                        "minimum": 1,
                        "maximum": 3650,
                        "type": "integer",
                        "description": "Optional shortcut for recent reviews. For example, 30 keeps reviews from the last 30 days unless startDate is also set."
                    },
                    "ratings": {
                        "title": "Star ratings",
                        "maxItems": 5,
                        "type": "array",
                        "description": "Optional list of star ratings to keep, e.g. [1,2]. Empty means all ratings.",
                        "items": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 5
                        }
                    },
                    "onlyNegative": {
                        "title": "Only negative reviews",
                        "type": "boolean",
                        "description": "Keep only negative reviews. Defaults to 1-2 star reviews unless negativeRatings is changed.",
                        "default": false
                    },
                    "negativeRatings": {
                        "title": "Negative ratings",
                        "maxItems": 5,
                        "type": "array",
                        "description": "Ratings treated as negative when onlyNegative is enabled and in the statistics.",
                        "items": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 5
                        }
                    },
                    "unansweredOnly": {
                        "title": "Unanswered reviews only",
                        "type": "boolean",
                        "description": "Keep only reviews that do not have a captured developer reply.",
                        "default": false
                    },
                    "minHelpfulCount": {
                        "title": "Minimum helpful count",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Keep only reviews with at least this many helpful/upvote counts when the platform provides that field.",
                        "default": 0
                    },
                    "keywords": {
                        "title": "Keywords",
                        "maxItems": 50,
                        "type": "array",
                        "description": "Optional keyword filters. Reviews are matched against title and text.",
                        "items": {
                            "type": "string",
                            "maxLength": 100
                        }
                    },
                    "keywordMode": {
                        "title": "Keyword mode",
                        "enum": [
                            "any",
                            "all"
                        ],
                        "type": "string",
                        "description": "Match any keyword or all keywords.",
                        "default": "any"
                    },
                    "deduplicate": {
                        "title": "Deduplicate reviews",
                        "type": "boolean",
                        "description": "Remove duplicate review IDs and fallback duplicate text/date/rating combinations.",
                        "default": true
                    },
                    "enableAiAnalysis": {
                        "title": "Enable DeepSeek AI analysis",
                        "type": "boolean",
                        "description": "Generate the AI product intelligence report. If DeepSeek fails, scraping and basic statistics still succeed.",
                        "default": true
                    },
                    "aiReportLanguage": {
                        "title": "AI report language",
                        "type": "string",
                        "description": "Language for AI-generated report sections.",
                        "default": "English"
                    },
                    "reportTopReviewCount": {
                        "title": "Top reviews in report",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Number of highest-priority reviews to show in the Markdown/HTML report.",
                        "default": 10
                    },
                    "aiMaxReviews": {
                        "title": "Max reviews sent to AI",
                        "minimum": 10,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Caps review samples sent to DeepSeek to control token cost.",
                        "default": 300
                    },
                    "aiRequestTimeoutMs": {
                        "title": "AI request timeout in ms",
                        "minimum": 10000,
                        "maximum": 300000,
                        "type": "integer",
                        "description": "Timeout for the DeepSeek analysis request. Increase this if larger reports need more generation time.",
                        "default": 120000
                    },
                    "requestDelayMs": {
                        "title": "Request delay in ms",
                        "minimum": 0,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Delay between platform review page requests.",
                        "default": 500
                    },
                    "requestTimeoutMs": {
                        "title": "Request timeout in ms",
                        "minimum": 5000,
                        "maximum": 120000,
                        "type": "integer",
                        "description": "Hard timeout for individual platform requests.",
                        "default": 30000
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
