# App Store App Details Scraper (`renzomacar/app-store-app-details-scraper`) Actor

Full App Store metadata for any iOS app: ratings, rating counts, in-app purchase names and prices, current version, release notes, size, languages and screenshots.

- **URL**: https://apify.com/renzomacar/app-store-app-details-scraper.md
- **Developed by:** [Renzo Madueno](https://apify.com/renzomacar) (community)
- **Categories:** E-commerce, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 app details

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 App Details Scraper - Ratings, IAPs & Versions

Complete public metadata for any iOS app: rating average and rating count, **the actual names and prices of its in-app purchases**, current version with release notes, first release date, file size, minimum iOS, supported languages, content rating, publisher details and screenshot URLs. No API key, no developer account, no App Store Connect access, no proxy.

Accepts numeric App Store IDs, `apps.apple.com` URLs and iOS bundle IDs, mixed freely in one run, across as many country storefronts as you want.

### The in-app purchase list, and why most scrapers do not have it

Apple's public metadata endpoint does **not** carry in-app purchase names or prices. Not a partial version of it - the field simply does not exist there. That is why almost every App Store scraper stops at "this app has IAPs, probably".

That data lives on the storefront HTML page instead, and this actor reads it from there. What comes back is the real list, item by item, with both the displayed string and a parsed number:

```json
"inAppPurchases": [
  { "name": "ChatGPT Plus",   "price": "$19.99",  "priceValue": 19.99 },
  { "name": "ChatGPT Go",     "price": "$8.00",   "priceValue": 8 },
  { "name": "100 Credits",    "price": "$4.00",   "priceValue": 4 },
  { "name": "ChatGPT Pro 5x", "price": "$100.00", "priceValue": 100 }
]
```

#### It works outside the English-speaking storefronts, which is harder than it sounds

Two things break naive IAP scrapers, and both were found by auditing content rather than trusting a fill rate:

**The section label is localized.** Apple writes "In-App Purchases" in the US, but "In-App-Käufe" in Germany, "Achats intégrés" in France, "Compras dentro do app" in Brazil and "アプリ内購入" in Japan. Anchoring on the English string makes every non-English storefront report `hasInAppPurchases: false` with an empty list - a confidently wrong column, in a field people will trust. This actor reads the price list structurally instead, so the label language is irrelevant.

**The decimal separator is localized too.** The same product is `$19.99`, `22,99 €`, `R$ 99,90` and `¥3,000`. Stripping separators turns `22,99 €` into 2299 - a hundredfold error - while `¥3,000` really is 3000. So separators are classified rather than deleted: the last separator is a decimal point only when one or two digits follow it. The parser is checked against 18 cases including `1.299,99 €`, `$1,299.99`, `¥16,800` and free-tier labels in five languages.

Verified live across six storefronts, same app:

| Storefront | IAP detected | Items | Parsed range | Raw price string |
|---|---|---|---|---|
| us | yes | 8 | 4 - 200 | `$19.99` |
| gb | yes | 5 | 6.99 - 200 | `£19.99` |
| de | yes | 5 | 7.99 - 229 | `22,99 €` |
| fr | yes | 5 | 7.99 - 229 | `22,99 €` |
| br | yes | 5 | 39.90 - 999.90 | `R$ 99,90` |
| jp | yes | 5 | 1400 - 30000 | `¥3,000` |

Note that the IAP catalogue itself genuinely differs per storefront - 8 items in the US, 5 in Germany - which is a real finding about how the publisher prices per market, not a scraping artefact.

#### Knowing when "no" means no

Every row carries **`inAppPurchaseSource`**:

- `storefront-page` - the page was read successfully, so `hasInAppPurchases` is a real answer either way.
- `unavailable` - the page could not be read, so `hasInAppPurchases` is `null`, not `false`.
- `not-requested` - you turned the option off.

A `false` that came from a failed fetch is indistinguishable from a real `false` unless the row says which it is. Now it does.

Apple shows at most ten IAP items on a storefront page, so `inAppPurchaseCount` reflects what the page lists rather than the app's full internal catalogue. That is a limit of the source, stated here rather than buried.

This costs one extra HTTP request per app and is billed as a separate event - and only when the page was actually read. Turn it off with `includeInAppPurchases: false` if you only need metadata.

### What you get per app

Every field below was present on **11 out of 11** apps in a live fill-rate measurement, except where noted.

| Field | What it holds |
|---|---|
| `appId` / `bundleId` | Numeric ID and iOS bundle identifier |
| `appName` | Title in this storefront |
| `country` | Storefront the row describes |
| `developer` / `developerId` / `developerUrl` | Publisher and their Apple artist page |
| `sellerName` / `sellerUrl` | Legal seller and their website (`sellerUrl` 10/11) |
| `price` / `currency` / `priceFormatted` / `isFree` | Up-front price in storefront currency. `price: 0` means free - it is a value, not a missing field, and `isFree` is derived from it, never from the localized label ("Gratis", "Grátis", "無料") |
| `hasInAppPurchases` | Boolean, from the storefront page |
| `inAppPurchases` | Array of `{ name, price, priceValue }` - the displayed string and the parsed number |
| `inAppPurchaseCount` | How many items were listed |
| `inAppPurchaseMinPrice` / `inAppPurchaseMaxPrice` | Parsed numeric range, locale-aware |
| `inAppPurchaseSource` | Where the IAP answer came from, so a `false` is never a failed read |
| `averageUserRating` / `userRatingCount` | All-time rating and rating volume |
| `averageUserRatingCurrentVersion` / `userRatingCountCurrentVersion` | The same, for the current build only |
| `version` | Current version string |
| `currentVersionReleaseDate` | When the current build shipped |
| `releaseNotes` | The "What's New" text |
| `originalReleaseDate` | First launch date |
| `daysSinceLastUpdate` | Computed, for sorting abandoned apps to the top |
| `primaryGenre` / `genres` | Category and full category list |
| `contentRating` / `advisories` | Age rating and Apple's content warnings |
| `minimumOsVersion` | Minimum iOS required |
| `fileSizeMb` | Download size |
| `languages` / `languageCount` | Supported language codes |
| `supportedDevices` | Device compatibility list |
| `iconUrl` / `screenshotUrls` / `appUrl` | Creative assets and store link |
| `description` | Full store description |

The two rating pairs are worth separating in your head. `averageUserRating` is the all-time number a user sees. `averageUserRatingCurrentVersion` is the current build's number - and when the second is well below the first, you are looking at a release that went wrong.

### Input

```json
{
  "appIds": [
    "6448311069",
    "com.burbn.instagram",
    "https://apps.apple.com/us/app/id310633997"
  ],
  "countries": ["us", "gb", "jp"],
  "includeInAppPurchases": true,
  "includeDescription": true,
  "includeScreenshots": false,
  "maxItems": 200
}
```

All three identifier forms work and can be mixed. Bundle IDs are resolved through Apple's lookup endpoint automatically.

**Input aliases.** `appIds` also accepts `ids`, `appId`, `apps`, `appUrls`, `bundleIds` and `urls`. `countries` also accepts `country`, `countryCodes` and `storefronts`. `includeInAppPurchases` also accepts `includeIap` and `withIap`. `maxItems` also accepts `maxResults` and `limit`. Comma separated strings work anywhere a list does.

### Competitor monetisation teardown

This is the actor's strongest use. Put ten competitor app IDs in and read the **Monetization** dataset view. In one table you get, for the whole competitive set: who is free and who charges up front, who sells subscriptions and at what price points, the cheapest and most expensive item each one offers, and the full IAP menu for each.

Pricing strategy in a category is normally reverse-engineered by installing ten apps and tapping through their paywalls. Here it is one run.

`inAppPurchaseMinPrice` and `inAppPurchaseMaxPrice` are parsed numerics, so sorting the set by price ceiling takes one click.

### Release cadence analysis

The **Release cadence** view pairs `version`, `currentVersionReleaseDate`, `daysSinceLastUpdate`, `originalReleaseDate` and `releaseNotes`.

Sort a competitive set by `daysSinceLastUpdate` descending and the abandoned apps float to the top. An app with strong ratings, meaningful rating volume and 400 days since its last build is a category position nobody is defending. That is a specific, actionable finding, and it comes from one column.

Read the other end of the sort too: `releaseNotes` across a set of actively shipping competitors is a public roadmap that they publish themselves.

### Price and availability across storefronts

Run the same app IDs across `["us","gb","de","jp","br","in"]`. Each country returns its own row with local `price`, `currency`, `priceFormatted` and local rating counts. An app that is not sold in a storefront is recorded in `FAILURES` as unavailable there, which is itself the answer to "have they launched in this market".

For a company doing international pricing this is the whole research task in one run.

### App Store listing audit

`languages` and `languageCount` tell you how many locales a competitor has localised for. `screenshotUrls` gives you their creative set for teardown. `advisories` and `contentRating` show how they positioned the app for age gating. `minimumOsVersion` shows how aggressively they drop old devices, which is a proxy for how much engineering budget they have.

### How this actor behaves when something goes wrong

1. **Errors never enter the dataset.** An app unavailable in a storefront, a bundle ID that does not resolve, a value that is not an app identifier at all - each is recorded in a `FAILURES` key-value record with the exact reason per input. None becomes a dataset row, because a row that says "error" would bill you for receiving an error.
2. **A run that delivered nothing is marked FAILED**, not SUCCEEDED.
3. **A failed IAP page never breaks the row.** If the storefront page cannot be read, the metadata row is still delivered with the IAP fields left null, and the IAP event is not charged for it.
4. **Metadata is fetched in batches** of up to 50 apps per HTTP request, so a 200-app run is fast and gentle on the source.
5. **The free tier returns data.** Proxy is optional and off by default.

### Pricing

Pay per event.

- **$0.001 per app delivered** - $1.00 per 1,000 apps.
- **$0.001 per in-app purchase lookup**, charged only when `includeInAppPurchases` is on and only for apps whose IAP data actually came back.

No subscription, no charge for starting a run. A 50-app competitive teardown with IAP data costs **$0.10**. `maxItems` is a hard ceiling on both rows and spend.

### Output sample

```json
{
  "appId": "6448311069",
  "bundleId": "com.openai.chat",
  "appName": "ChatGPT",
  "country": "us",
  "developer": "OpenAI",
  "developerId": "1684349733",
  "sellerName": "OpenAI OpCo, LLC",
  "price": 0,
  "currency": "USD",
  "priceFormatted": "Free",
  "isFree": true,
  "hasInAppPurchases": true,
  "inAppPurchases": [
    { "name": "ChatGPT Plus", "price": "$19.99" },
    { "name": "ChatGPT Go", "price": "$8.00" },
    { "name": "100 Credits", "price": "$4.00" }
  ],
  "inAppPurchaseCount": 8,
  "inAppPurchaseMinPrice": 4,
  "inAppPurchaseMaxPrice": 200,
  "averageUserRating": 4.8303,
  "userRatingCount": 9552259,
  "version": "1.2026.224",
  "currentVersionReleaseDate": "2026-08-22T01:20:24.000Z",
  "releaseNotes": "Bug fixes and performance improvements",
  "originalReleaseDate": "2023-05-18T07:00:00.000Z",
  "daysSinceLastUpdate": 0,
  "primaryGenre": "Productivity",
  "contentRating": "12+",
  "minimumOsVersion": "17.0",
  "fileSizeMb": 312.44,
  "languages": ["EN", "ES", "FR", "DE", "JA", "PT", "IT"],
  "languageCount": 7,
  "appUrl": "https://apps.apple.com/us/app/chatgpt/id6448311069",
  "scrapedAt": "2026-08-22T04:17:44.902Z"
}
```

### Dataset views

- **Overview** - app, developer, country, rating, rating count, price, IAP flag, version, last update.
- **Monetization** - free flag, price, IAP count, cheapest and priciest item, and the full IAP list.
- **Release cadence** - version, last update, days since, first release, release notes, minimum iOS, size.

### Frequently asked questions

**Are the in-app purchase prices real?** Yes - read from the app's own storefront page, item by item, in the storefront's currency, and verified across six storefronts. Both the displayed string and a parsed number are returned so you can check the parse yourself.

**Does it work on non-English storefronts?** Yes. The section label and the decimal separator are both localized, and both are handled. See the IAP section above.

**`hasInAppPurchases` is false - can I trust it?** Check `inAppPurchaseSource`. If it says `storefront-page`, yes. If it says `unavailable`, the page could not be read and the field will be `null` rather than `false`.

**Why do I only see up to ten IAP items?** That is how many Apple lists on the storefront page. It is the source's limit, not the actor's.

**Do I need App Store Connect access?** No. Everything here is public. It works on apps you do not own, which is the point.

**Can I mix ID formats?** Yes. Numeric IDs, store URLs and bundle IDs in the same `appIds` list.

**What happens if an app is not sold in a country?** That country is recorded in `FAILURES` with the reason, the other countries still return rows, and you are not billed for it.

**Why is `sellerUrl` sometimes empty?** Some publishers do not provide one. Measured at 10 of 11 apps.

**Can I schedule it?** Yes. Watching `version`, `daysSinceLastUpdate` and IAP price changes over time is a genuine competitive intelligence feed - though if version tracking is all you need, the App Update Monitor is cheaper for that job.

### Related actors

- **App Update Monitor** - new versions and release notes across iOS and Android, with change detection.
- **App Store Reviews Scraper** - customer reviews across every storefront.
- **ASO Keyword Rank Tracker** - App Store search positions for a keyword and app.
- **App Store Top Charts Scraper** - Free, Paid and Grossing charts by country and category.

# Actor input Schema

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

Numeric app IDs, apps.apple.com URLs, or iOS bundle IDs. Mixed input is fine. Aliases: ids, appId, apps, appUrls, bundleIds.

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

Two letter iTunes storefronts. Price, availability and rating counts are per storefront. Aliases: country, countryCodes.

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

The IAP name and price list is not in Apple's lookup API. Enabling this fetches the storefront page for each app to read it. Verified on 6 storefronts (us, gb, de, fr, br, jp). Prices are parsed locale-aware, so a European 22,99 reads as 22.99 and not 2299. Costs one extra event per app.

## `includeDescription` (type: `boolean`):

Include the full description.

## `includeScreenshots` (type: `boolean`):

Include screenshot URLs.

## `maxItems` (type: `integer`):

Hard ceiling on apps delivered and therefore on what you are charged.

## `proxyConfiguration` (type: `object`):

Not required.

## Actor input object example

```json
{
  "appIds": [
    "6448311069",
    "com.burbn.instagram",
    "https://apps.apple.com/us/app/id310633997"
  ],
  "countries": [
    "us"
  ],
  "includeInAppPurchases": true,
  "includeDescription": true,
  "includeScreenshots": true,
  "maxItems": 200,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per app: version, release notes, ratings, price, size, languages and in-app purchases.

# 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": [
        "6448311069",
        "com.burbn.instagram",
        "https://apps.apple.com/us/app/id310633997"
    ],
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("renzomacar/app-store-app-details-scraper").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": [
        "6448311069",
        "com.burbn.instagram",
        "https://apps.apple.com/us/app/id310633997",
    ],
    "countries": ["us"],
}

# Run the Actor and wait for it to finish
run = client.actor("renzomacar/app-store-app-details-scraper").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 '{
  "appIds": [
    "6448311069",
    "com.burbn.instagram",
    "https://apps.apple.com/us/app/id310633997"
  ],
  "countries": [
    "us"
  ]
}' |
apify call renzomacar/app-store-app-details-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/9r8bjiLngQtydV5va/builds/jW2C5mOU2TksMUavp/openapi.json
