# App Review Radar - Apple App Store & Podcast Review Monitor (`constant_quadruped/app-review-radar`) Actor

Monitor Apple App Store apps & Apple Podcasts shows for new customer reviews, correlate complaints to app versions, tag sentiment themes, and get a 'what changed since last run' delta digest. Pure HTTP on Apple's official zero-auth APIs — no browser, no proxies, cheap and reliable.

- **URL**: https://apify.com/constant\_quadruped/app-review-radar.md
- **Developed by:** [CQ](https://apify.com/constant_quadruped) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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 Radar — Apple App Store & Podcast Review Monitor

**Watch your app (and your competitors) on the Apple App Store and Apple Podcasts — and know exactly what changed since last time.**

App Review Radar is an *unattended review-intelligence monitor*, not a one-off review dumper. Point it at one or many App Store app IDs (and/or Apple Podcasts show IDs), add a few competitor IDs, and each run returns the newest customer reviews enriched with version correlation, sentiment themes, rating deltas, and a "what changed since last run" digest.

It runs entirely on Apple's **official, zero-auth public APIs** (iTunes RSS Customer Reviews, iTunes Lookup, iTunes Search). No headless browser, no residential proxies, no login walls, no Cloudflare/reCAPTCHA. That means it keeps working unattended — and because it's pure HTTP, runs are cheap.

---

### What it does

For every app/show × country you monitor, each run gives you:

1. **Latest customer reviews** — author, star rating, title, content, **the exact app version each review was left on**, vote count/sum, and timestamp.
2. **Per-version breakdown** — average rating + review count + top complaint themes grouped by the version reviews were left on, so you can pin complaints to a release.
3. **Theme breakdown** — crash, login, price, ads, bug, performance, feature requests (fully customizable), counted from **real review text** with sample snippets.
4. **Rating deltas vs last run** — movement in Apple's official `averageUserRating` and `userRatingCount`, plus version-change detection.
5. **Change digest + negative-spike flag** — new reviews, spiking complaint themes, and an `isNegativeSpike` alert when 1-2 star share jumps past your threshold.

State is persisted in the Apify key-value store, so the **`sinceLastRun` delta** works automatically across scheduled runs.

---

### Inputs

| Field | Type | Description |
|---|---|---|
| `appIds` | array | App Store numeric IDs **or** app URLs to monitor |
| `podcastIds` | array | (optional) Apple Podcasts show IDs/URLs — same pipeline |
| `searchTerms` | array | (optional) names resolved to IDs via iTunes Search |
| `competitorIds` | array | (optional) competitors included for side-by-side comparison |
| `countries` | array | 2-letter storefront codes (default `["us"]`) |
| `maxPages` | int | 1–10 pages per app per country (≈50 reviews/page) |
| `minRating` / `maxRating` | int | (optional) star filter, e.g. only 1–2★ for support triage |
| `sinceLastRun` | bool | only emit new reviews/changes since last run (default `true`) |
| `themeKeywords` | object | (optional) custom theme → keyword groups |
| `includeAggregates` | bool | also pull rating/version snapshot via iTunes Lookup (default `true`) |
| `negativeSpikeThreshold` | number | fraction jump in 1–2★ share that flags a spike (default `0.15`) |

---

### Outputs

Each dataset item is one app/show × country, with:

| Field | Description |
|---|---|
| `appId`, `trackName`, `country`, `kind`, `role` | identity + primary/competitor role |
| `sellerName`, `genres` | publisher + categories |
| `averageUserRating`, `userRatingCount` | Apple's official aggregate |
| `currentVersion`, `currentVersionReleaseDate` | latest release info |
| `reviews[]` | each with `rating`, `title`, `content`, **`appVersion`**, votes, `updatedAt` |
| `perVersionBreakdown[]` | avg rating + count + top themes per app version |
| `themeBreakdown[]` | theme → count + sample snippets (from real text) |
| `ratingDelta` | rating/count movement + version change vs last run |
| `newReviewsCount`, `changeDigest` | new + spiking themes since last run |
| `isNegativeSpike` | boolean alert on a negative-sentiment jump |

A table view (**Overview**) summarizes app, rating, version, new reviews and spike flags.

---

### Example use cases

**1. Indie dev — release regression triage.** Schedule daily with `appIds: ["YOUR_APP_ID"]`, `minRating: 1`, `maxRating: 2`. Every morning you get only the new 1–2★ reviews, grouped by the version they landed on — instantly see if your latest release spiked "crash" or "login" complaints.

**2. ASO / app-marketing agency — competitive watch.** `appIds: ["CLIENT_ID"]`, `competitorIds: ["RIVAL_1","RIVAL_2","RIVAL_3"]`, weekly cadence, `countries: ["us","gb","de"]`. Side-by-side rating deltas and theme breakdowns across storefronts for your weekly client report.

**3. Podcast network — audience sentiment monitor.** `podcastIds: ["SHOW_ID_A","SHOW_ID_B"]`, weekly. Track review sentiment and feature requests per show, with a digest of what's newly spiking.

---

### Example input

```json
{
  "appIds": ["389801252", "https://apps.apple.com/us/app/slack/id618783545"],
  "competitorIds": ["310633997"],
  "countries": ["us"],
  "maxPages": 10,
  "sinceLastRun": true,
  "includeAggregates": true
}
````

***

### Why it's reliable & cheap

- **Apple official APIs, zero auth** — no Cloudflare, no reCAPTCHA, no fingerprinting. The same infrastructure that powers `apps.apple.com`.
- **Pure HTTP GETs** — no headless browser, no proxies, minimal compute → low cost per run, high margin on pay-per-result.
- **Built-in throttle + exponential backoff** for Apple's gentle ~20 req/min per-IP limit.
- **No mock data, ever** — every field is derived from the live JSON for the IDs you supply.

***

*Tip: run it on a daily or weekly schedule and let the `sinceLastRun` digest do the watching for you.*

# Actor input Schema

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

Apple App Store numeric IDs (e.g. 389801252) or full app URLs (e.g. https://apps.apple.com/us/app/instagram/id389801252) to monitor.

## `podcastIds` (type: `array`):

Apple Podcasts show IDs or URLs — monitored through the same review pipeline.

## `searchTerms` (type: `array`):

App/podcast names to resolve to IDs automatically via the iTunes Search API when you don't have the numeric ID.

## `competitorIds` (type: `array`):

Competitor apps to include in the same run for side-by-side rating and complaint-theme comparison.

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

2-letter storefront codes. Reviews are per-country, so this controls coverage. Default: us.

## `maxPages` (type: `integer`):

Each page is up to 50 newest reviews; 10 pages ≈ 500 newest reviews.

## `minRating` (type: `integer`):

Only emit reviews with rating >= this value.

## `maxRating` (type: `integer`):

Only emit reviews with rating <= this value. Set min=1,max=2 for support triage of negative reviews.

## `sinceLastRun` (type: `boolean`):

When true, emits only reviews and changes new since the previous run (using the key-value store state). Turn off for a full snapshot every run.

## `themeKeywords` (type: `object`):

Map of theme -> array of keywords to tag in review text. Defaults cover crash/login/price/ads/bug/performance/feature\_request.

## `includeAggregates` (type: `boolean`):

Also pull Apple's official aggregate rating, rating count, current version and release date per app.

## `negativeSpikeThreshold` (type: `number`):

Flag isNegativeSpike when the 1-2 star share jumps by at least this fraction vs last run (0-1).

## Actor input object example

```json
{
  "appIds": [
    "389801252",
    "https://apps.apple.com/us/app/slack/id618783545"
  ],
  "countries": [
    "us"
  ],
  "maxPages": 10,
  "sinceLastRun": true,
  "includeAggregates": true,
  "negativeSpikeThreshold": 0.15
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per app/show + country: track name, ID, role, average rating, rating count, current version, reviews reported, new reviews, and negative-spike flag

# 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": [
        "389801252"
    ],
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("constant_quadruped/app-review-radar").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": ["389801252"],
    "countries": ["us"],
}

