# App Store Scraper: App Details, Reviews, Search & Top Charts (`everyotherfriday/app-store`) Actor

Everything public about an iOS app in clean rows: ratings, price, version history notes, screenshots, developer and genre data, plus up to 500 recent reviews per country storefront and chart positions. Loop countries for global review coverage. Built for ASO, competitor tracking and app research.

- **URL**: https://apify.com/everyotherfriday/app-store.md
- **Developed by:** [Paul Vasquez](https://apify.com/everyotherfriday) (community)
- **Categories:** Developer tools, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 66.7% 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.

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

## Apple App Store Suite

Collect public Apple App Store metadata, keyword search results, chart entries,
and customer reviews without an API key. This Python 3.12 actor uses Apple's
iTunes lookup and search APIs, Apple Marketing Tools charts, and the XML customer
review feeds. Optional public product page enrichment looks for additional data
inside embedded JSON. It is useful for storefront comparisons, app research,
review monitoring, and small competitive datasets. Results describe the public
storefront at collection time; this is not an installation or revenue estimator.

### Quick start

Install Python 3.12, create a virtual environment, and install requirements:

```powershell
python -m venv .venv
.venv/Scripts/python.exe -m pip install -r requirements.txt
apify validate-schema .actor/input_schema.json
apify run
```

The included INPUT.json looks up WhatsApp and Facebook in the US and GB
storefronts and requests up to 50 reviews per app per storefront. It needs no
keys and is intended for the daily automated smoke test, normally completing
within two minutes. The validation harness runs that input plus the requested
budget search and top-free chart checks with isolated local storage:

```powershell
.venv/Scripts/python.exe -m unittest discover -s tests -v
powershell -File validation/run_live.ps1
```

The harness launches the actual Apify SDK, saves logs and complete JSON results
under validation, and checks counts, uniqueness, and eligible event counts.
It does not push or publish the actor. Local SDK charges do not bill money.

### Inputs and modes

`mode` selects lookup, search, charts, or reviews; lookup is the default.
For lookup and reviews, supply `appIds` as strings containing numeric Apple
IDs, bundle identifiers, or HTTPS apps.apple.com product URLs. Bundle IDs are
resolved through lookup before fetching reviews. Unsupported identifiers produce
free error rows while other inputs continue. Search accepts `queries`, an array
of phrases, and requests one page of up to 200 candidates per query and country.
Apple controls relevance and may return fewer candidates than requested.

Charts accepts `chart` as top-free or top-paid. The actor fetches the overall
top 100 chart and hydrates selected IDs through lookup so chart app rows have
the same metadata fields as other modes. Optional `genreId` filters search and
filters chart entries within that overall top 100; it does not request a separate
genre chart. No ranking beyond the fetched page is inferred.

`countries` defaults to \["us"]. Codes are normalized to lowercase and duplicate
codes removed. `maxApps`, default 100, is a global cap on unique app/storefront
pairs, including resolved apps in reviews mode. Input order determines which
pairs fit. The same app in two storefronts consumes two slots. Search results
and lookup aliases are deduplicated within each storefront.

Set `includeReviews` to true to attach review collection to app discovery.
Reviews mode emits review rows without app rows. `maxReviewsPerApp` defaults
to 100 and accepts 1 through 500 per app per review country. `reviewCountries`
defaults to countries; supply additional storefronts to collect more than 500
reviews across countries. Each feed exposes at most ten pages, usually 50
reviews per page. The XML variant is intentional: Apple's JSON variant can
return empty feeds. Repeated review IDs are removed per app and storefront;
duplicate-only or empty pages stop pagination. These feeds are recent snapshots,
not a complete historical archive.

### Output and enrichment

Every dataset row has `rowType` and `source`. App rows include IDs, name,
developer and seller, price and currency, category and genres, overall and
current-version ratings and counts, version and dates, release notes, description,
size, minimum OS, content rating, languages, screenshots, icon, URL, and country.
Unavailable API fields remain null or empty arrays. Size retains Apple's API
representation, generally a byte-count string.

Review rows contain appId, country, reviewId, author, rating, title, content,
version, updatedAt, voteCount, and voteSum. Page rows identify the search query
or chart and report candidate count before the global cap and deduplication.
SUMMARY in the default key-value store records row counts, event counts, and
processing time. Errors and empty results appear as separate free rows.

`includePageDetails` defaults to false. When enabled, the actor searches embedded
JSON for privacy labels, in-app purchases, rating histograms, and screenshots.
These additional fields preserve Apple's nested structures. Missing or changed
page structures leave details unavailable; they do not imply no tracking or no
purchases. Fetch failures add a warning without discarding the API app row.

### Pricing and reliability

The custom PPE prices are $0.002 per app-returned, $0.0002 per review-returned,
and $0.005 per page-returned. Each successful emitted row requests one matching
event through Actor.charge. Empty search/chart results are free summary rows.
Errors and zero-result summaries are uncharged. Configure these events in Console
and disable synthetic events before publication; the metadata file alone does
not activate Store pricing. A refused charge stops output. Charging precedes
persistence, so a storage failure cannot be rolled back transactionally.

Requests retry twice with one- and two-second backoff on 429, server errors,
and transport failures. Other HTTP errors fail immediately. `timeoutSecs` bounds
each HTTP operation, not the whole run. Large country lists and optional details
increase runtime. Public endpoints can throttle or change. See VALIDATION.md
for measured results and the distinction between mocked, live, and hosted checks.

### Example output

One recorded dataset row, trimmed by omitting fields only. Values are the saved snapshot, not current measurements. Source: [validation/results-lookup.json](validation/results-lookup.json), first row in the rows array.

```json
{
  "rowType": "app",
  "appId": "310633997",
  "bundleId": "net.whatsapp.WhatsApp",
  "name": "WhatsApp Messenger",
  "country": "us",
  "price": 0.0,
  "currency": "USD",
  "version": "26.37.76"
}
```

### Use cases

- A mobile product manager can collect recent competitor reviews and group complaints by app version before prioritizing customer interviews.
- An app publisher can compare the same app across US and GB storefronts, preserving country alongside prices, currencies, and rating counts.
- An acquisition analyst can search a category keyword and assemble an app shortlist for manual review of developers and product pages.
- An app marketing agency can save top-free chart snapshots and compare returned app IDs across runs in its own reporting database.

### Pricing example

Hypothetical batch, calculated from [`.actor/pay_per_event.json`](.actor/pay_per_event.json):

| Event | Count | USD per event | Subtotal |
| --- | ---: | ---: | ---: |
| `app-returned` | 100 | $0.002 | $0.2000 |
| `review-returned` | 1,000 | $0.0002 | $0.2000 |
| `page-returned` | 10 | $0.005 | $0.0500 |

Total declared event charges: **$0.45**. These counts are a budgeting example, not a promised yield or an actual bill. Any applicable platform or proxy costs are outside this calculation.

### Limitations

Review feeds expose recent pages, not complete lifetime feedback. Chart genre filtering operates inside the fetched overall top 100. Optional page details may be absent even when an app lookup succeeds.

# Actor input Schema

## `mode` (type: `string`):

Operation to perform.

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

Numeric IDs, bundle IDs or Apple App Store URLs.

## `queries` (type: `array`):

Search phrases; one page of up to 200 candidates each.

## `chart` (type: `string`):

Overall chart; genre filtering applies within its top 100.

## `genreId` (type: `string`):

Optional numeric Apple genre identifier.

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

Two-letter storefront codes.

## `maxApps` (type: `integer`):

Global cap on unique app/storefront pairs.

## `includeReviews` (type: `boolean`):

Fetch reviews for resolved apps.

## `maxReviewsPerApp` (type: `integer`):

Maximum reviews per app per review storefront.

## `reviewCountries` (type: `array`):

Review storefronts; defaults to countries when omitted.

## `includePageDetails` (type: `boolean`):

Best-effort public page embedded JSON extraction.

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

HTTP operation timeout in seconds.

## Actor input object example

```json
{
  "mode": "lookup",
  "appIds": [
    "310633997",
    "284882215"
  ],
  "chart": "top-free",
  "countries": [
    "us"
  ],
  "maxApps": 10,
  "includeReviews": false,
  "maxReviewsPerApp": 100,
  "includePageDetails": false,
  "timeoutSecs": 20
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `summary` (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 = {
    "appIds": [
        "310633997",
        "284882215"
    ],
    "maxApps": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("everyotherfriday/app-store").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": [
        "310633997",
        "284882215",
    ],
    "maxApps": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("everyotherfriday/app-store").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": [
    "310633997",
    "284882215"
  ],
  "maxApps": 10
}' |
apify call everyotherfriday/app-store --silent --output-dataset

```

## MCP server setup

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

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/xPdhC81XRFlNdgruz/builds/VoYkeaVQNrgawLcf0/openapi.json
