# ASO Keyword Rank Tracker: App Store & Google Play (`themineworks/aso-keyword-rank-tracker`) Actor

Track where any app ranks for any keyword on BOTH stores, in any country, in one dataset. Rank, rating, installs, developer, price and Apple top chart position, plus a free row naming the tracked apps that did not rank. No API key, no proxy.

- **URL**: https://apify.com/themineworks/aso-keyword-rank-tracker.md
- **Developed by:** [The Mine Works](https://apify.com/themineworks) (community)
- **Categories:** SEO tools, Business, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## 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

## 📱 ASO Keyword Rank Tracker: App Store & Google Play

> ⚡ Apple's public search and chart APIs plus Google Play's own page data. No API key, no proxy, no browser, no login.

> 💸 You are charged only for a ranking row that lands in your dataset. Keyword summary rows and the run summary are free.

### Overview

Almost every app store actor on this Store scrapes reviews. Reviews tell you what users thought last week. They do not tell you the thing an ASO team actually needs to know every morning: **where does my app sit in the search results today, and who is above me?**

This actor answers that for both stores at once, in any country, in one dataset:

- **App Store rank** from the iTunes Search API, whose result order is Apple's own search relevance order for that storefront, with 20 metadata fields attached to every row.
- **Google Play rank** from the Play search page's embedded data payload.
- **Apple top chart position** joined in per country, so a row shows keyword rank and chart rank together. Ranking first for "chatgpt" and sitting third in the free chart are different facts, and the row carries both.

✅ Both stores in one row shape · ✅ Any country storefront · ✅ Tracked app and competitor flags · ✅ A free row naming the tracked apps that did NOT rank · ✅ MCP ready for AI agents

### The row a rank tracker usually cannot give you

Every ranking product returns a list of apps that ranked. The single most useful ASO datapoint is the opposite: **your app was not on the page at all**. A list cannot express that, because absence has no row.

So every search also emits a free `keyword_summary` row:

```json
{
  "_type": "keyword_summary",
  "keyword": "habit tracker",
  "country": "us",
  "store": "appstore",
  "results_count": 9,
  "top_app": "Habit Tracker",
  "top_developer": "InnerGrow",
  "tracked_ranks": ["Habit Tracker - HabitKit: #2"],
  "tracked_not_ranked": ["com.calm.android"]
}
```

An all digits tracked id is an Apple track id and can never appear in a Google Play result, so it is not reported as missing there. That would read like a real gap and it is not one.

### Input

```json
{
  "keywords": ["habit tracker", "meditation"],
  "countries": ["us", "gb", "de"],
  "stores": ["appstore", "googleplay"],
  "trackedApps": ["com.roehl.habitkit", "6448311069"],
  "trackedDeveloper": "headspace",
  "trackedOnly": false,
  "maxResultsPerKeyword": 25,
  "includeTopCharts": true
}
```

| Field | What it takes |
| --- | --- |
| `keywords` | Search terms, exactly as a user would type them. |
| `countries` | Two letter storefront codes. Rankings differ sharply by country, which is where most of the value is. |
| `stores` | `appstore`, `googleplay`, or both. |
| `trackedApps` | Your app and your competitors, by Android package name, iOS bundle id, or numeric App Store id. |
| `trackedDeveloper` | Flag every app whose developer name contains this text, for publisher share of page. |
| `trackedOnly` | Deliver only tracked rows. Cheap daily monitoring when on, full competitive page when off. |
| `maxResultsPerKeyword` | How deep to record. App Store returns up to 200, Google Play about 30. Your main cost control. |
| `includeTopCharts` | Join Apple's chart position. One extra request per country for the whole run. |
| `chartKind` | `top-free` or `top-paid`. |
| `language` | Google Play language code. The App Store uses the storefront's own language automatically. |

One search is run per keyword per country per store. Three keywords across three countries on both stores is eighteen searches.

### Output

```json
{
  "keyword": "chatgpt",
  "country": "us",
  "store": "appstore",
  "store_name": "App Store",
  "rank": 1,
  "rank_bucket": "top3",
  "app_id": "6448311069",
  "bundle_id": "com.openai.chat",
  "app_name": "ChatGPT",
  "developer": "OpenAI OpCo, LLC",
  "rating": 4.83042,
  "rating_count": 9313887,
  "price": 0,
  "is_free": true,
  "primary_genre": "Productivity",
  "content_rating": "12+",
  "version": "1.2026.209",
  "released_at": "2023-05-18T07:00:00Z",
  "updated_at": "2026-08-07T21:46:28Z",
  "app_url": "https://apps.apple.com/us/app/chatgpt/id6448311069",
  "top_chart_rank": 3,
  "in_top_chart": true,
  "is_tracked_app": true,
  "results_in_search": 7,
  "checked_at": "2026-08-13T15:10:47.823Z"
}
```

Google Play rows carry `installs` and `in_app_purchases` where Apple carries `rating_count`, `price`, `version` and release dates. Each store is reported with the fields it actually publishes, rather than padded with nulls to make the two look identical.

### How the Play ranking is read, and why

Google Play ships its search results as a nested array inside the page's own data payload. The obvious approach is to read the rendered HTML around each app link instead, and it was tried first: on four locale and keyword combinations it lost the title, developer and rating on **10 to 23 percent** of apps, because the first appearance of an app link on the page is often a carousel or a preload hint rather than the result card. Reading the payload recovered **100 percent** of them on the same four combinations.

The payload's internal index layout is Google's private business and will change eventually. So the result cluster is located structurally, by finding the longest array whose members are shaped like app entries, rather than by a hardcoded path, and every field read is bounds checked. If Google reshuffles the inner indices, individual metadata fields go quiet; the **rank order, which is what this actor exists for, comes from the cluster order and survives**.

### Common use cases

**Daily rank monitoring.** Schedule it with `trackedOnly` on and your keyword set. One row per app per keyword per country per day, ready to chart.

**Competitive intelligence.** Turn `trackedOnly` off and capture the whole results page. You get every competitor's rating, rating count, install band, price and last update date alongside their rank.

**Keyword research and prioritisation.** Run a candidate keyword list once and see which terms your app already ranks for, which are dominated by apps with millions of ratings, and which are genuinely winnable.

**Market entry.** Run the same keywords across ten storefronts. Search results differ far more between countries than most teams expect, and the gaps are where new installs are.

**Publisher share of voice.** Set `trackedDeveloper` and count how much of each results page a single publisher owns.

**Investor and market research.** Rank plus rating count plus install band across a category is a credible proxy for relative traction without buying a data subscription.

**AI agents.** Point an agent at it through MCP and ask which competitors moved into the top five for your head term this week.

### Cost control built in

1. **Run level spend cap.** Job count is keywords times countries times stores, so it multiplies fast and a per keyword budget would bound nothing. The ceiling applies to the whole run.
2. **Zero yield abort.** Two consecutive searches returning nothing and the run stops. One productive search resets it, so a few dud keywords never trip it.
3. **No transport escalation.** A store that stops answering is reported as an empty search in the summary. There is no retry through a paid proxy or a browser.

### Pricing

| Event | Price | You pay when |
| --- | --- | --- |
| Ranking checked | $0.0005 | A ranking row lands in your dataset |

**$0.50 per 1,000 rankings.** Keyword summary rows, the run summary and empty searches are free. Tracking one app across 20 keywords and 5 countries on both stores with `trackedOnly` on costs well under a cent per day.

### Run it on a schedule

1. Run once with your keywords, countries and tracked apps, then click **Save as a task**.
2. In the Apify Console go to **Schedules → Create new**.
3. Pick a daily frequency and attach the saved task.
4. Wire the dataset to Sheets, Slack or a webhook from the **Integrations** tab.

Every row carries `checked_at`, so appending each daily run into one dataset gives you a rank history with no extra work. Group on `keyword`, `country`, `store` and `app_id`.

### FAQ

**Is the App Store order the real search ranking?**
It is Apple's own public search endpoint for that storefront, and its result order is the store's search relevance order. That is the same signal ASO tools use. It is a ranking, not a rendering of the app you would see on a specific device, so a personalised in app result can differ slightly.

**Why does Google Play return about 30 results?**
Because that is what the first search page contains. Going deeper requires triggering the store's infinite scroll, which needs a browser, and a browser costs many times more per result than every ranking row here is worth. Thirty deep is well past where anyone gets installs.

**Do you need my developer account?**
No. Everything here is public store data that any user can see. No login, no key, no console access.

**Why is `top_chart_rank` empty on most rows?**
Because most apps are not in Apple's top 100 chart for that country. A meditation app ranking first for "meditation" is usually nowhere near the overall free chart, and the empty field is the correct answer rather than a failed lookup. Verified: ChatGPT returns chart position 3, Google Gemini 11, Claude 13.

**Can I track iOS and Android with one id?**
Often yes, because many publishers use the same reverse domain string for both. When they differ, add both. A numeric id is treated as Apple only.

**Does it work in non English storefronts?**
Yes. Set `language` for Google Play and the App Store follows the storefront automatically. German results correctly return German titles and comma formatted ratings, which are normalised to numbers.

### Use from Claude, ChatGPT and any MCP agent

```
https://mcp.apify.com/?tools=themineworks/aso-keyword-rank-tracker
```

Or call it programmatically:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('themineworks/aso-keyword-rank-tracker').call({
  keywords: ['habit tracker', 'meditation'],
  countries: ['us', 'gb', 'de'],
  stores: ['appstore', 'googleplay'],
  trackedApps: ['com.roehl.habitkit'],
  maxResultsPerKeyword: 25,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Questions, or need a store or field we do not cover yet? Reach out through the Apify profile.

# Actor input Schema

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

Search terms to check, exactly as a user would type them into the store search box.

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

Two letter storefront codes such as us, gb, in, de, br, jp. Rankings differ sharply by country, so this is where most of the value is. One search is run per keyword per country per store.

## `stores` (type: `array`):

Which stores to check: appstore, googleplay, or both. Leave empty for both.

## `trackedApps` (type: `array`):

Your app and your competitors. Use the Android package name (com.example.app) or the iOS bundle id or numeric App Store id. Matched rows are flagged, and any tracked app that does NOT rank is named in a free keyword summary row.

## `trackedDeveloper` (type: `string`):

Flag every app whose developer name contains this text. Useful for measuring a publisher's share of the results page.

## `trackedOnly` (type: `boolean`):

Deliver only rows matching a tracked app or tracked developer. Turn this on for cheap daily rank monitoring, off when you want the full competitive results page.

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

How deep to record the results page. The App Store returns up to 200, Google Play returns about 30 per search. This is your main cost control.

## `includeTopCharts` (type: `boolean`):

Add each App Store app's position in Apple's public top chart for that country, so keyword rank and chart rank sit in the same row. One extra request per country for the whole run.

## `chartKind` (type: `string`):

Which Apple chart to join against.

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

Language code for Google Play results, for example en, de, pt. The App Store uses the country storefront's own language automatically.

## Actor input object example

```json
{
  "keywords": [
    "habit tracker",
    "meditation"
  ],
  "countries": [
    "us"
  ],
  "stores": [
    "appstore",
    "googleplay"
  ],
  "trackedApps": [],
  "trackedOnly": false,
  "maxResultsPerKeyword": 10,
  "includeTopCharts": true,
  "chartKind": "top-free",
  "language": "en"
}
```

# 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": [
        "habit tracker",
        "meditation"
    ],
    "countries": [
        "us"
    ],
    "stores": [
        "appstore",
        "googleplay"
    ],
    "trackedApps": [],
    "trackedDeveloper": "",
    "trackedOnly": false,
    "maxResultsPerKeyword": 10,
    "includeTopCharts": true,
    "chartKind": "top-free",
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("themineworks/aso-keyword-rank-tracker").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",
        "meditation",
    ],
    "countries": ["us"],
    "stores": [
        "appstore",
        "googleplay",
    ],
    "trackedApps": [],
    "trackedDeveloper": "",
    "trackedOnly": False,
    "maxResultsPerKeyword": 10,
    "includeTopCharts": True,
    "chartKind": "top-free",
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("themineworks/aso-keyword-rank-tracker").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",
    "meditation"
  ],
  "countries": [
    "us"
  ],
  "stores": [
    "appstore",
    "googleplay"
  ],
  "trackedApps": [],
  "trackedDeveloper": "",
  "trackedOnly": false,
  "maxResultsPerKeyword": 10,
  "includeTopCharts": true,
  "chartKind": "top-free",
  "language": "en"
}' |
apify call themineworks/aso-keyword-rank-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,themineworks/aso-keyword-rank-tracker"
        }
    }
}

```

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/V8zPb5Sf5glrkqr3O/builds/Jl8AtWY2pVMH2aKWG/openapi.json
