# Apple App Store Search & Keyword Ranking (`steadydata/app-store-search`) Actor

Search the App Store for up to 200 keywords per run and get the ranked results with full listing data: position, title, developer, rating, price, genre, version and more, per storefront. Or track your own apps' keyword ranking. Pay only for delivered rows.

- **URL**: https://apify.com/steadydata/app-store-search.md
- **Developed by:** [Steadydata Team](https://apify.com/steadydata) (community)
- **Categories:** E-commerce, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 app rankeds

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 Keyword Ranking & Search Scraper

Search the Apple App Store for **up to 200 keywords per run** and get the ranked results
with the **full listing data** for every app: rank position, title, developer, rating and
rating count, price, genre, age rating, version, dates, size, languages, description and
artwork. Per storefront, for iPhone, iPad or Mac apps. Or flip it around and **track your
own apps' rank** for a keyword list. **You only pay for rows that are actually delivered.**

### Why this scraper

- **Search and details in one call.** Other search scrapers give you a list of app ids
  and leave you to fetch the details separately. Every row here is a complete listing
  record, with the same field names as the App Store App Details Scraper from this
  publisher, so the two datasets join field by field.
- **Rank tracking built in.** Put your own app ids or bundle ids in `trackApps` and each
  keyword returns only your apps, with their position. An app that does not rank in the
  fetched range comes back with an empty position, which is the answer a rank tracker
  needs.
- **Apple's own ranking.** The order is the App Store's, per storefront and per device
  type, not a re-sorted approximation.
- **Fast and light.** One request per keyword through Apple's official search API: in
  testing on 13-09-2026 a keyword returned 47 apps in about 1.5 seconds.
- **Only delivered rows are charged.** A keyword with no results comes back as a clear
  error record at no cost. No start fee.

### Who this is for

ASO and app marketing teams tracking keyword rank for their own apps and their
competitors'. Analysts mapping who ranks for a category, per country. Anyone building an
app dataset who wants search and listing data in one pass.

### Who this is not for

Apple returns at most **190 apps per keyword**; that is its ceiling, not a setting. There
is no search volume in this data (Apple does not publish it) and no install counts for
iOS apps, so `ratings` is the size signal. Reviews are not included: they carry reviewer
names, which this publisher does not collect. Autocomplete suggestions are not part of
this actor.

### Input example

```json
{
    "keywords": ["fitness tracker", "habit tracker", "calorie counter"],
    "country": "us",
    "language": "",
    "device": "iphone",
    "maxResultsPerKeyword": 50,
    "trackApps": []
}
```

Rank tracking for your own apps:

```json
{
    "keywords": ["fitness tracker", "habit tracker"],
    "country": "us",
    "device": "iphone",
    "maxResultsPerKeyword": 100,
    "trackApps": ["com.myfitnesspal.mfp", "341232718"]
}
```

### Output example

```json
{
    "keyword": "fitness tracker",
    "position": 1,
    "appId": 341232718,
    "bundleId": "com.myfitnesspal.mfp",
    "url": "https://apps.apple.com/us/app/myfitnesspal/id341232718?uo=4",
    "title": "MyFitnessPal: Calorie Counter",
    "developer": "MyFitnessPal, Inc.",
    "score": 4.71,
    "ratings": 2356837,
    "price": 0,
    "priceText": "Free",
    "currency": "USD",
    "isFree": true,
    "genre": "Health & Fitness",
    "contentRating": "4+",
    "version": "26.36.0",
    "minimumOsVersion": "16.0",
    "updated": "2026-09-08T10:12:00Z",
    "languages": ["EN", "NL", "DE"],
    "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/.../512x512bb.jpg",
    "country": "US",
    "device": "iphone",
    "status": "ok"
}
```

A tracked app that does not rank for a keyword:

```json
{"keyword": "fitness tracker", "position": null, "bundleId": "com.nowhere.app", "title": null, "country": "US", "status": "ok"}
```

A keyword that returns nothing produces an error record instead, and is **not** charged:

```json
{
    "input": "zzzzqqqq",
    "status": "error",
    "errorCode": "NO_RESULTS",
    "error": "No apps for 'zzzzqqqq' in the US App Store"
}
```

Error codes: `INVALID_KEYWORD`, `NO_RESULTS`, `BLOCKED`. `INPUT_TRUNCATED` appears once when your input is longer than this actor accepts.

### Related actors from steadydata

- [app-store-app-details](https://apify.com/steadydata/app-store-app-details): the full record of an app you found
- [google-play-app-details](https://apify.com/steadydata/google-play-app-details): the Google Play side

### Pricing

Pay per event: one `app-ranked` event per delivered row. A tracked app reported as not
ranked is one row, because that is the answer you asked for. No charge for keywords that
fail, no start fee, no separate platform-usage surcharge. `maxResultsPerKeyword` is your
hard cost ceiling per keyword.

**Free Apify plan:** this actor delivers up to 25 rows per run for accounts on the Apify free
plan, and then stops with a message. That limit is set by us, not by Apify. It exists so the
actor keeps paying for itself for the people who do pay. Any paid Apify plan runs it at full
size, billed per delivered row, with failed rows never charged.

**Reviews:** if this actor saves you time, a short review on this page is the one thing that
helps most. Ratings are what other buyers look at first, and we have no other way to ask.

### FAQ

**Why does rank tracking cost as much per app as a full search row?**
Because it is the same call and the same work; you simply pay for far fewer rows. Tracking
3 apps across 50 keywords is 150 rows, where the full search would be 5,000.

**Does the rank match what I see in the App Store app?**
It is the storefront ranking Apple's search API returns for that term, which is what ASO
tools use. The app on a phone personalises somewhat, so a single device can differ.

**Can I search iPad or Mac apps?**
Yes, set `device`. Each device type has its own ranking.

**How fast does a large keyword list run?**
Apple documents a rate limit of roughly 20 calls per minute for its search API, and this actor respects it: one keyword every three seconds. A 200-keyword run therefore takes about ten minutes. That is a deliberate choice; hammering the API would get the run blocked instead.

**Do nonsense keywords cost me money?**
Apple matches loosely, so even a typo usually returns a few apps rather than nothing; those are delivered rows. Keep `maxResultsPerKeyword` at the depth you actually need and check your keyword list before a large run.

**Is there search volume or a difficulty score?**
No. Apple does not publish either. Combine this with the Google Trends actors from this
publisher if you need a demand signal.

**Is personal data collected?**
No. Developer names are companies; no contact data, no reviews, no reviewer names.

**What happens when Apple changes something?**
The search API has been stable for years and is versioned by Apple. The actor is monitored
daily and fixed fast, and while it is broken you are not charged, because only delivered
rows cost anything.

# Changelog

This Actor's version history is a separate document: https://apify.com/steadydata/app-store-search/changelog.md

# Actor input Schema

## `keywords` (type: `array`):

Search terms, one per row, up to 200. Each keyword is one App Store search; a keyword without results comes back as a free error record.

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

Two-letter storefront (us, gb, nl, de ...). Rankings, prices and availability differ per storefront.

## `language` (type: `string`):

Listing language as Apple codes it, e.g. en\_us, nl\_nl, de\_de. Leave empty for the storefront default.

## `device` (type: `string`):

Which App Store to search: iPhone, iPad or Mac apps.

## `maxResultsPerKeyword` (type: `integer`):

Cost ceiling per keyword, in Apple's ranking order. Apple returns at most 190 apps per search. One delivered row is one charged event.

## `trackApps` (type: `array`):

Optional. App ids or bundle ids of your own apps: only their rows are delivered, with the rank position per keyword. An app that does not rank in the fetched range is delivered with an empty position, which is the answer a rank tracker needs, and counts as one row.

## Actor input object example

```json
{
  "keywords": [
    "fitness tracker",
    "habit tracker"
  ],
  "country": "us",
  "language": "",
  "device": "iphone",
  "maxResultsPerKeyword": 50,
  "trackApps": []
}
```

# Actor output Schema

## `results` (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 = {
    "keywords": [
        "fitness tracker",
        "habit tracker"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadydata/app-store-search").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 = { "keywords": [
        "fitness tracker",
        "habit tracker",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("steadydata/app-store-search").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 '{
  "keywords": [
    "fitness tracker",
    "habit tracker"
  ]
}' |
apify call steadydata/app-store-search --silent --output-dataset

```

## MCP server setup

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

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/uMXsQZxwCvpaMIcfc/builds/AhrTfq9yQQydEzZxj/openapi.json
