ASO Rank Tracker API — App Store & Google Play Change Alerts avatar

ASO Rank Tracker API — App Store & Google Play Change Alerts

Pricing

from $2.00 / 1,000 rank checks

Go to Apify Store
ASO Rank Tracker API — App Store & Google Play Change Alerts

ASO Rank Tracker API — App Store & Google Play Change Alerts

Monitor Apple App Store and Google Play keyword rankings via API, MCP, or schedule — emits only material rank changes, with confirmation runs to suppress rank jitter. Per-country checks, persistent baselines, uncharged change alerts. $1 per 1,000 rank checks. No browser, no API key.

Pricing

from $2.00 / 1,000 rank checks

Rating

0.0

(0)

Developer

Heim AI

Heim AI

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Keyword rank monitoring that emits only material changes. Give it a list of (keyword, appId, store, country) checks and a monitorId; each run fetches App Store / Play search rankings, compares against a persistent per-monitor snapshot, and pushes dataset rows only when an app moves, enters, or drops from results (plus a one-time baseline and a run summary). Schedule it daily; attach a webhook on SUCCEEDED and consume only type:"change" rows.

Quick start

Console: paste the prefill checks (Calm on Google Play + Apple for "meditation") and run. First run → 2 baseline rows + 1 summary.

CLI:

apify call kaz_kakyo/aso-rank-delta -i '{
"checks": [
{"keyword":"meditation","appId":"com.calm.android","store":"google","country":"us"},
{"keyword":"meditation","appId":"571800810","store":"apple","country":"us"}
],
"monitorId": "default"
}'

REST:

curl -X POST "https://api.apify.com/v2/acts/kaz_kakyo~aso-rank-delta/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"checks":[{"keyword":"meditation","appId":"com.calm.android","store":"google","country":"us"}],"monitorId":"prod"}'

Input

FieldTypeDefaultNotes
checksarrayrequired{keyword, appId, store, country}. store ∈ apple|google. Apple appId = trackId digits or bundleId. Max 500 unique / run.
monitorIdstringdefaultSnapshot namespace; monitors do not interact.
materialityThresholdint 1–1003Min |Δrank| vs last reported rank. Enter/exit always material.
confirmationRuns1|212 = hold until same direction on a second run.
appleSearchDepthint 10–200100iTunes Search limit (may serve fewer).
emitBaselinesbooltrueFirst observation emits baseline.
proxyConfigurationproxyApify DCDatacenter only; local runs work without it.

Output (uncharged rows)

type:"change" — material rank move after confirmation rules:

{
"type": "change",
"monitorId": "default",
"store": "google",
"country": "us",
"keyword": "meditation",
"appId": "com.calm.android",
"appName": "Calm",
"prevRank": 10,
"newRank": 6,
"delta": -4,
"direction": "up",
"materialityThreshold": 3,
"confirmationRuns": 1,
"confirmed": true,
"firstSeenAt": null,
"searchDepth": 23,
"servedResults": 23,
"observedAt": "2026-08-01T00:00:00.000Z",
"prevReportedAt": "2026-07-31T00:00:00.000Z",
"runId": "..."
}

directionup | down | entered | dropped. delta is null for entered/dropped.

type:"baseline" — first observation of a check:

{
"type": "baseline",
"monitorId": "default",
"store": "apple",
"country": "us",
"keyword": "meditation",
"appId": "571800810",
"appName": "Calm",
"rank": 2,
"searchDepth": 100,
"servedResults": 94,
"observedAt": "2026-08-01T00:00:00.000Z",
"runId": "..."
}

type:"summary" — exactly one, last row of every run (checksObserved, changes, baselines, pendingHolds, errors, …).

type:"error" — uncharged; reasons: invalid-input, fetch-failed, charge-limit, cap-exceeded, snapshot-reset, snapshot-entry-dropped, no-valid-checks, monitor-busy, timeout-budget, resume-stranded (rare: a check interrupted mid-charge whose durable state predates this build, so it cannot be replayed — never re-run and never charged again). Invalid input never fails the run. Identity fields on error rows are included only when they are primitive strings (truncated to 120 chars).

How change detection works

Each check has a committed (last reported) rank in the named KV store aso-rank-delta-state. Observations are compared to that committed rank, not to the previous raw observation — slow drifts accumulate until they cross materialityThreshold.

  • No entry yet → baseline (optional) + commit the observation.
  • Material and confirmationRuns=1 → emit change, commit.
  • Material and confirmationRuns=2 → first run stores a pending hold (no row); second run with the same direction confirms and emits change with firstSeenAt. Direction flip restarts the hold; jitter back under threshold clears it.

Scheduling + webhook

  1. Create a task with your checks + monitorId.
  2. Schedule cron (e.g. 0 6 * * *).
  3. Webhook on ACTOR.RUN.SUCCEEDED → fetch dataset items filtered to type=change (or filter client-side).
  4. MCP / agent callers: same input schema; read only change rows for alerts.

Example task body:

{
"actId": "kaz_kakyo/aso-rank-delta",
"input": {
"checks": [{"keyword":"meditation","appId":"com.calm.android","store":"google","country":"us"}],
"monitorId": "calm-us",
"materialityThreshold": 3,
"confirmationRuns": 2
}
}

Honest limits

  • Apple: iTunes Search API ordering is a proxy for App Store search, depth ≤200, and the API may return fewer than requested. Not device-personalized. A parsed response with resultCount: 0 is a legitimate billable not-found.
  • Google Play: the real web SERP serves only ~20–30 apps; there is no deeper pagination (legacy batchexecute RPC is dead). Rank is null beyond what is served. hl is fixed to en; gl (country) drives ranking. A response is billable only when the ds:4 blob parses and yields ≥1 app entry — a missing/unparseable ds:4 or a zero-app parse is treated as fetch-failed (uncharged), because an empty Play SERP is indistinguishable from a layout change or consent page. Rare keywords with a genuinely empty Play SERP therefore come back as fetch-failed error rows, not billed not-found.
  • Concurrent runs of the same monitorId: the second run exits SUCCEEDED with a single uncharged monitor-busy error row and performs no checks/charges.
  • Platform migration mid-run: a check whose charge or delivery was interrupted is written ahead (state + output row) before it is charged, so the resumed run replays it — re-delivering the row only when the dataset cannot prove it already landed, and never charging for it again. In the rare unprovable case (dataset unreadable) a row may appear twice; it is never charged twice and never dropped.
  • Ranks can jitter day-to-day — use confirmationRuns: 2 for alerts.
  • Not personalized / not identical to every device locale.

Pricing

Pay-per-event: $0.005 per run start + $0.001 per successfully observed check (found or definite not-found). Change, baseline, summary, and error rows are free.

Worked example: 10 keywords × 2 stores = 20 checks/day → $0.005 + 20×$0.001 = $0.025/day = $0.75/month.