ASO Rank Tracker API — App Store & Google Play Change Alerts
Pricing
from $2.00 / 1,000 rank checks
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
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
| Field | Type | Default | Notes |
|---|---|---|---|
checks | array | required | {keyword, appId, store, country}. store ∈ apple|google. Apple appId = trackId digits or bundleId. Max 500 unique / run. |
monitorId | string | default | Snapshot namespace; monitors do not interact. |
materialityThreshold | int 1–100 | 3 | Min |Δrank| vs last reported rank. Enter/exit always material. |
confirmationRuns | 1|2 | 1 | 2 = hold until same direction on a second run. |
appleSearchDepth | int 10–200 | 100 | iTunes Search limit (may serve fewer). |
emitBaselines | bool | true | First observation emits baseline. |
proxyConfiguration | proxy | Apify DC | Datacenter 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": "..."}
direction ∈ up | 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→ emitchange, commit. - Material and
confirmationRuns=2→ first run stores a pending hold (no row); second run with the same direction confirms and emitschangewithfirstSeenAt. Direction flip restarts the hold; jitter back under threshold clears it.
Scheduling + webhook
- Create a task with your
checks+monitorId. - Schedule cron (e.g.
0 6 * * *). - Webhook on
ACTOR.RUN.SUCCEEDED→ fetch dataset items filtered totype=change(or filter client-side). - 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: 0is 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.
hlis fixed toen;gl(country) drives ranking. A response is billable only when theds:4blob parses and yields ≥1 app entry — a missing/unparseableds:4or a zero-app parse is treated asfetch-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 asfetch-failederror rows, not billed not-found. - Concurrent runs of the same
monitorId: the second run exitsSUCCEEDEDwith a single unchargedmonitor-busyerror 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: 2for 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.