# App Store Reviews & Charts Monitor (`bigdavidson/app-store-reviews-monitor`) Actor

Apple App Store reviews, app metadata and top-chart ranks by app id, URL or search term, across storefronts, with new-since-last-run review tracking. Official public Apple endpoints only.

- **URL**: https://apify.com/bigdavidson/app-store-reviews-monitor.md
- **Developed by:** [Jack Sheward](https://apify.com/bigdavidson) (community)
- **Categories:** Marketing, SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## App Store Reviews & Charts Monitor

Monitor any **Apple App Store** app across any App Store countries. You get recent **customer reviews** (star rating, title, text, app version, date), **app details and rating**, and **top-chart ranks**. Give it app ids, App Store links, bundle ids or plain app names.

It is built for monitoring. On a schedule it can return **only the reviews that are new since your last run**. If a run stops early, the next run picks up the reviews it skipped. Each app record also includes a **rating breakdown per app version**, so you can see whether the latest release made ratings worse. **Chart ranks follow the App Store's own Apps charts**: Top Free and Top Paid exclude games, as the App Store does. Every record is flat JSON with a fixed set of fields, so AI agents, Zapier, Make, n8n or a spreadsheet can use it directly.

It uses only Apple's public, no-login endpoints: the iTunes Search/Lookup API and Apple's RSS chart and review feeds. There are no browsers, no scraped tokens and no accounts.

**Who it's for:**

- App developers and product teams watching their own reviews and competitors'.
- ASO and marketing people tracking chart ranks, screenshots and listing changes.
- Support teams that want 1–2★ reviews sent to Slack or a helpdesk.
- AI agents that need current App Store data.

### What you get

Each dataset row has a `record_type`:

| `record_type` | One row per | Charged event |
|---|---|---|
| `app` | app × storefront: details, rating, and an aggregate of the reviews scanned | `app-record` |
| `review` | customer review | `review` |
| `chart` | chart position, or one row per tracked app with `chartTrackedAppsOnly` | `chart-row` |
| `notice` | explanation of something skipped: bad id, unknown country, empty feed… | **free** |

All ids (`app_id`, `developer_id`, `review_id`, `genre_id`, `category_id`) are strings.

#### Review fields

- `app_id`, `app_name`, `country`, `review_id`
- `rating` (1–5), `title`, `text`
- `author`: the reviewer's public App Store nickname
- `app_version`, `date` (UTC ISO), `vote_sum`, `vote_count`, `fetched_at`
- `is_new`: `true` when no earlier monitoring run with the same `stateKey` returned this review (see the FAQ)

#### App fields

- **Identity:** `app_id`, `country`, `name`, `developer`, `developer_id`, `seller_name`, `bundle_id`.
- **Price and category:** `price`, `currency`, `formatted_price`, `primary_genre`, `genres`, `content_rating`, `advisories`. Genre names are in English in every storefront. A genre Apple adds later keeps Apple's own label until the actor knows it.
- **Versions:** `version`, `current_version_release_date`, `release_date`.
- **Rating:** `rating_avg` and `rating_count` (lifetime, star-only ratings included). `rating_avg` is `null` when the app has no ratings. Both are `null` for Mac apps, because Apple's Lookup API does not report their ratings. Use `reviews_avg_rating` for Mac apps.
- **Size and compatibility:** `file_size_mb` (decimal MB, as the App Store shows it), `minimum_os_version`, `languages`.
- **Links and media:** `app_url`, `icon_url`, `screenshot_urls`, `ipad_screenshot_urls` (up to 10 each), `developer_url`.
- **Text:** `release_notes`, `description`, both complete (the App Store's limit is 4,000 characters).
- `matched_search_term`: the search term that found the app in this storefront. It is `null` for apps given as ids, URLs or bundle ids.

When reviews are fetched in the same run, the record also has:

- `reviews_scanned`, `reviews_new`, `reviews_avg_rating`, `reviews_histogram`
- `reviews_oldest`, `reviews_newest`
- `version_ratings`: reviews and average stars per app version, newest version first

These cover **every review the run scanned** for that app and storefront, from the newest down to where it stopped. The star filter and `onlyNewReviews` then choose which of them are returned as rows. So with `ratings: ["negative"]` you get only 1–2★ rows, but `reviews_scanned`, the histogram and `version_ratings` still describe all the reviews scanned. The fields are `null` when reviews are not fetched.

#### Chart fields

- `chart`, `country`.
- `genre_id` and `genre_name`: what the chart ranks.
  - `All apps (games excluded)` for overall free and paid charts.
  - `All apps and games` for grossing.
  - `All Mac apps` for Mac.
  - The genre name for genre charts.
- `rank`: the position in Apple's chart feed (see the FAQ on app bundles). With `chartTrackedAppsOnly`, `null` means the app is not in the first `chart_depth` positions.
- `chart_depth`: how many positions were scanned.
- `app_id`, `app_name`, `developer`, `bundle_id`, `category` (the app's genre in English) and `category_id`, `price`, `currency`, `release_date`, `app_url`, `icon_url`, `is_tracked_app`.
- `source`: which Apple feed answered.
- `fetched_at`.

#### Real output (runs of 2026-09-25)

App record (app name `pinterest`, US storefront, 500 reviews fetched; the screenshot and language lists are shortened here):

```json
{
  "record_type": "app",
  "app_id": "429047995",
  "country": "us",
  "name": "Pinterest",
  "developer": "Pinterest, Inc.",
  "developer_id": "328135727",
  "seller_name": "Pinterest, Inc.",
  "bundle_id": "pinterest",
  "price": 0.0,
  "currency": "USD",
  "formatted_price": "Free",
  "primary_genre": "Lifestyle",
  "genres": ["Lifestyle", "Photo & Video"],
  "content_rating": "12+",
  "advisories": ["Infrequent/Mild Medical/Treatment Information", "Infrequent/Mild Cartoon or Fantasy Violence", "Infrequent/Mild Sexual Content and Nudity", "Infrequent/Mild Alcohol, Tobacco, or Drug Use or References", "Infrequent/Mild Profanity or Crude Humor", "Infrequent/Mild Horror/Fear Themes", "Infrequent/Mild Mature/Suggestive Themes"],
  "version": "14.36",
  "current_version_release_date": "2026-09-16T15:11:28Z",
  "release_date": "2011-04-28T15:51:13Z",
  "rating_avg": 4.778,
  "rating_count": 5776573,
  "file_size_mb": 425.3,
  "minimum_os_version": "16.0",
  "languages": ["AF", "AR", "BN", "BG", "HR", "CS", "DA", "NL", "EN", "FI", "FR", "DE", "…"],
  "app_url": "https://apps.apple.com/us/app/pinterest/id429047995",
  "icon_url": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/7d/d9/a2/7dd9a2dc-88a8-9ced-4a33-f06f9d0091bd/AppIcon-0-0-1x_U007epad-0-1-0-0-0-85-220.png/512x512bb.jpg",
  "screenshot_urls": ["https://is1-ssl.mzstatic.com/image/thumb/PurpleSource211/v4/07/43/95/074395db-1ab5-4adf-ec05-037f91216984/iPhone-5.5_1.png/392x696bb.png", "…"],
  "ipad_screenshot_urls": ["https://is1-ssl.mzstatic.com/image/thumb/PurpleSource211/v4/2e/d2/90/2ed29067-5da9-5761-5977-9ef54cfb4955/iPad-12.9_1.png/576x768bb.png", "…"],
  "developer_url": "https://apps.apple.com/us/developer/pinterest-inc/id328135727",
  "release_notes": "Every week, we polish up the Pinterest app. This update includes:\n- Bug fixes\n- Performance improvements\n\nTell us if you like this latest version at https://help.pinterest.com/contact",
  "description": "Pinterest is a place of endless possibilities. You can:\n- Discover everyday inspiration\n- Shop styles you love\n- Try and learn something new\n\nCreate boards, save Pins and make collages of all your inspiration. Unlock billions of ideas, from fashion tips and easy recipes to DIY projects and fresh ways to redo your space. Creating the life you love? \n\nIt's Possible.",
  "matched_search_term": "pinterest",
  "reviews_scanned": 500,
  "reviews_new": 500,
  "reviews_avg_rating": 2.36,
  "reviews_histogram": {"1": 231, "2": 84, "3": 53, "4": 37, "5": 95},
  "reviews_oldest": "2026-09-11T22:35:07Z",
  "reviews_newest": "2026-09-23T23:02:55Z",
  "version_ratings": [
    {"version": "14.36", "reviews": 297, "avg_rating": 2.47},
    {"version": "14.35", "reviews": 203, "avg_rating": 2.2}
  ],
  "fetched_at": "2026-09-25T02:15:11Z"
}
```

In this example the lifetime rating is 4.78★, but the last 500 written reviews average 2.36★. The aggregate fields exist to surface gaps like this.

Review:

```json
{
  "record_type": "review",
  "app_id": "429047995",
  "app_name": "Pinterest",
  "country": "us",
  "review_id": "14585540524",
  "rating": 1,
  "title": "fix your app!!!!",
  "text": "its good but why cant i see my pins from my main account on my alt account?? fix your app people cant even see my pins! your updating anything but your own app",
  "author": "maxine 🌸",
  "app_version": "14.36",
  "date": "2026-09-23T22:00:05Z",
  "vote_sum": 0,
  "vote_count": 0,
  "is_new": true,
  "fetched_at": "2026-09-25T02:15:11Z"
}
```

Chart row. Rank tracking for RadarScope (`288419283`) in US Top Paid Apps:

```json
{
  "record_type": "chart",
  "chart": "top-paid",
  "country": "us",
  "genre_id": null,
  "genre_name": "All apps (games excluded)",
  "rank": 5,
  "chart_depth": 100,
  "app_id": "288419283",
  "app_name": "RadarScope",
  "developer": "Base Velocity, LLC",
  "bundle_id": "com.basevelocity.RadarScope",
  "category": "Weather",
  "category_id": "6001",
  "price": 9.99,
  "currency": "USD",
  "release_date": "2019-02-12",
  "app_url": "https://apps.apple.com/us/app/radarscope/id288419283",
  "icon_url": "https://is1-ssl.mzstatic.com/image/thumb/Purple221/v4/df/7f/31/df7f31db-6974-a1fb-748d-f31b4d3c8f7e/App_Icon-0-0-1x_U007emarketing-0-0-0-8-0-0-85-220.png/100x100bb.png",
  "is_tracked_app": true,
  "source": "marketingtools-rss",
  "fetched_at": "2026-09-25T02:15:23Z"
}
```

The dataset has four table views: **Reviews**, **Apps**, **Charts** and **Notices**. You can export as JSON, CSV or Excel.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `apps` | list | ChatGPT (`6448311069`), only when `apps`, `searchTerms` and every chart option are empty | App Store ids, App Store URLs, bundle ids (`com.burbn.instagram`) or app names. Ids, URLs and bundle ids name one exact app. App names are searched for in each storefront (see the FAQ). Up to 100. |
| `searchTerms` | list | — | Names or keywords to search for. Each is searched in every storefront, and that storefront's top result(s) are monitored there. Up to 20. |
| `countries` | list | `us` | Storefront codes (`us`, `gb`, `de`, `jp`…) or country names. With no countries set, the country in an App Store URL is used. Max 50. |
| `modes` | list | see below | Any of `metadata`, `reviews`, `charts`. |
| `maxReviewsPerApp` | int | 100 | Maximum reviews per app per storefront, newest first. Max 500, which is Apple's limit. On a monitor's first run it also sets how far back the monitor starts. |
| `onlyNewReviews` | bool | false | Return, and charge for, only reviews that earlier monitoring runs have not returned. |
| `ratings` | list | all | Star filter, e.g. `["1","2"]`, `["negative"]` (1–2), `["positive"]` (4–5), `["1-3"]`. |
| `chartTypes` | list | `top-free` | `top-free`, `top-paid`, `top-grossing`, `top-free-ipad`, `top-paid-ipad`, `top-grossing-ipad`, `top-free-mac`. A name that mentions games, such as `top free games`, gives that chart in the Games genre. |
| `chartGenres` | list | overall | Genre names or ids: `Games`, `Productivity`, `Finance`, `Puzzle games`, `6014`… |
| `chartLimit` | int | 25 | Rows per chart (1–100). |
| `chartTrackedAppsOnly` | bool | false | Rank tracking. Returns one row per app, chart and storefront instead of the whole chart. |
| `searchResultsPerTerm` | int | 1 | How many search results to monitor per term and storefront (max 10). |
| `maxRecords` | int | — | Hard cap on charged records per run. |
| `stateKey` | string | `default` | Name of a monitor. It keeps separate "new review" state for separate monitors of the same app. |

**Which modes run.** Without `modes`, the actor fetches metadata and reviews. If you set any chart option (`chartTypes`, `chartGenres` or `chartTrackedAppsOnly`) and leave `modes` empty, it fetches only charts. The apps you list then only mark their chart rows (`is_tracked_app`), or are the apps ranked with `chartTrackedAppsOnly`. They are not charged as app records. If you set `modes` and a chart option, charts are added to your modes, and a free notice row says so. In the Console form, **Apps** is prefilled with ChatGPT and **What to fetch** is empty, so adding a chart there fetches charts only.

Input is lenient:

- Values are case-insensitive and trimmed.
- A list entry can hold several comma-separated values (`"us, gb"`).
- Anything the actor cannot use is skipped and explained in a free `notice` row, and the run does not fail because of it. Up to 20 such notices are listed per field; the rest are counted in one more notice.
- Unusable apps, search terms, chart types and genres are never swapped for something else. If you name apps, search terms or charts and none of them can be used (a URL without an id, a Google Play link, an unknown chart or genre), the run returns only notice rows and charges nothing. The example app ChatGPT is used only when the input names no apps, search terms or chart options at all. Two settings do fall back, with a notice: an unusable `countries` list becomes `us` (or the storefront in an App Store URL), and unusable `modes` become the default.
- App names and search terms return whatever App Store search returns in each storefront. The FAQ explains how the actor guards against Apple's stand-in results.

Apify checks the fields above against the input schema before the actor starts. A list field given as a single string is rejected: `"ratings": "negative"` fails, so send `["negative"]`. Apify does not check unknown keys, so the actor's aliases work with either a list or a single string: `appIds`, `country`, `maxReviews`, `onlyNew`, `stars`, `genre` and more.

Examples:

```json
{ "apps": ["https://apps.apple.com/us/app/duolingo-language-lessons/id570060128", "com.burbn.instagram", "Notion"],
  "countries": ["us", "gb", "de"], "maxReviewsPerApp": 500 }
```

```json
{ "apps": ["6448311069", "6473753684"], "searchTerms": ["google gemini"], "modes": ["reviews"],
  "ratings": ["negative"], "onlyNewReviews": true, "stateKey": "slack-bad-reviews", "countries": ["us", "ca", "au"] }
```

```json
{ "apps": ["Notion", "Todoist"], "chartGenres": ["Productivity"], "chartTrackedAppsOnly": true, "countries": ["us", "gb"] }
```

```json
{ "chartTypes": ["top-free", "top-grossing"], "chartGenres": ["all", "Games"], "countries": ["us", "jp"], "chartLimit": 100 }
```

### Pricing

Pay per event. There is no subscription or rental fee, and platform usage is included.

| Event | Price | When |
|---|---|---|
| `actor-start` | $0.005 | once per run, charged with the run's first charged record |
| `app-record` | $0.002 | per app per storefront (metadata mode) |
| `review` | $0.0004 | per review returned |
| `chart-row` | $0.0005 | per chart row returned |
| `notice` rows | free | skipped input and upstream problems |

Worked examples:

| Run | Records | Cost |
|---|---|---|
| Default (ChatGPT, US, 100 reviews) | 1 app + 100 reviews | $0.005 + $0.002 + $0.04 = **$0.047** |
| Deep pull: 3 apps × 3 countries × 500 reviews | 9 apps + 4,500 reviews | $0.005 + $0.018 + $1.80 = **$1.82** |
| Daily monitor: 5 apps × 3 countries, `onlyNewReviews`, ~20 new reviews each | 15 apps + ~300 reviews | $0.005 + $0.03 + $0.12 ≈ **$0.16/day** |
| Top 100 free + grossing charts in US + GB | up to 400 chart rows | $0.005 + $0.20 = **$0.205** |
| Rank tracking: 3 apps, 2 charts, 3 countries (no `modes`) | 18 chart rows | $0.005 + $0.009 = **$0.014** |

A run that returns no charged records costs nothing, not even the start fee. That covers a monitor run with no new reviews (in `reviews`-only mode), app ids Apple does not know, and input where nothing could be used.

You can set a maximum charge per run in the run options, or use `maxRecords`. The actor stops cleanly at that limit. It keeps everything it has already fetched and ends as SUCCEEDED. If the maximum charge cannot cover the $0.005 start fee plus one record ($0.0054 for a review), the actor fetches nothing and charges nothing.

### FAQ

#### How many reviews can I get?

Apple's public review feed returns the **500 most recent written reviews per app per country**, as 10 pages of 50. To go further back, add more countries. To keep a complete history from now on, schedule the actor with `onlyNewReviews` and add each run's rows to your own table. Very busy apps (ChatGPT, Instagram) collect 500 reviews in 1–5 days, so reviews beyond Apple's 500 are out of reach even with a daily schedule.

#### How does "new since last run" work?

A monitoring run is a run with `onlyNewReviews: true` or with a `stateKey`. Monitoring runs save the ids of the reviews they returned in a named key-value store in **your** Apify account, called `app-store-reviews-monitor-state`. The ids are stored per app, storefront and `stateKey`. Other runs only read this state, so an ad-hoc run never uses up a scheduled monitor's new reviews.

**Where a monitor starts.** The first monitoring run for an app and storefront looks at the newest `maxReviewsPerApp` reviews that pass the star filter. That window sets where the monitor starts: it never returns reviews older than the oldest review in the window. To start further back, give the first run a larger `maxReviewsPerApp` (up to 500).

A review is new (`is_new: true`) when no earlier monitoring run returned it and it is not older than where the monitor starts. On a monitor's first run every review is new. Apple sometimes publishes a review a day or more after its timestamp, so the check allows 3 days for that.

**Runs that stop early.** With `onlyNewReviews: true`, only new reviews are returned and charged. Paging stops once the feed reaches reviews the monitor already covered. If a run stops before it has returned everything, the next run returns the reviews it skipped, as long as Apple still lists them among its 500 most recent. This includes the first run: if your maximum charge stops it after 50 of its 500 reviews, the next run returns the other 450. A run can stop early because of `maxRecords`, your maximum charge, a timeout or an Apple error. After the first run, `maxReviewsPerApp` can stop it early too. If a timeout or an Apple error stops the first run before it has looked at its whole window, the next run still counts as the first.

Reviews removed by the star filter count as handled. Give each differently filtered monitor its own `stateKey`.

#### How are app names, search terms and bundle ids matched?

- An **App Store id or URL** names one app. It is fetched in every storefront you list, or only in the URL's storefront when you set no `countries`. Where the app is not sold, you get a free notice.
- A **bundle id** is looked up in your first storefront and, if Apple does not find it there, in the next ones. The app it names is then fetched like an id.
- An **app name or search term** is searched in each storefront separately, and that storefront's top result is fetched there. `matched_search_term` shows which term found it. Apple's search is fuzzy and app names differ by country, so the results can differ between storefronts. The actor applies two checks:
  - Sometimes a storefront does not sell the app, and its top result is some other app. For example, searching `Venmo` in `gb` returns PayPal, while `us` returns Venmo. When the term appears in the top result's name in other storefronts but not in this one, nothing is fetched there, and a free notice explains why.
  - When the storefronts' top results are different apps that all contain the term, each storefront keeps its own result, and a free info notice lists them.
- With only one storefront, or when no storefront's top result contains the term, there is nothing to compare, so Apple's top result is used. With `searchResultsPerTerm` above 1, the checks are off and every storefront keeps all its results. Use ids or URLs when you need one exact app.
- Search is slow. Apple allows about 20 searches a minute, so each term takes about 3 s per storefront. After 200 searches (for example 20 terms × 10 storefronts), the remaining storefronts reuse the app each term found, and a free notice says so.

#### How fresh are the reviews? How often should I schedule?

Apple's review feed behaves like a **daily snapshot**. In our tests (September 2026), the newest review in the feed was about 1–1.5 days old. Every app we tried stopped at the same cutoff, and the feed was identical two hours apart. Scheduling more often than **once a day** therefore returns nothing new. The actor returns what the feed contains.

#### Are star-only ratings included?

No. The feed contains written reviews only. The app record's `rating_avg` and `rating_count` include star-only ratings. They are lifetime values: Apple's public Lookup API no longer gives a separate current-version rating. For the rating of each version, use `version_ratings`, which is built from the written reviews scanned.

#### What is `version_ratings`?

It groups the reviews scanned in this run by the app version the reviewer used and averages their stars. You can use it to see whether the latest release changed sentiment.

#### Do the chart ranks match the App Store?

Almost. The App Store has separate **Apps** and **Games** charts:

- `top-free`, `top-paid`, `top-free-ipad` and `top-paid-ipad` without a genre are the **Apps** charts, so games are excluded. We checked them against the App Store charts pages on 2026-09-24, and the US top 25 matched position for position on iPhone and iPad.
- For game charts, use the genre `Games` (or a game genre such as `Puzzle games`).
- `top-grossing` has no App Store equivalent any more. It is Apple's combined ranking of apps and games, labelled `All apps and games`.

**App bundles are never in Apple's chart feeds.** A bundle is several apps sold together. The App Store charts show bundles, but Apple's feeds leave them out, so an app ranked below a bundle shows one place higher than on the App Store. This mostly affects paid charts. For example, in September 2026 a driving-theory test bundle sat around #20 of UK Top Paid Apps, so the actor's ranks from there down were one place higher. The actor returns Apple's feeds as they are and cannot see where the bundles rank.

`top-paid` comes from Apple's newer chart feed (`source: marketingtools-rss`), which covers about 100 positions. That feed is slow on its first request for a storefront (up to about 30 s) and sometimes times out. The actor retries it, and if it still does not answer, uses Apple's older feed with games removed (`source: itunes-rss`) and adds a free notice row. The older feed reaches only about 20–60 positions deep for paid apps, so with `chartTrackedAppsOnly` an app ranked below that gets `rank: null` in that run. If the newer feed fails for 2 storefronts in a row, the actor stops asking it for the rest of the run. The iPad Apps charts have the same limit (about 30–70 positions). `chart_depth` tells you how deep each chart went.

Rows from the newer feed do not carry price, currency, bundle id or category. The actor fills them from the older feed, or from Apple's Lookup API for apps outside the older feed's top 100, so these fields are filled on every ranked row unless that lookup fails.

#### Why is `rank` null?

With `chartTrackedAppsOnly`, a row with `rank: null` means that the app is not in the first `chart_depth` positions of that chart in that storefront. Such a row carries the app's id and name only. The other app fields (`price`, `bundle_id`, `category` and so on) are `null` there. Add `metadata` to `modes` if you also want the app details.

#### Which countries work?

Any App Store storefront, given as a 2-letter code (`us`, `gb`, `de`, `jp`, `br`, `in`…). Common country names such as "United Kingdom" or "Germany" also work. If Apple rejects a code, you get a free notice row, and the rest of the run continues.

#### Does it cover Google Play?

No. It covers the Apple App Store only.

#### Do I need an API key or an Apple account?

No. It uses Apple's public iTunes Search/Lookup API and public RSS feeds. It sends an honest User-Agent and paces its requests:

- Search/Lookup: about one call every 3 s, since Apple documents about 20 calls/min.
- Feeds: about 3 calls per second.

Apple's review feed sometimes returns an empty page that its CDN then caches for a few minutes. The actor detects these and asks for the same page again under Apple's alternate feed URL forms, so you don't lose reviews. It retries rate limits and server errors a few times, honouring `Retry-After`. It never waits past the run's timeout: it stops early and keeps what it has.

#### What happens with a wrong app id or URL?

You get a free `notice` row naming the input and the reason, and everything else still runs. Nothing is charged for the skipped item. If nothing in the input is usable, the run returns only notice rows and charges nothing. It does not fall back to the example app. A malformed item from Apple is skipped the same way.

### Use with AI agents / MCP

Add the actor to the Apify MCP server (`mcp.apify.com`). Claude, ChatGPT, Cursor and other MCP clients can then call it as a tool. Agents can ask questions like these:

- *"What are people complaining about in the latest Duolingo release in the UK?"* The agent sets `apps`, `countries` and `ratings: ["negative"]`.
- *"Where does Notion rank in US Productivity today?"* The agent sets `apps: ["Notion"]`, `chartGenres: ["Productivity"]` and `chartTrackedAppsOnly: true`. The run fetches charts only and costs about $0.0055.
- *"Any new 1-star reviews for our app since yesterday?"* The agent sets `onlyNewReviews: true`, `ratings: ["1"]` and its own `stateKey`.

Every field description in the input schema states what it costs. The output is small, flat and typed. Skipped items come back as free `notice` rows, so an agent can explain them instead of guessing. For exact targeting, agents should pass App Store ids or URLs rather than names.

### Disclaimer

Unofficial. This actor is not affiliated with, endorsed by or sponsored by Apple Inc. App Store and iTunes are trademarks of Apple Inc. It reads only public, unauthenticated Apple endpoints. Review text belongs to its authors. Use the data in line with Apple's terms and applicable law.

### Changelog

- **0.1 (2026-09-25)**: First release.
  - Metadata, including screenshots, advisories, the full description and English genre names in every storefront.
  - Reviews: dedupe, a new-since-last-run monitor that resumes after early stops (including a capped first run), star filter and per-version aggregates.
  - App names and search terms are searched in each storefront, with a check for Apple's stand-in results. Bundle ids are found in any of the listed storefronts.
  - Charts: 7 chart types that follow the App Store's Apps charts, genres and rank tracking. Up to 50 storefronts.
  - Recovery for Apple's cached empty review pages.
  - Unusable input returns free notice rows and is never replaced by the example app. A run without charged records costs nothing.

# Actor input Schema

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

Apps to monitor: numeric App Store ids (6448311069), App Store URLs (https://apps.apple.com/us/app/chatgpt/id6448311069), bundle ids (com.burbn.instagram) or plain app names (searched for, like searchTerms). Up to 100. Ids, URLs and bundle ids mean one exact app in every storefront; an app name is searched in each storefront separately. Each app costs $0.002 per storefront (metadata) plus $0.0004 per review. If apps, searchTerms and every chart option are left empty, the example app ChatGPT is used. Entries that cannot be used are skipped with a free notice row; if none is usable, nothing is fetched or charged.

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

Optional app names/keywords to find via the App Store search (e.g. "duolingo"). Up to 20. Each term is searched in every storefront, and each storefront monitors its own top result(s). Where a storefront's top result is only Apple's stand-in for an app it does not sell (the term is in the top result's name in other storefronts but not there, e.g. 'Venmo' in gb returns PayPal), nothing is fetched there and a free notice says so. Searching takes about 3 s per term per storefront (Apple allows ~20 searches a minute); past 200 searches the remaining storefronts reuse the app found. Use ids or URLs for exact targeting.

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

2-letter App Store country codes (us, gb, de, jp, br…) or country names. Reviews, prices and charts are per storefront. Default: us (or the country in an App Store URL). Max 50.

## `modes` (type: `array`):

Any of: metadata (app details + rating, $0.002/app/storefront), reviews (recent customer reviews, $0.0004 each), charts (top-chart ranks, $0.0005/row), or all. Case-insensitive; synonyms such as details, ratings or rankings work. Empty = metadata + reviews, or charts only when a chart option (chartTypes, chartGenres, chartTrackedAppsOnly) is set. When both modes and a chart option are set, charts is added to these modes.

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

Cap on reviews returned per app per storefront, newest first. Apple's public feed exposes at most the 500 most recent written reviews per storefront.

## `onlyNewReviews` (type: `boolean`):

Monitoring mode: return (and charge for) only reviews that earlier monitoring runs did not return. The first monitoring run returns the newest maxReviewsPerApp reviews and sets where the monitor starts: older reviews are never returned by it. If a run stops before returning everything it should (maxRecords, max charge, timeout, an Apple error, or on later runs maxReviewsPerApp), the next run returns the reviews it skipped, while Apple still lists them (newest 500). Seen review ids are saved in the named key-value store 'app-store-reviews-monitor-state' in your account, per app, storefront and stateKey. Apple's review feed updates about once a day, so schedule at most daily.

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

Optional: only return reviews with these star ratings, e.g. \["1", "2"], \["negative"] (1-2), \["positive"] (4-5) or ranges like \["1-3"]. Empty = all ratings. Filtered-out reviews are not charged. The app record's review aggregates (reviews\_scanned, histogram, version\_ratings) still cover every review scanned, not only the ones returned.

## `chartTypes` (type: `array`):

Top charts: top-free, top-paid, top-free-ipad, top-paid-ipad (the App Store's Apps charts, games excluded; for game charts add chartGenres "Games", or write e.g. "top free games"), top-grossing, top-grossing-ipad (apps and games combined; no longer shown in the App Store), top-free-mac. Setting this turns on charts. Default when only other chart options are set: top-free. $0.0005 per row. Unrecognised names are skipped with a free notice; none is substituted.

## `chartGenres` (type: `array`):

Optional App Store genres for the charts: names (Games, Productivity, Finance, Puzzle games…) or ids (6014). Empty = the overall chart. Setting this turns on charts. Unknown genres are skipped with a free notice; if none is usable, no chart is fetched.

## `chartLimit` (type: `integer`):

Rows per chart (1-100). Each row costs $0.0005. Some Apps charts have fewer positions: the iPad Apps charts (about 30-70), and top-paid (about 20-60) when Apple's newer chart feed does not answer (a free notice row says so).

## `chartTrackedAppsOnly` (type: `boolean`):

Rank tracking: scan up to 100 positions of each chart and return only rows for the apps in 'apps'/'searchTerms' — one row per app per chart per storefront, with rank null when the app is not within chart\_depth positions. Turns on charts; without modes set, only charts are fetched (no metadata or review charges). Needs apps or searchTerms: without any usable one, no chart is fetched or charged.

## `searchResultsPerTerm` (type: `integer`):

How many top search results to monitor per search term.

## `maxRecords` (type: `integer`):

Optional hard cap on charged records (app records + reviews + chart rows) for this run. Free notice rows do not count.

## `stateKey` (type: `string`):

Name of a monitor: keeps separate new-review state for separate monitors of the same app (e.g. one per Slack channel or star filter). Runs with a stateKey or onlyNewReviews save state; other runs only read it. Default: default.

## Actor input object example

```json
{
  "apps": [
    "6448311069"
  ],
  "countries": [
    "us"
  ],
  "maxReviewsPerApp": 100,
  "onlyNewReviews": false,
  "chartLimit": 25,
  "chartTrackedAppsOnly": false,
  "searchResultsPerTerm": 1
}
```

# Actor output Schema

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

No description

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

No description

## `apps` (type: `string`):

No description

## `charts` (type: `string`):

No description

## `notices` (type: `string`):

No description

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

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "apps": ["6448311069"],
    "countries": ["us"],
}

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

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

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

```

## CLI example

```bash
echo '{
  "apps": [
    "6448311069"
  ],
  "countries": [
    "us"
  ]
}' |
apify call bigdavidson/app-store-reviews-monitor --silent --output-dataset

```

## MCP server setup

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

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/Npvb8yYRPsu1fdraL/builds/iqBELKiNqlliydhlB/openapi.json
