# App Store Rank Tracker — Track Rankings, Prices & Competitors (`quaffable_mettle/app-store-rank-tracker-track-rankings-prices-competitors`) Actor

Track App Store rankings, prices and rank movements across countries. Get structured data for ASO research, competitor monitoring, app market analysis and product intelligence.

- **URL**: https://apify.com/quaffable\_mettle/app-store-rank-tracker-track-rankings-prices-competitors.md
- **Developed by:** [Emir Llonviche](https://apify.com/quaffable_mettle) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$80.00 / 1,000 chart processeds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## App Store Charts Scraper — New & Rising, 32 Countries

Get the Top Free and Top Paid App Store charts for up to 32 countries in a single run, and get what a chart alone never tells you: **which apps are new, which ones are climbing, which ones chart in several countries at once, and what each one actually costs where it charts.**

*This is an unofficial scraper. It is not affiliated with, endorsed by, or sponsored by Apple Inc. All trademarks belong to their respective owners.*

It reads Apple's public chart feed directly — no headless browser, no proxies, no API key, no login. A 3-country run finishes in a few seconds.

### What you get

One row per app, per country, per chart:

| Field | What it is |
|---|---|
| `storefront`, `storefrontName`, `chartName` | Country code, country name, and which chart |
| `rank` | Position in the chart, 1 = top |
| `name`, `developer`, `seller` | App title and who publishes it |
| `appId`, `appStoreUrl`, `artworkUrl` | Apple id, public page, icon at 512×512 |
| **`isNew`** | The app was not in this chart on your previous run |
| **`rankChange`** | Positions gained since your previous run (12 → 4 = `+8`) |
| **`peakRank`** | Best rank it has reached across all your runs |
| **`daysInChart`** | How long it has been charting without interruption |
| **`alsoChartingIn`, `storefrontCount`** | The other countries where it charts *right now* |
| **`formattedPrice`, `price`, `currency`, `isFree`** | The local price, in the local currency |
| **`averageUserRating`, `userRatingCount`** | The local rating — French users rate differently from American ones |
| **`daysSinceUpdate`, `lastUpdatedAt`, `version`, `releaseNotes`** | How actively the app is maintained |
| `primaryGenre`, `genres`, `contentRating`, `minimumOsVersion`, `fileSizeMb` | Category and technical details |
| `releaseDate`, `chartUpdatedAt`, `scrapedAt`, `sourceUrl` | First release, Apple's chart timestamp, your run timestamp, source |

### What makes it different

**Movement.** Each run is compared against your previous one, stored between runs, so you see what entered the chart and what is climbing. Apps are tracked by their Apple id, so a rebranded app is not mistaken for a new one. Free and paid charts are tracked separately — they are two different rankings.

**Cross-country presence.** Every country you ask for is fetched in the same run, so an app breaking out in four markets at once is visible as such. A scraper that does one country per run cannot compute this at all.

**Local prices and local ratings.** Details are looked up **per storefront**. Apple's lookup returns the same number `2.99` whether you ask in dollars or euros — only the currency changes — and ratings differ by market too. Enriching a French chart against the American storefront would give you dollars and US ratings for apps ranked in France. Quietly wrong, and easy to miss. This Actor does one lookup per country instead.

On the **first** run there is no history to compare against, so `isNew`, `rankChange` and `daysInChart` are `null` — not `true`, not `0`. Claiming everything is new when we simply have no history would be a lie you could not detect. Run it a second time and the movement fields fill in.

### Input

```json
{
  "storefronts": ["US", "GB", "FR", "DE"],
  "charts": ["top-free", "top-paid"],
  "chartSize": 100,
  "includeDetails": true,
  "genre": "",
  "timeoutSecs": 30
}
```

- **storefronts** — two-letter country codes. Verified on both charts: `AR AT AU BE BR CA CH CL CO DE DK ES FI FR GB IE IN IT JP KR MX NL NO NZ PL PT RU SE SG TR US ZA`. Other codes are attempted with a warning rather than refused.
- **charts** — `top-free`, `top-paid`, or both.
- **chartSize** — 10 to 100 apps per chart.
- **includeDetails** — genre, price, rating, version and update recency. One extra request per country.
- **genre** — keep only apps whose genre contains this text (`Games`, `Finance`, `Social Networking`…). Needs details on; see the limits below.

### Typical uses

- Spot an app breaking out before it is obvious — new entries climbing in several countries at once.
- Track your own app, or a competitor's, across markets on a schedule.
- Compare what a category costs country by country, in local currency.
- Find well-ranked apps that have stopped shipping updates.

### Pricing

**$0.08 per chart processed** — one country, one chart. Two charts across three countries is six charts, $0.48. You are charged only for charts that actually returned data: a country that fails costs you nothing. Chart size, details and the genre filter are all included.

### Running it on a schedule

Movement fields need a previous run to compare against, so this Actor is at its best on a schedule. Use Apify's **Schedule**, and keep the same countries and charts between runs so the history stays comparable.

### Limits, stated plainly

- **Apple publishes exactly two charts here: Top Free and Top Paid.** There is no top-grossing chart on this feed, and no iPad or Mac variant — every other name returns 404. Verified, not assumed.
- **100 apps per chart is a hard ceiling.** Above 100 Apple answers with a server error rather than a shorter list, so higher values are clamped before the request.
- **The chart feed's `genres` field is always empty.** Genre comes from the details lookup, which is why the genre filter needs details switched on. The Actor tells you this instead of silently returning nothing.
- `rankChange` compares against **your** previous run, whatever its date. Two runs three weeks apart give you a three-week delta.
- An app that leaves the chart and returns is counted as new on its return: `daysInChart` measures an uninterrupted stay.
- **Freshness is not uniform across countries.** During testing one storefront's free chart carried a timestamp ten days old while its paid chart was same-day. Every row exposes `chartUpdatedAt` so you can see the age of what you got instead of guessing.
- Charts run in parallel and stay isolated: one country failing never stops the others, and `RUN_SUMMARY` records exactly which ones failed and why.

### Output

Alongside the dataset, each run writes a `RUN_SUMMARY` record: charts requested, how many succeeded, how many failed with the reason, total rows, lookup calls made, and how many apps chart in more than one country. Point a monitor at it and you will know a storefront broke before your users tell you.

### Support

Found a bug, or a storefront that should be on the verified list? Open an issue on the Actor page and it will be looked at.

# Actor input Schema

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

Two-letter country codes. Each chart is fetched independently, so a failing storefront never breaks the run. Verified on both charts: AR AT AU BE BR CA CH CL CO DE DK ES FI FR GB IE IN IT JP KR MX NL NO NZ PL PT RU SE SG TR US ZA.

## `charts` (type: `array`):

Which rankings to fetch. Apple publishes exactly two under this feed — there is no top-grossing chart. Each storefront-and-chart pair is billed once.

## `chartSize` (type: `integer`):

How many apps to take from each chart, from the top down. Apple's hard ceiling is 100 — asking for more returns a server error, so higher values are clamped.

## `includeDetails` (type: `boolean`):

Add genre, local price and currency, star rating and rating count, current version, days since the last update, developer, age rating, size and minimum iOS version. Looked up per storefront, so prices and ratings are the local ones — not the American ones.

## `genre` (type: `string`):

Keep only apps whose genre contains this text, for example "Games", "Finance", "Social Networking". Requires app details: Apple's chart feed ships an empty genre field, so the genre can only come from the lookup.

## `timeoutSecs` (type: `integer`):

How long to wait for each request before retrying. Raise it only if you see timeout warnings in the log.

## Actor input object example

```json
{
  "storefronts": [
    "US",
    "GB",
    "FR"
  ],
  "charts": [
    "top-free",
    "top-paid"
  ],
  "chartSize": 100,
  "includeDetails": true,
  "genre": "Games",
  "timeoutSecs": 30
}
```

# Actor output Schema

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

One row per app, per storefront, per chart: rank, movement since your last run, cross-country presence, local price and rating, and update recency.

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

Which charts were requested, how many succeeded or failed and why. Point a monitor at this to catch a storefront breaking before your users do.

# 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",
        "FR"
    ],
    "charts": [
        "top-free",
        "top-paid"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("quaffable_mettle/app-store-rank-tracker-track-rankings-prices-competitors").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",
        "FR",
    ],
    "charts": [
        "top-free",
        "top-paid",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("quaffable_mettle/app-store-rank-tracker-track-rankings-prices-competitors").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",
    "FR"
  ],
  "charts": [
    "top-free",
    "top-paid"
  ]
}' |
apify call quaffable_mettle/app-store-rank-tracker-track-rankings-prices-competitors --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,quaffable_mettle/app-store-rank-tracker-track-rankings-prices-competitors"
        }
    }
}

```

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/Lo9wx22pqaWL6MqAY/builds/Qcg6OuMqghGfdUl19/openapi.json