# Run the Actor and wait for it to finish
run = client.actor("constant_quadruped/app-review-radar").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": [
    "389801252"
  ],
  "countries": [
    "us"
  ]
}' |
apify call constant_quadruped/app-review-radar --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "App Review Radar - Apple App Store & Podcast Review Monitor",
        "description": "Monitor Apple App Store apps & Apple Podcasts shows for new customer reviews, correlate complaints to app versions, tag sentiment themes, and get a 'what changed since last run' delta digest. Pure HTTP on Apple's official zero-auth APIs — no browser, no proxies, cheap and reliable.",
        "version": "1.0",
        "x-build-id": "vqvpfrB2mwudsU9gR"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/constant_quadruped~app-review-radar/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-constant_quadruped-app-review-radar",
                "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/constant_quadruped~app-review-radar/runs": {
            "post": {
                "operationId": "runs-sync-constant_quadruped-app-review-radar",
                "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/constant_quadruped~app-review-radar/run-sync": {
            "post": {
                "operationId": "run-sync-constant_quadruped-app-review-radar",
                "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": "App Store app IDs or URLs",
                        "type": "array",
                        "description": "Apple App Store numeric IDs (e.g. 389801252) or full app URLs (e.g. https://apps.apple.com/us/app/instagram/id389801252) to monitor.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "podcastIds": {
                        "title": "Apple Podcasts show IDs or URLs (optional)",
                        "type": "array",
                        "description": "Apple Podcasts show IDs or URLs — monitored through the same review pipeline.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "searchTerms": {
                        "title": "Search terms (optional)",
                        "type": "array",
                        "description": "App/podcast names to resolve to IDs automatically via the iTunes Search API when you don't have the numeric ID.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "competitorIds": {
                        "title": "Competitor app IDs/URLs (optional)",
                        "type": "array",
                        "description": "Competitor apps to include in the same run for side-by-side rating and complaint-theme comparison.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "countries": {
                        "title": "Storefront countries",
                        "type": "array",
                        "description": "2-letter storefront codes. Reviews are per-country, so this controls coverage. Default: us.",
                        "default": [
                            "us"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxPages": {
                        "title": "Max review pages per app per country (1-10)",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "Each page is up to 50 newest reviews; 10 pages ≈ 500 newest reviews.",
                        "default": 10
                    },
                    "minRating": {
                        "title": "Minimum star rating filter (optional)",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Only emit reviews with rating >= this value."
                    },
                    "maxRating": {
                        "title": "Maximum star rating filter (optional)",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Only emit reviews with rating <= this value. Set min=1,max=2 for support triage of negative reviews."
                    },
                    "sinceLastRun": {
                        "title": "Only new reviews/changes since last run",
                        "type": "boolean",
                        "description": "When true, emits only reviews and changes new since the previous run (using the key-value store state). Turn off for a full snapshot every run.",
                        "default": true
                    },
                    "themeKeywords": {
                        "title": "Custom theme keyword groups (optional)",
                        "type": "object",
                        "description": "Map of theme -> array of keywords to tag in review text. Defaults cover crash/login/price/ads/bug/performance/feature_request."
                    },
                    "includeAggregates": {
                        "title": "Include rating/version snapshot (iTunes Lookup)",
                        "type": "boolean",
                        "description": "Also pull Apple's official aggregate rating, rating count, current version and release date per app.",
                        "default": true
                    },
                    "negativeSpikeThreshold": {
                        "title": "Negative-spike threshold",
                        "minimum": 0,
                        "maximum": 1,
                        "type": "number",
                        "description": "Flag isNegativeSpike when the 1-2 star share jumps by at least this fraction vs last run (0-1).",
                        "default": 0.15
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
