# App Store & Google Play App Details + Download Estimates (`openkrill/app-store-app-details`) Actor

Give App Store or Google Play ids or links, get one row per app: name, developer, rating, rating and review counts, price, category, last update, Play installs, plus a monthly download and revenue range marked as an estimate. Public store sources only. $2 per 1,000 apps, failed apps free.

- **URL**: https://apify.com/openkrill/app-store-app-details.md
- **Developed by:** [Nguyen Vu Trung Hieu](https://apify.com/openkrill) (community)
- **Categories:** Developer tools, Marketing, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 app returneds

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

### What does App Store & Google Play App Details do?

Give it App Store or Google Play apps by id or link, and it returns one row per app with the store's public details and our download and revenue estimate.
Both stores share one output schema, so a mixed list of iPhone and Android apps comes back as one table.

Use it for competitor tracking, ASO research, app market sizing, investor and M\&A screening, lead lists of app publishers, and AI agents that need app facts in one call.

Price: **$2.00 per 1,000 apps returned**.
Apps that are not listed, invalid input and store pages that come back without their details are free.

### What data do you get for each app?

#### Store facts

- `name`, `developer`, `category`, `url` and `icon`.
- `rating` (average stars, 2 decimals) and `ratingCount` (number of star ratings) on both stores.
- `reviewCount`: written reviews, on Google Play only. The App Store's public lookup publishes rating counts, not review counts, so it is `null` there.
- `price`, `currency` and `free`.
- `releaseDate`, `lastUpdated` and `version`.
- Google Play only: `installs` (the exact install count behind the "1B+" band), `installsText` and `inAppPriceRange`.

#### Estimates

Every field whose name starts with `estimated` is our estimate, not a figure the store or the developer published.

- `estimatedMonthlyDownloadsLow` and `estimatedMonthlyDownloadsHigh`: a monthly download range.
  On Google Play it is the exact install count over the app's age, plus or minus 30%.
  On the App Store it is the app's ratings per day over its life, times 30 to 50 downloads per rating; this understates apps that are growing fast.
- `estimatedMonthlyRevenueLow` and `estimatedMonthlyRevenueHigh` in `estimateCurrency`: a monthly revenue range when a price is known, from the app price or the low end of Google Play's in-app price range, assuming 1.5 to 3.5% of downloads pay for 3.5 to 4.5 months.
  Free App Store apps get no revenue range, because their in-app prices are not in a public source we read.
- `estimateBasis` (`installs` or `ratings`) and `estimateNote` say how each range was made.

The estimates come from the engine behind our [App Store Opportunity Tracker](https://tools.openkrill.app/), which also scores chart apps and tracks their movement every day.

#### Status

`status` is `ok` on every billed row.
Any other status is a free row with the reason in `error`: `invalid_input`, `not_found`, `rate_limited`, `upstream_error`, `source_off` (the store's robots.txt does not allow the read) or `parse_error`.
A `parse_error` row lists the missing fields in `missing`.

### Input

| Field | Description |
| --- | --- |
| `apps` | One app per line: an App Store link or numeric id (`310633997` or `id310633997`), or a Google Play link or package name (`com.spotify.music`). Both stores can be mixed. Up to 1,000 apps; the same app twice is read and billed once. |
| `country` | Two-letter store country, default `us`. Rating, price and currency are the ones that store shows in that country. |
| `store` | `auto` (default) reads App Store links and numeric ids as App Store apps and everything else as Google Play. Pick `apple` to read bundle ids such as `com.burbn.instagram` on the App Store. |

```json
{
  "apps": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997",
    "570060128",
    "https://play.google.com/store/apps/details?id=com.spotify.music",
    "com.duolingo"
  ],
  "country": "us"
}
```

### Example output

A Google Play row, from a run on 2026-10-25 (icon URL cut):

```json
{
  "status": "ok", "input": "com.duolingo", "store": "play", "appId": "com.duolingo", "country": "us",
  "url": "https://play.google.com/store/apps/details?id=com.duolingo",
  "name": "Duolingo: Learn Languages", "developer": "Duolingo", "category": "Education",
  "rating": 4.75, "ratingCount": 51282112, "reviewCount": 966953, "price": 0, "currency": "USD", "free": true,
  "releaseDate": "2013-05-29", "lastUpdated": "2026-10-20", "version": null,
  "installs": 988483551, "installsText": "500,000,000+", "inAppPriceRange": "$0.99 - $239.99 per item",
  "icon": "https://play-lh.googleusercontent.com/tw_coGKgk1K...",
  "estimatedMonthlyDownloadsLow": 4238953, "estimatedMonthlyDownloadsHigh": 7872342,
  "estimatedMonthlyRevenueLow": 220320, "estimatedMonthlyRevenueHigh": 1227495, "estimateCurrency": "USD",
  "estimateBasis": "installs",
  "estimateNote": "Estimate: lifetime exact installs over app age, plus or minus 30 percent; revenue assumes 1.5 to 3.5 percent of downloads pay.",
  "missing": [], "error": null, "fetchedAt": "2026-10-25T07:58:17.949Z"
}
```

Google Play shows no version for apps whose version varies with the device, so `version` can be `null` there.

An App Store row from the same run (icon URL cut):

```json
{
  "status": "ok", "input": "479516143", "store": "apple", "appId": "479516143", "country": "us",
  "url": "https://apps.apple.com/us/app/id479516143",
  "name": "Minecraft: Play with Friends!", "developer": "Mojang AB", "category": "Games",
  "rating": 4.51, "ratingCount": 844029, "reviewCount": null, "price": 6.99, "currency": "USD", "free": false,
  "releaseDate": "2011-11-17", "lastUpdated": "2026-09-28", "version": "26.52",
  "installs": null, "installsText": null, "inAppPriceRange": null,
  "icon": "https://is1-ssl.mzstatic.com/image/thumb/Purple211/...",
  "estimatedMonthlyDownloadsLow": 139228, "estimatedMonthlyDownloadsHigh": 232046,
  "estimatedMonthlyRevenueLow": 973204, "estimatedMonthlyRevenueHigh": 1622002, "estimateCurrency": "USD",
  "estimateBasis": "ratings",
  "estimateNote": "Estimate: 30 to 50 downloads per rating; revenue is downloads times the app price.",
  "missing": [], "error": null, "fetchedAt": "2026-10-25T07:58:16.259Z"
}
```

A free row for an app the store does not list:

```json
{ "status": "not_found", "input": "com.nonexistent.zzzz.app", "store": "play", "appId": "com.nonexistent.zzzz.app",
  "country": "gb", "error": "Google Play app com.nonexistent.zzzz.app is not listed in GB.", "missing": [] }
```

### What it reads, and what it does not

It reads only public store sources whose robots.txt and terms allow it:

- The App Store side uses Apple's published iTunes lookup (`itunes.apple.com/lookup`), many ids per request.
- The Google Play side reads the app's public details page (`play.google.com/store/apps/details`), which no robots.txt line covers.

It does not read Apple's web catalog API, Apple search pages or Google Play's internal RPC channel, because their robots.txt disallows them.
That is why App Store rows have no review count, in-app price list or rating split.

Every run reads each host's robots.txt before anything else and skips a path it disallows, as RFC 9309 says.
It sends one request at a time to each host, at least 1 second apart, and backs off when a store asks it to.
That makes it about 1 Google Play app per second, and about 50 App Store apps per second.
It needs no login, no proxy and no key.

If a store changes its format, a row that loses a paid field (name, developer, category, last update, price, rating count, rating, review count or installs) comes back as a free `parse_error` row.
The run fails with a message once more than 1 in 10 rows of a store are like that, so you never pay for blank details.
Google Play apps with no public ratings yet (new and early-access apps) also come back as free `parse_error` rows, because their page looks the same as a broken one; the `error` says so.

### More Actors from OpenKrill

- [Public Job Board Scraper](https://apify.com/openkrill/public-job-board-scraper): jobs from Greenhouse, Lever, Ashby, Workday and dozens more hiring systems in one schema.
- Need reviews too? [Google Play & App Store Reviews](https://apify.com/openkrill/app-store-play-reviews) returns review text, ratings and dates for the same apps.

### Questions

Open an issue on the Actor's Issues tab; we answer within 2 working days.

# Actor input Schema

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

One app per line: an App Store link or numeric id (310633997 or id310633997), or a Google Play link or package name (com.spotify.music). Both stores can be mixed. An App Store bundle id works with Store set to App Store. Up to 1,000 apps per run; the same app twice is read and billed once.

## `country` (type: `string`):

Two-letter store country (us, gb, de, jp...). Rating, price and currency are the ones that store shows in that country.

## `store` (type: `string`):

Auto reads App Store links and numeric ids as App Store apps, everything else as Google Play. Pick App Store to read bundle ids such as com.burbn.instagram on the App Store.

## Actor input object example

```json
{
  "apps": [
    "https://apps.apple.com/us/app/whatsapp-messenger/id310633997",
    "570060128",
    "https://play.google.com/store/apps/details?id=com.spotify.music",
    "com.duolingo"
  ],
  "country": "us",
  "store": "auto"
}
```

# Actor output Schema

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

No description

## `estimates` (type: `string`):

No description

## `dataset` (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": [
        "https://apps.apple.com/us/app/whatsapp-messenger/id310633997",
        "570060128",
        "https://play.google.com/store/apps/details?id=com.spotify.music",
        "com.duolingo"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("openkrill/app-store-app-details").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/whatsapp-messenger/id310633997",
        "570060128",
        "https://play.google.com/store/apps/details?id=com.spotify.music",
        "com.duolingo",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("openkrill/app-store-app-details").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/whatsapp-messenger/id310633997",
    "570060128",
    "https://play.google.com/store/apps/details?id=com.spotify.music",
    "com.duolingo"
  ]
}' |
apify call openkrill/app-store-app-details --silent --output-dataset

```

## MCP server setup

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

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/QNGeFBN0tATe6VvLs/builds/bmXr6Wq4U8iPUgx0K/openapi.json
