# App Store Search Scraper (`renzomacar/app-store-search-scraper`) Actor

Turn a keyword into the ranked list of App Store apps, with ratings, price, developer, category and version. Up to 200 results per keyword per country. No API key.

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

## Pricing

from $0.80 / 1,000 app results

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 Search Results Scraper - Keyword to Ranked Apps

Type a keyword, get the ranked list of App Store apps that Apple returns for it - up to 200 results per keyword per country - with rating, rating count, publisher, price, category, version, release dates and the store URL already attached. No API key, no developer account, no proxy.

This is the market map for a keyword. Where the ASO Keyword Rank Tracker answers "where does *my* app sit", this actor answers "who is in this space at all, and what do they look like".

### What you get per result

| Field | What it holds | Fill rate on live data |
|---|---|---|
| `keyword` / `country` | The search that produced this row | always |
| `rank` | Position in the result list, 1 upward | always |
| `resultsReturned` | How many results Apple actually returned for this search | always |
| `appId` / `bundleId` | Numeric App Store ID and iOS bundle ID | 11/11 |
| `appName` | App title as it appears in this storefront | 11/11 |
| `developer` / `developerId` | Publisher and their Apple artist ID | 11/11 |
| `price` / `currency` / `priceFormatted` / `isFree` | Pricing in storefront currency | 11/11 |
| `averageUserRating` | Rating average, four decimals | 11/11 |
| `userRatingCount` | Total ratings | 11/11 |
| `primaryGenre` / `genres` | Category and full category list | 11/11 |
| `contentRating` | Age rating | 11/11 |
| `version` | Current version string | 11/11 |
| `currentVersionReleaseDate` | When the current build shipped | 11/11 |
| `originalReleaseDate` | When the app first launched | 11/11 |
| `minimumOsVersion` | Minimum iOS required | 11/11 |
| `fileSizeMb` | Download size | 11/11 |
| `languages` / `languageCount` | Supported language codes | 11/11 |
| `iconUrl` / `appUrl` | Icon and store link | 11/11 |
| `description` | Full store description, optional | 11/11 |

Fill rates were measured across an 11-app live sample of Apple's metadata response. Every field listed is present on every row. There is nothing in that table that arrives "sometimes".

### Honesty about the result count

Apple's search endpoint accepts `limit` up to **200** and then returns **slightly fewer than asked**. Measured: a request for 200 came back with 189, 190, 192, 193 and 196 across different keywords and storefronts. A request for 50 came back with 45.

Every row therefore carries `resultsReturned` - the count Apple actually gave for that search. Nothing is padded and nothing is invented to hit the number you typed.

### Input

```json
{
  "keywords": ["habit tracker", "budget planner", "sleep sounds"],
  "countries": ["us", "gb", "de"],
  "limit": 200,
  "entity": "software",
  "includeDescription": false,
  "maxItems": 2000
}
```

**Input aliases.** `keywords` also accepts `keyword`, `terms`, `term`, `queries`, `query`, `search` and `searchTerms`. `countries` also accepts `country`, `countryCodes` and `storefronts`. `limit` also accepts `resultsPerKeyword` and `depth`. `maxItems` also accepts `maxResults` and `maxRows`. Comma separated strings work anywhere a list does.

#### Every input field

- **`keywords`** - what to search for. Required.
- **`countries`** - two letter iTunes storefronts. Results and ordering differ per storefront. Verified working on `us`, `gb`, `de`, `jp`, `br`, `in`, `pe` and `au`.
- **`limit`** - 1 to 200 results per keyword.
- **`entity`** - `software` for iPhone apps, `iPadSoftware` for iPad, `macSoftware` for Mac.
- **`lang`** - optional Apple language tag such as `de_de`, `es_es`, `ja_jp`. Changes the localised names and descriptions returned. Verified: `country=de&lang=de_de` returns German app titles.
- **`attribute`** - set to `softwareDeveloper` to search publisher names instead of app text.
- **`includeDescription`** - off by default. Descriptions run to several thousand characters each and will bloat a CSV export.
- **`maxItems`** - hard ceiling on rows delivered and on the bill.
- **`proxyConfiguration`** - optional, off by default.

### Competitor discovery for a keyword

Run one keyword at `limit: 200` and you have the complete addressable competitive set for that term, ranked. Sort by `userRatingCount` and the structure of the category appears immediately: a handful of incumbents with hundreds of thousands of ratings, a middle band, and a long tail of apps with under a thousand.

The middle band is usually the interesting one. Those are apps that found the keyword but have not yet won it.

### Finding underserved keywords

Two columns do most of this work.

Look for keywords where the top ten results have **low `userRatingCount`**. That is a term with search intent that no strong app has claimed. Then look at `currentVersionReleaseDate` in that same top ten - if the leaders last shipped a build a year ago, the category is not just weak, it is abandoned.

A keyword whose top ten averages 900 ratings and 14 months since last update is a market opening. That combination is two sorts away in the dataset this actor produces.

### Market sizing before you build

Before committing to an app idea, run twenty keywords a user might type to find it. The output answers, with data rather than intuition:

- How many apps already serve this intent, and how good are they?
- What do they charge? `isFree` and `priceFormatted` across the set gives you the pricing norm.
- How old are they? `originalReleaseDate` shows whether this is a settled category or a new one.
- Are they maintained? `currentVersionReleaseDate` shows who is still shipping.
- Who publishes them? Group by `developer` to spot categories dominated by one studio with fifteen near-identical apps.

