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

Track App Store and Google Play keyword ranks daily, with rank changes, top charts and keyword suggestions. Export CSV or pull via API.

- **URL**: https://apify.com/carmentara/aso-rank-tracker.md
- **Developed by:** [Carmentara OU](https://apify.com/carmentara) (community)
- **Categories:** Marketing, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 keyword rank rows

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

### App Store & Google Play Keyword Rank Tracker (ASO)

Track where your apps and competitors rank for the keywords people type into Apple’s App Store and Google Play. Run it daily to get rank deltas, snapshot top charts, and expand your list with keyword suggestions.

- 🔎 Keyword ranks: position for each keyword × country × store, with previous rank and change
- 🎯 Tracked apps: rows for the apps you name (your app + competitors)
- 📈 Top charts: top-free/top-paid on both stores; Apple only for top-grossing
- 💡 Suggestions: autocomplete keywords on both stores
- 📤 Export to CSV, Excel, JSON, Google Sheets, or pull via the API

### Who uses it

- ASO and growth teams, indie devs, agencies, data teams
- Anyone who needs daily keyword checks and chart snapshots with minimal upkeep

### How to track keyword rankings

1. Create a free Apify account.
2. Open this Actor and set Mode to `keyword-ranks`.
3. Add your Search keywords.
4. Under Tracked apps, add your app id/bundle/package and a few competitors:
   - Apple: numeric id (`571800810`) or App Store URL or bundle id (`com.calm.calmapp`)
   - Google: package name (`com.calm.android`) or Play URL
   - Tip: If you track different IDs per store, use explicit prefixes (`apple:...`, `google:...`). Unprefixed entries are applied to every selected store (bundle id on Apple; package id on Google).
5. Turn on Only tracked apps if you only need your apps’ rows (cheapest).
6. Click Start, then Save as a new task and Schedule it daily.

`rankChange` is positive when an app moved up. It compares with your last run via a delta key‑value store.

### Input examples

Keyword ranks (only tracked apps, cheapest)

```json
{
  "mode": "keyword-ranks",
  "keywords": ["meditation", "sleep sounds"],
  "countries": ["us", "gb"],
  "stores": ["apple", "google"],
  "trackedApps": ["apple:571800810", "google:com.calm.android"],
  "onlyTrackedApps": true,
  "maxResultsPerKeyword": 30
}
```

Top charts

```json
{ "mode": "top-charts", "countries": ["us"], "stores": ["apple","google"], "charts": ["top-free"], "maxChartEntries": 50 }
```

Keyword suggestions

```json
{ "mode": "keyword-suggestions", "keywords": ["sleep"], "countries": ["us"], "stores": ["apple","google"] }
```

### Output

- keyword-ranks: one row per keyword × country × store with `tracked` rank details and optional `top` list (first N results)
- top-charts: one row per chart entry with `previousRank`/`rankChange`
- keyword-suggestions: one row per suggestion with `position`

### Sample output (real)

Keyword ranks (second run, so `previousRank`/`rankChange` are filled)

```json
{
  "store": "google",
  "country": "us",
  "language": "en",
  "keyword": "meditation",
  "checkedAt": "2026-10-05T07:41:29.384Z",
  "resultsCount": 20,
  "tracked": [
    {
      "appId": "com.calm.android",
      "title": "Calm - Sleep, Meditate, Relax",
      "rank": 7,
      "previousRank": 7,
      "rankChange": 0,
      "status": "same"
    }
  ],
  "top": [
    { "rank": 1, "appId": "com.spotlightsix.zentimerlite2", "title": "Insight Timer - Meditation App", "developer": "Insight Network Inc", "rating": 4.6966915, "ratingCount": null, "price": "Free", "url": "https://play.google.com/store/apps/details?id=com.spotlightsix.zentimerlite2" },
    { "rank": 2, "appId": "meditofoundation.medito", "title": "Medito: Meditation & Sleep", "developer": "Medito for Mindfulness, Meditation and Sleep", "rating": 4.783599, "ratingCount": null, "price": "Free", "url": "https://play.google.com/store/apps/details?id=meditofoundation.medito" },
    { "rank": 3, "appId": "com.getsomeheadspace.android", "title": "Headspace: Sleep & Meditation", "developer": "Headspace for Meditation, Mindfulness and Sleep", "rating": 4.2566533, "ratingCount": null, "price": "Free", "url": "https://play.google.com/store/apps/details?id=com.getsomeheadspace.android" }
  ],
  "sourceUrl": "https://play.google.com/store/search?q=meditation&c=apps&hl=en&gl=US"
}
```

Top charts (first run for this chart)

```json
{
  "store": "apple",
  "country": "us",
  "chart": "top-free",
  "category": null,
  "rank": 1,
  "appId": "6760173601",
  "title": "Muse from Meta",
  "developer": "Meta Platforms, Inc.",
  "rating": null,
  "ratingCount": null,
  "price": "Free",
  "url": "https://apps.apple.com/us/app/muse-from-meta/id6760173601?uo=2",
  "previousRank": null,
  "rankChange": null,
  "checkedAt": "2026-10-05T07:41:30.428Z"
}
```

Keyword suggestions

```json
{
  "store": "apple",
  "country": "us",
  "seed": "sleep",
  "suggestion": "sleeper",
  "position": 1,
  "checkedAt": "2026-10-05T07:41:31.043Z"
}
```

### How much does it cost?

You pay per row pushed (pay‑per‑event).

| What you get | Event name | Price |
|---|---|---|
| Keyword rank row (one keyword × country × store) | `keyword-rank-check` | $0.002 |
| Top chart entry row | `chart-entry` | $0.001 |
| Keyword suggestion row | `keyword-suggestion` | $0.0005 |

Example: 20 keywords × 5 countries × 2 stores = 200 rows/day → 6,000 rows/month → 6,000 × $0.002 = $12/month (plus any charts/suggestions you run).

Use “Max PPE charge (USD)” to hard‑cap what one run can cost. The run stops pushing rows at the cap.

### Scheduling and deltas

- Save your input as a task and add a daily schedule.
- `previousRank`/`rankChange` compare with the previous run via a named key‑value store (Delta store name). Give different scheduled tasks different store names if you want separate histories.

### Integrations

- API and clients: start runs and fetch results with the Apify API and the Python/JS clients.
- Zapier, Make, n8n, Google Sheets, Slack: wire results where your team works.
- AI agents (MCP): available through the Apify MCP server (`mcp.apify.com`). See Apify docs for connecting agents to platform datasets and key‑value stores.

### Limitations

- Google Play search depth is capped at 30 results per query by the underlying public endpoints.
- Apple top charts are limited to 100 entries by Apple’s official RSS.
- Google Play top‑grossing is currently unsupported (the public library returns top‑free). It’s skipped with a warning.
- Language defaults to `en`. On Google Play it affects result titles.
- Positions are what a logged‑out visitor sees; storefront personalization can cause slight differences.

### FAQ

- Is it allowed to scrape these results?
  The Actor reads public storefront pages and RSS endpoints that anyone can open without logging in. It doesn’t access accounts or private data. You’re responsible for how you use the data (especially personal data) under your local laws.

- How often should I run it?
  Daily for keyword ranks and charts is typical. Use a scheduled task and keep the same keyword spelling to build a consistent history.

- How are rank changes computed?
  The Actor stores the last seen ranks in a named key‑value store (Delta store name). Each run compares `rank` with the previous snapshot to produce `previousRank` and `rankChange`.

- Why does a tracked app show `not-ranked`?
  It wasn’t found within the scanned depth for that keyword/country/store on this run. Increase `maxResultsPerKeyword` (Apple: up to 200; Google: capped at 30 per query).

- Why only 30 Google results?
  Google Play’s public search endpoints return at most ~30 per query; the Actor caps to 30 and documents it to avoid misleading “not ranked” beyond that depth.

- When should I use `apple:` / `google:` prefixes?
  Use them when you track different identifiers per store. Unprefixed entries are applied to every selected store (bundle id on Apple; package id on Google).

### Local development

See `DEVELOPMENT.md` for the dev quickstart.

### License

MIT — © Carmentara

# Actor input Schema

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

What to run: keyword-ranks (main), top-charts, or keyword-suggestions.

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

keyword-ranks: search keywords to check. keyword-suggestions: seed keywords. Not used by top-charts.

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

Lowercase ISO-2 country codes like us, gb, de.

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

Which stores to query: apple (App Store) and/or google (Google Play).

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

Apple: numeric id or App Store URL (or bundle id); Google: package name or Play URL.

## `topN` (type: `integer`):

How many top search results to include in each keyword-ranks row (0 = none).

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

How deep to search for tracked apps per keyword. Apple: max 200. Google Play: capped at 30 per query.

## `onlyTrackedApps` (type: `boolean`):

keyword-ranks: omit the top list and output only your tracked apps.

## `charts` (type: `array`):

top-charts: which charts to fetch. Note: Google Play top-grossing is currently unsupported.

## `categories` (type: `array`):

top-charts: optional categories. Apple: numeric genre id (e.g. 6017 Education). Google: category id (e.g. GAME, PRODUCTIVITY).

## `maxChartEntries` (type: `integer`):

top-charts: number of chart entries per chart (Apple max 100; Google up to 200).

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

Language hint for the store where applicable, e.g. en. On Google Play, this affects result titles.

## `deltaStoreName` (type: `string`):

Named key-value store that keeps last ranks so scheduled runs show previousRank/rankChange.

## `concurrency` (type: `integer`):

Parallel requests to the stores.

## `maxPpeChargeUSD` (type: `number`):

Stop pushing results once this many USD of pay-per-event charges are reached in this run.

## Actor input object example

```json
{
  "mode": "keyword-ranks",
  "keywords": [
    "meditation",
    "sleep sounds"
  ],
  "countries": [
    "us"
  ],
  "stores": [
    "apple",
    "google"
  ],
  "trackedApps": [
    "571800810",
    "com.calm.android"
  ],
  "topN": 10,
  "maxResultsPerKeyword": 50,
  "onlyTrackedApps": false,
  "charts": [
    "top-free",
    "top-paid"
  ],
  "categories": [],
  "maxChartEntries": 100,
  "language": "en",
  "deltaStoreName": "aso-rank-tracker-delta",
  "concurrency": 3,
  "maxPpeChargeUSD": 1
}
```

# Actor output Schema

## `dataset` (type: `string`):

All results are stored in the default dataset. Row shape depends on the mode (keyword-ranks, top-charts or keyword-suggestions).

# 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": [
        "meditation",
        "sleep sounds"
    ],
    "trackedApps": [
        "571800810",
        "com.calm.android"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("carmentara/aso-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": [
        "meditation",
        "sleep sounds",
    ],
    "trackedApps": [
        "571800810",
        "com.calm.android",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("carmentara/aso-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": [
    "meditation",
    "sleep sounds"
  ],
  "trackedApps": [
    "571800810",
    "com.calm.android"
  ]
}' |
apify call carmentara/aso-rank-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,carmentara/aso-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/vcNQJ1UIHAx2vKEAv/builds/T70KBkDzxSUIEEkcm/openapi.json
