# App Store Chart Breakout Scanner (`perpetual_ocher/app-store-chart-breakout-scanner`) Actor

Finds apps breaking out across multiple Apple App Store markets at once. Official Apple RSS feeds only. No reviews, no developer names, no personal data.

- **URL**: https://apify.com/perpetual\_ocher/app-store-chart-breakout-scanner.md
- **Developed by:** [Kangsoo Hyun](https://apify.com/perpetual_ocher) (community)
- **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/actors/running/actors-in-store.md#pay-per-usage

## 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 Chart Breakout Scanner

**Find the apps that are breaking out across many Apple App Store markets at the same time — before they show up in the US top 10.**

This Actor reads Apple's own public chart feeds for up to 50 country storefronts in a single run, then scores every app on how *broadly* it is charting, not just how high. The output is one clean, normalized dataset you can drop straight into a spreadsheet, a BI tool, or an AI agent.

It does **not** scrape reviews, developers, or anything about people. See [What this Actor deliberately does not do](#what-this-actor-deliberately-does-not-do).

***

### Why breadth beats rank

A single-country top-100 list tells you what is already big in one place. It does not tell you what is *moving*.

An app that sits at rank 40 in twenty-five different countries is a very different signal from an app at rank 40 in the US alone — the first is a global breakout in progress, the second is one market's local taste. This Actor computes that difference for you and hands you a single sortable number.

**Use it to:**

- Spot a competitor's new app going global while it is still outside the US top 20
- Track your own app's rank across every market you ship in, in one run, on a schedule
- Feed a market-intelligence agent a compact daily snapshot of what is rising worldwide
- Find category breakouts to benchmark your ASO or UA spend against

***

### What you get

Every row is one app, in one market, on one chart — enriched with cross-market context computed across the whole run:

| Field | Meaning |
|---|---|
| `app_id`, `app_name`, `app_url` | Apple's App Store identifiers and canonical link |
| `chart`, `storefront`, `rank` | Which chart, which country, what position |
| `breadth_score` | 0–100. How broadly *and* how highly this app is charting (formula below) |
| `markets_charting` / `markets_scanned` | In how many of the scanned markets this app appears |
| `best_rank`, `best_rank_market` | Its strongest position and where |
| `median_rank` | Typical position across all markets it charts in |
| `primary_genre`, `genres` | Apple's category labels |
| `release_date`, `days_since_release`, `is_new_release` | How new the app is |
| `is_multi_market_breakout` | New release **and** charting in ≥ N markets |
| `charts_present` | Whether it shows up on top-free, top-paid, or both |
| `feed_updated_at`, `snapshot_at` | Apple's feed timestamp and your run timestamp |

#### How `breadth_score` is calculated

No black box — you can recompute it yourself:

```
coverage     = markets_charting / markets_scanned
rank_quality = 1 - (median_rank - 1) / chart_depth
breadth_score = round(100 * coverage * rank_quality)
```

An app charting at rank 1 in every scanned market scores 100. An app charting at the very bottom of one market out of fifty scores 0.

***

### Input

| Option | Default | Notes |
|---|---|---|
| `storefrontPreset` | `top10` | `top10`, `top30`, `top50`, or `custom` |
| `storefronts` | – | Your own ISO-3166-1 alpha-2 codes, e.g. `["us","gb","jp"]`, when preset is `custom` |
| `chartTypes` | `["top-free"]` | `top-free` and/or `top-paid` |
| `chartDepth` | `50` | How deep to read each chart. Apple caps this at 100 |
| `maxResults` | `1000` | Hard cap on billable rows |
| `outputGranularity` | `market-row` | See below — this is the option that controls your bill |
| `onlyBreakouts` | `false` | Return only rows flagged `is_multi_market_breakout` |
| `minMarketsForBreakout` | `3` | Markets needed to qualify as a breakout |
| `newReleaseWindowDays` | `90` | How new an app must be to count as a new release |
| `sortBy` | `breadth_score` | Also `markets_charting`, `rank`, `newest` |

#### Choose the granularity that matches what you are paying for

You are charged per row delivered, so pick deliberately:

- **`market-row`** — one row per app **per market** per chart. Use this when you want rank tracking per country. Scanning 30 markets × 2 charts at depth 50 produces roughly 3,000 rows.
- **`app-summary`** — one row per app per chart, carrying a `market_ranks` map of every country it charts in. **Typically 10–30× fewer rows for the same information**, because the top charts overlap heavily between countries. Use this when you want to know *which apps* are moving, not their exact position in each country.

If you only want the signal, combine `outputGranularity: "app-summary"` with `onlyBreakouts: true`.

***

### Example

**Input**

```json
{
  "storefrontPreset": "top30",
  "chartTypes": ["top-free", "top-paid"],
  "chartDepth": 50,
  "outputGranularity": "app-summary",
  "onlyBreakouts": true,
  "minMarketsForBreakout": 5,
  "newReleaseWindowDays": 60
}
```

**Output row**

```json
{
  "app_id": "6800616114",
  "app_name": "Play Music Theory",
  "app_url": "https://apps.apple.com/us/app/play-music-theory/id6800616114",
  "chart": "top-paid",
  "primary_genre": "Education",
  "release_date": "2026-08-19",
  "days_since_release": 31,
  "is_new_release": true,
  "markets_charting": 30,
  "markets_scanned": 30,
  "best_rank": 1,
  "best_rank_market": "us",
  "median_rank": 3,
  "breadth_score": 96,
  "is_multi_market_breakout": true,
  "market_ranks": { "us": 1, "jp": 3, "gb": 3, "de": 2, "fr": 4 },
  "snapshot_at": "2026-09-19T14:57:13.049Z"
}
```

A paid education app at rank 1–4 in all thirty of the world's largest App Store markets, five weeks after release. That is the kind of row this Actor exists to surface.

***

### Run it on a schedule

Breadth is most useful as a time series. Set an Apify **Schedule** to run this daily, point it at the same dataset, and you have a rank history across every market you care about — with no baseline to maintain yourself.

***

### What this Actor deliberately does not do

This is a product decision, not an oversight.

- **No personal data.** No reviews, no reviewer names, no developer or publisher names, no emails, no profiles. Apple's feed includes a developer name field; this Actor drops it before anything is written to the dataset.
- **No app icons or artwork.** Apple licenses those as promotional content under specific placement conditions. This Actor does not redistribute them.
- **No login walls, no cookies, no session replay, no anti-bot evasion.** There is nothing to evade.
- **No scraping of App Store web pages.** The only endpoint this Actor touches is Apple's public Marketing Tools RSS API (`rss.marketingtools.apple.com`), the same feed Apple's own public RSS Builder generates. Its `robots.txt` declares no disallowed paths.

The practical upside for you: no proxies, no blocks, no breakage when a page layout changes, and nothing in the output that creates a data-protection problem downstream.

***

### Known limits — stated up front

- **Apple caps chart depth at 100.** Asking for more returns an error, so the Actor clamps to 100.
- **Apple publishes `top-free` and `top-paid` for apps.** There is no `top-grossing` app feed on this endpoint.
- **Not every storefront publishes every chart.** Where Apple returns nothing, the run reports it and continues rather than failing.
- **Apple's feed returns transient `503`s under parallel load.** The Actor stays at a polite request rate and retries with exponential backoff. This is why a wide scan takes a minute or two rather than seconds.
- **`genres` is occasionally empty** for a small number of entries in Apple's feed. The field is passed through as-is rather than guessed at.
- **Charts are a ranking, not a download count.** Apple does not publish download numbers here, and neither does this Actor.

***

### Output granularity and your bill

The `RUN_STATS` record in the run's key-value store reports `market_row_observations`, `rows_built`, `rows_emitted`, and `unique_apps`, so you can see exactly what you were charged for and tune `outputGranularity`, `chartDepth`, and `maxResults` before scaling up.

***

### Data source

Apple Marketing Tools RSS API — `https://rss.marketingtools.apple.com/api/v2/{storefront}/apps/{chart}/{limit}/apps.json`

Publicly documented by Apple through its RSS Builder at <https://rss.marketingtools.apple.com>. App Store content and chart data are Apple's; this Actor normalizes and scores what Apple publishes.

# Actor input Schema

## `storefrontPreset` (type: `string`):

Which set of Apple App Store country storefronts to scan. Choose 'custom' to supply your own list below.

## `storefronts` (type: `array`):

ISO 3166-1 alpha-2 country codes, e.g. us, gb, jp. Only used when the preset is set to 'custom'.

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

Which Apple charts to scan. Apple publishes top-free and top-paid for apps.

## `chartDepth` (type: `integer`):

How many ranks deep to read per chart. Apple's feed caps at 100.

## `maxResults` (type: `integer`):

Hard cap on rows written to the dataset. You are charged per row delivered.

## `newReleaseWindowDays` (type: `integer`):

An app first released within this many days counts as a new release for breakout detection.

## `minMarketsForBreakout` (type: `integer`):

How many scanned markets an app must chart in (while also being a new release) to be flagged as a multi-market breakout.

## `onlyBreakouts` (type: `boolean`):

Drop every row that is not flagged as a multi-market breakout. Cuts your bill when you only want the signal.

## `outputGranularity` (type: `string`):

'market-row' emits one row per app per market per chart (best for rank tracking). 'app-summary' emits one row per app per chart with a per-market rank map (typically 10-30x fewer billable rows).

## `sortBy` (type: `string`):

How to order rows in the output dataset.

## Actor input object example

```json
{
  "storefrontPreset": "top10",
  "storefronts": [
    "us",
    "gb",
    "de",
    "jp",
    "br"
  ],
  "chartTypes": [
    "top-free"
  ],
  "chartDepth": 50,
  "maxResults": 1000,
  "newReleaseWindowDays": 90,
  "minMarketsForBreakout": 3,
  "onlyBreakouts": false,
  "outputGranularity": "market-row",
  "sortBy": "breadth_score"
}
```

# Actor output Schema

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

Dataset items matching the Actor dataset schema (market-row or app-summary).

## `runStats` (type: `string`):

JSON summary written to the default key-value store as RUN\_STATS.

# 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 = {
    "storefronts": [
        "us",
        "gb",
        "de",
        "jp",
        "br"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("perpetual_ocher/app-store-chart-breakout-scanner").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 = { "storefronts": [
        "us",
        "gb",
        "de",
        "jp",
        "br",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("perpetual_ocher/app-store-chart-breakout-scanner").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 '{
  "storefronts": [
    "us",
    "gb",
    "de",
    "jp",
    "br"
  ]
}' |
apify call perpetual_ocher/app-store-chart-breakout-scanner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,perpetual_ocher/app-store-chart-breakout-scanner"
        }
    }
}
```

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/NkLDp166wruaEIrUX/builds/YKbBHvD3A174XAqtp/openapi.json