### Multi-country keyword research

The same keyword returns a different ranked list in every storefront, and often different apps entirely. Running one keyword set across ten countries shows you where a category is mature and where it is not. An app idea saturated in the US may have four weak competitors in Brazil, and that difference is visible in a single dataset.

Use the `lang` parameter alongside `countries` when you want the localised names too.

### Searching by developer instead of by app text

Set `attribute: "softwareDeveloper"` and the keyword is matched against publisher names. This is how you enumerate a competitor's catalogue on the Apple side, or find every app published by a studio you are researching.

### How this actor behaves when something goes wrong

1. **Errors never enter the dataset.** A keyword returning zero results, or a storefront failing, is recorded in a `FAILURES` key-value record with the exact reason. It never 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. **Transient failures are retried** with backoff before being recorded.
4. **The free tier returns data.** No proxy is required and none is used unless you turn one on.

### Pricing

Pay per event. **$0.0002 per app result delivered** - $0.20 per 1,000 rows. No subscription, no charge for starting a run.

Ten keywords at 200 results across three countries is about 5,700 rows, or roughly **$1.14**. `maxItems` is a hard ceiling on both rows and spend.

### Output sample

```json
{
  "keyword": "habit tracker",
  "country": "us",
  "rank": 3,
  "resultsReturned": 191,
  "appId": "1477912538",
  "bundleId": "com.habitify.ios",
  "appName": "Habitify: Habit Tracker",
  "developer": "Unstatic Ltd",
  "developerId": "1445916138",
  "price": 0,
  "currency": "USD",
  "priceFormatted": "Free",
  "isFree": true,
  "averageUserRating": 4.7412,
  "userRatingCount": 21874,
  "primaryGenre": "Productivity",
  "genres": ["Productivity", "Health & Fitness"],
  "contentRating": "4+",
  "version": "12.4.1",
  "currentVersionReleaseDate": "2026-08-11T16:20:03.000Z",
  "originalReleaseDate": "2019-09-24T07:00:00.000Z",
  "minimumOsVersion": "16.0",
  "fileSizeMb": 84.12,
  "languages": ["EN", "ES", "FR", "DE", "JA"],
  "languageCount": 5,
  "iconUrl": "https://is1-ssl.mzstatic.com/image/thumb/.../512x512bb.jpg",
  "appUrl": "https://apps.apple.com/us/app/habitify-habit-tracker/id1477912538?uo=4",
  "scrapedAt": "2026-08-22T04:17:12.664Z"
}
```

### Dataset views

- **Ranked results** - keyword, country, position, app, developer, rating, rating count, price. The leaderboard.
- **Market research** - position, category, rating count, last update, first release, free flag, minimum iOS. The view you sort to find gaps.

### Frequently asked questions

**How many results can I get per keyword?** Up to 200 requested; Apple returns a few less. `resultsReturned` tells you the true figure per row.

**Is this the same ranking a user sees?** It is Apple's public search ordering for that storefront, which is what ASO tooling in this price range uses.

**Do I need an API key?** No. No key, no developer account, no proxy.

**Can I search iPad or Mac apps?** Yes, via `entity`: `iPadSoftware` or `macSoftware`.

**Why is `description` missing?** `includeDescription` defaults to `false` because descriptions are long enough to make a CSV unmanageable. Turn it on if you need the text.

**How is this different from the ASO Keyword Rank Tracker?** This returns the whole ranked list for a keyword. The tracker returns one row per keyword telling you where *your* app sits, with rank history. Use this one to explore, that one to monitor.

### Related actors

- **ASO Keyword Rank Tracker** - your app's position for a keyword, with change over time.
- **App Store Top Charts Scraper** - category and country chart rankings.
- **App Store App Details Scraper** - full metadata including in-app purchase names and prices.
- **App Store Reviews Scraper** - customer reviews across every storefront.

# Actor input Schema

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

What to search for. Aliases: keyword, terms, term, queries, search.

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

Two letter iTunes storefronts. Results and ordering differ per storefront. Aliases: country, countryCodes.

## `limit` (type: `integer`):

Apple accepts up to 200 and typically returns a few less than asked (about 191 to 193 for a request of 200). The number actually returned is reported per row.

## `entity` (type: `string`):

Which storefront catalogue to search.

## `lang` (type: `string`):

Apple language tag such as de\_de, es\_es, ja\_jp. Changes the localized names and descriptions returned.

## `attribute` (type: `string`):

softwareDeveloper searches publisher names instead of app text.

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

Descriptions run to several thousand characters and bloat CSV exports, so they are off by default.

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

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

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

Not required.

## Actor input object example

```json
{
  "keywords": [
    "habit tracker",
    "budget planner"
  ],
  "countries": [
    "us"
  ],
  "limit": 50,
  "entity": "software",
  "attribute": "",
  "includeDescription": false,
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per app in the search results, in ranked order, with full metadata.

# 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": [
        "habit tracker",
        "budget planner"
    ],
    "countries": [
        "us"
    ]
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,renzomacar/app-store-search-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/0Kyo7anZMaQJ1gzND/builds/P9tXlTIecG5IgZknZ/openapi.json
