# App Store Price Tracker — App & In-App Prices by Country (`northbell/app-store-price-tracker`) Actor

An app's price and its in-app purchase prices in every App Store country, with the price changes since your last run. Give it App Store links or app IDs and a list of countries (or all 175 storefronts); get one row per price and per in-app purchase in the local currency. No login.

- **URL**: https://apify.com/northbell/app-store-price-tracker.md
- **Developed by:** [Northbell](https://apify.com/northbell) (community)
- **Categories:** Marketing, 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 rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Price Tracker — App & In-App Prices by Country

Give it App Store links or app IDs and a list of countries (or all 175 App Store storefronts) and get the **app's price and its in-app purchases with their prices in each country's own currency**, one clean row each. **$0.50 per 1,000 rows**, no login, no API key. One app in all 175 storefronts is 175 price rows, plus its in-app purchase rows when you include them.

Run it again — or put it on a schedule — and every row also tells you **what changed since the last run**: the previous price, whether it changed, by how many percent, and which in-app purchases are new. The App Store only shows you today's price; this keeps the memory, per app and country.

### What you get

| Field | What it is |
|---|---|
| `type` | `price` (the app itself), `iap` (one in-app purchase), `summary` (one free row per app) or `error` (free) |
| `appId`, `appName`, `developer`, `url`, `input` | the app (the name is the one the first country returned, so it is the same on every row), the seller, the App Store link for that country, and the text you gave for this app |
| `country`, `countryName`, `currency` | the storefront and the currency its prices are in (`USD`, `JPY`, `EUR`, `BRL`…) |
| `price`, `formattedPrice`, `isFree` | the app's price as a number, and as the store writes it (`$4.99`, `¥800`, `5,99 €`; `Free` for free apps) |
| `name`, `position`, `listings` | `iap` rows: the in-app purchase's name, its place in Apple's list (1 = first), and how many times Apple listed this exact name and price |
| `hasInAppPurchases` | `price` rows: whether the app page says the app has in-app purchases (`null` when in-app purchases were not read) |
| `previousPrice`, `previousCurrency`, `priceChanged`, `changePercent`, `previousObservedAt` | the change since your previous run (`null` on the first run). `changePercent` is `0` when a paid price is the same; it stays `null` for a free item and when the currency changed |
| `isNew` | `iap` rows: `true` when this purchase was not in the list last time (`null` on the first run) |
| `observedAt` | when the row was read |

The **summary row** (free) has, per app: `countriesRequested`, `countriesRead`, `countriesNotAvailable` (the app is not on sale there), `countriesFailed`, `freeInCountries`, `currencies`, `iapCount`, `iapNotRead` (countries whose in-app list could not be read), and — from the second run — `priceChangesSinceLastRun`, `iapPriceChangesSinceLastRun`, `iapNewSinceLastRun`, `iapNoLongerListedSinceLastRun`.

### Run it again and again

- **On a schedule** — save your input as a task and add a **Schedule**: the same apps every day. Send each run to Google Sheets, Slack or email with an integration on the task.
- **Watch for changes** — Keep the same input on the schedule. Each run is its own dataset with the date it ran, so the series builds up run by run and you can compare one day with the last.
- **In bulk** — Put every app in **Apps** and every store in **Countries**: one run prices them all.

### Use it for

- **Pricing teams** — put your own price and your competitors' side by side in every storefront, and see where a rival is cheaper
- **Price-change alerts** — schedule it daily and filter for `priceChanged = true` to hear about a regional increase or a new subscription tier the day it appears
- **Localisation and pricing research** — all the in-app purchase prices for an app in every country, in one table

### Input

```json
{
  "apps": ["https://apps.apple.com/us/app/duolingo-language-lessons/id570060128", "1232780281"],
  "countries": ["us", "gb", "de", "jp", "br", "in"],
  "includeInAppPurchases": true
}
```

Leave `countries` as the pre-filled 20 major storefronts, list your own, or write `"all"` for every one of Apple's 175. What changed is remembered per app and country in a named key-value store in your Apify account, so the second run of the same Actor already shows changes. Schedule it (Apify → Schedules) for a daily log.

### Pricing

**$0.50 per 1,000 rows** (one row = one app price or one in-app purchase price in one country), plus $0.01 per run. Error rows and the free summary rows are not charged. The $0.01 run-start fee is the one charge every run pays, even one that returns nothing. Example (10/9): Duolingo, Notion and Procreate Pocket in the 20 default countries returned 60 price rows and 200 in-app purchase rows (260 rows = $0.13, plus the 3 free summary rows) — $0.14 with the run fee. One app's price in all 175 storefronts, without in-app purchases, is at most 175 rows: about $0.09, or $0.10 with the run fee.

### Notes

- Public data only: the app price from Apple's public iTunes Lookup service and the "In-App Purchases" list on the app's public page at apps.apple.com. No reviews, reviewer names, emails or phone numbers are returned.
- **The country you choose is the App Store you get.** Currency and prices differ by storefront, and Apple answers for the country named in the request, not for where the caller is. On 10/9, from the same machine, Procreate Pocket came back as `$5.99` in `us`, `¥1,000` in `jp`, `6,99 €` in `de`, `R$ 39,90` in `br` and `₹ 599` in `in`. Apple has **175 storefronts**; `"all"` reads every one of them.
- **No currency conversion.** Every row keeps its own storefront's `currency`. Apple publishes no exchange rates, so a "cheapest country" would rest on a rate taken from somewhere else; the summary row therefore has no `cheapestCountry`. Compare prices inside one currency (many storefronts share `USD` or `EUR`), or convert in your own sheet. In a 10/9 check of three paid apps across all 175 storefronts the prices came back in 43 currencies.
- **Country codes.** Use Apple's two-letter codes; the United Kingdom is `gb`, not `uk`. A code Apple does not have (`uk`, `zz`) gives a free error row instead of another country's data, because Apple would quietly show the US store for it. A code that is not an App Store country (for example `bd`) gives a free error row too.
- **An app that is not on sale** in a country shows up in the summary row's `countriesNotAvailable` (it is not an error row and costs nothing). An app on sale in none of the countries you asked for gives one free error row.
- **In-app purchases are Apple's "Top In-App Purchases" list, not the full catalogue.** The page lists at most 10 per country, in Apple's own order (`position`), and the ten can differ between countries. Apple gives no purchase IDs, and one name can appear at several prices (monthly, yearly, family plans). Entries with the same name **and** price are returned once, with `listings` saying how many times Apple listed them, so a country can return fewer than 10 rows.
- **Since the last run, purchases are matched by name.** The same name with the same price is the same purchase (`isNew: false`, `priceChanged: false`). If the leftovers under one name are equally many on both sides, they are paired lowest price to lowest price and flagged `priceChanged: true` with `previousPrice` and `changePercent`. Anything else is `isNew: true` — it can be a purchase that moved into the top 10 rather than one that was just created. `iapNoLongerListedSinceLastRun` counts the ones that dropped out.
- **A change of currency** (a country joining the euro, for example) is reported as `priceChanged: true` with `previousCurrency`, `changePercent` stays `null`, and that run's in-app purchases are not compared with the old currency's.
- Fields that are empty on some rows: `previousPrice`, `previousCurrency`, `priceChanged`, `changePercent`, `previousObservedAt` and `isNew` are `null` on the first run for an app and country. `changePercent` is `0` for an unchanged price and the percentage for a changed one; it is `null` for a free item (nothing to divide by), when the currency changed, and when a price could not be read as a number. `hasInAppPurchases` is `null` when *Include in-app purchases* is off or the page could not be read. The summary's `...SinceLastRun` fields are `null` until an app has been read before.
- `price` of an in-app purchase is the number read from the storefront's own text (`89,99 €` is 89.99, `Rp 119ribu` is 119000); `formattedPrice` always keeps the text as the store writes it. For a paid app the same reading is checked against Apple's own number for the app price: in a 10/9 check of 519 paid prices (three apps, 175 storefronts) every one matched. A storefront whose format does not reproduce Apple's number would get text only (`price: null`) for its in-app purchases rather than a wrong number.
- `appName` is the name the first country returned, asked for in English, so all of an app's rows carry the same name; Apple's own name for an app can still differ by country (on 10/9 one game's name ended with a different subtitle in France and Japan).
- If one country's in-app purchases cannot be read, you still get the price row and a free error row for that country. If Apple's price service cannot be reached at all, the run fails instead of finishing empty.
- **Run time.** Apple documents about 20 calls a minute for its price service, so the Actor reads one country about every 3.5 seconds, however many apps you give it for prices (up to 100 apps per run). Reading in-app purchases adds one app page per app and country, three at a time, about a quarter of a second each (30 pages took 8 seconds on 10/9). Measured on 10/9: three apps in the 20 default countries, with in-app purchases, took 64 and 70 seconds in two runs; the prices of three apps in all 175 storefronts (no in-app purchases) took 10 minutes 26 seconds. A very large run, such as 100 apps in all 175 storefronts with in-app purchases (about 17,500 app pages, roughly 80 minutes at that rate), is better split into several runs.

To track an app's **keyword ranks and rating swings** every day, see [App Store Rank & Rating Watch](https://apify.com/northbell/app-store-rank-and-review-watch). For the full **app page** (privacy labels, version history, release notes), use [App Store App Details](https://apify.com/northbell/app-store-app-details). For the **reviews** of an app, see [App Store Reviews Scraper](https://apify.com/northbell/app-store-reviews-scraper).

### For AI agents

`{"apps": ["570060128"], "countries": ["us", "jp", "de"]}` returns, for each country, one `price` row (price, currency, `formattedPrice`), the app's in-app purchases as `iap` rows with prices in the local currency, and one free `summary` row. Rows have `type`: `price`, `iap`, `summary` or `error`. Call it again with the same input to get `previousPrice`, `priceChanged`, `changePercent` and `isNew`. There is no currency conversion: compare prices inside one `currency`.

# Actor input Schema

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

One app per line: an App Store link (apps.apple.com/…/app/…/id123456789) or the numeric app ID. Up to 100 apps per run. The country inside a pasted link is ignored; set Countries instead.

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

Two-letter App Store country codes, one per line (us, gb, jp, de, br, in …; the United Kingdom is gb). Leave the 20 pre-filled ones, or write all to read every one of Apple's 175 storefronts (Apple documents about 20 calls a minute for its price service, so a country takes about 3.5 seconds: all 175 take about 10 minutes).

## `includeInAppPurchases` (type: `boolean`):

Also return the app's top in-app purchases (Apple lists up to 10 per country) with their prices in the local currency. Turn off to get only the app's own price, which is faster and returns fewer rows.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128"
  ],
  "countries": [
    "us",
    "gb",
    "ca",
    "au",
    "de",
    "fr",
    "es",
    "jp",
    "kr",
    "cn",
    "in",
    "id",
    "br",
    "mx",
    "tr",
    "ch",
    "se",
    "pl",
    "sg",
    "ae"
  ],
  "includeInAppPurchases": true
}
```

# Actor output Schema

## `prices` (type: `string`):

App prices and in-app purchase prices by country, with the price change since the last run.

# 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": [
        "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128"
    ],
    "countries": [
        "us",
        "gb",
        "ca",
        "au",
        "de",
        "fr",
        "es",
        "jp",
        "kr",
        "cn",
        "in",
        "id",
        "br",
        "mx",
        "tr",
        "ch",
        "se",
        "pl",
        "sg",
        "ae"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("northbell/app-store-price-tracker").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": ["https://apps.apple.com/us/app/duolingo-language-lessons/id570060128"],
    "countries": [
        "us",
        "gb",
        "ca",
        "au",
        "de",
        "fr",
        "es",
        "jp",
        "kr",
        "cn",
        "in",
        "id",
        "br",
        "mx",
        "tr",
        "ch",
        "se",
        "pl",
        "sg",
        "ae",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("northbell/app-store-price-tracker").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": [
    "https://apps.apple.com/us/app/duolingo-language-lessons/id570060128"
  ],
  "countries": [
    "us",
    "gb",
    "ca",
    "au",
    "de",
    "fr",
    "es",
    "jp",
    "kr",
    "cn",
    "in",
    "id",
    "br",
    "mx",
    "tr",
    "ch",
    "se",
    "pl",
    "sg",
    "ae"
  ]
}' |
apify call northbell/app-store-price-tracker --silent --output-dataset

```

## MCP server setup

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

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/R2HRkbcV3LN4qwpfv/builds/ArbIAvKOTLJ5ChKE6/openapi.json
