How to track App Store & Google Play keyword rankings daily (without an ASO subscription)

Stop paying hundreds a month for AppTweak, Sensor Tower or data.ai. Track keyword ranks on the App Store and Google Play every day, with history and alerts, on Apify's free $5 credit.
People find apps by typing a few words into the App Store or Google Play search box: "meditation", "sleep sounds", "mindfulness". Where your app lands for those words decides whether a new user sees it at all. But neither Apple nor Google gives you a free, exportable history of your organic keyword rank by country. Change your subtitle, ship a release, or watch a competitor climb, and you have no record of what moved and when.
The usual fixes hurt in different ways:
- ASO SaaS (AppTweak, Sensor Tower, data.ai and similar) will track keywords for you, but plans often run to hundreds of dollars a month, which is a hard sell for an indie app or a small ASO team that only needs daily ranks for a short keyword list.
- Checking by hand means opening each store, each country, each keyword in a private window, counting down to your app, and pasting the number into a spreadsheet. It works for two keywords. It falls apart for twenty, and you still have no reliable history.
- No history means you can't tell a one-day blip from a week-long drop, and you can't prove to yourself (or a stakeholder) that last Tuesday's listing change did anything.
This guide covers three honest options. Pick the one that fits how many keywords and countries you care about.
- Free: check by hand (or with free storefront tools)
- Build it yourself on the public storefront endpoints
- Least effort: a scheduled Apify actor that keeps App Store + Google Play ranks in one feed
Option 1: Check by hand (free, slow)
- Open a private browser window, so you see what a logged-out visitor sees.
- On the App Store and Google Play, search each keyword for each country you care about (change the storefront country, or use the
gl=/ country picker). - Count down to your app. Write the position, the date, the store and the country into a spreadsheet.
- Repeat tomorrow.
This is free and honest. It is also easy to miscount past the first screen, and almost nobody keeps it up every day across two stores and several countries. Results can also differ between a logged-in account and a private window — the stores personalise.
Option 2: Call the public endpoints yourself
You can script it. Apple exposes search and chart RSS/JSON endpoints; Google Play has public search pages and unofficial clients that read them. A weekend prototype that prints "Calm is #3 for meditation in the US App Store" is realistic.
What you then own forever:
- Two different response shapes (Apple vs Google), plus country and language parameters.
- Pagination and depth limits (Google Play search depth is capped around 30 results per query).
- A place to store yesterday's ranks so you can compute a change.
- A scheduler, retries, and someone to fix the scraper when a markup or endpoint changes.
It is doable. It is also a small product you then have to maintain. If that is your job, stop here and build it. If it isn't, option 3 exists so you don't have to.
Option 3: One scheduled actor for both stores
We built and maintain that tracker as an Apify actor: App Store & Google Play Keyword Rank Tracker (ASO). Its keyword-ranks mode returns where your apps (and competitors) sit for each keyword × country × store. What it does:
- Covers App Store and Google Play in one feed, with the same row shape.
- Gives you tracked-app ranks (
rank,previousRank,rankChange,status) inside each keyword row. - Remembers the previous run via a named key-value store, so scheduled runs fill in the deltas.
- Also has top-charts and keyword-suggestions modes when you want chart snapshots or autocomplete ideas.
- Needs no Apple or Google developer account.

Pricing: $0.002 per keyword-rank row (one keyword × country × store), $0.001 per top-chart entry, $0.0005 per keyword suggestion, plus a $0.00005 start fee per run. There is no monthly fee from us, and platform usage is not charged separately on pay-per-event.
Concrete free-credit example. Tracking 10 keywords in us and gb on both stores is 10 × 2 × 2 = 40 rows a day. Over a 30-day month that is 1,200 rows × $0.002 = $2.40, plus 30 × $0.00005 = $0.0015 in start fees.
How many daily checks fit in Apify's free $5/month credit? At $0.002 per keyword-rank row, $5 covers about 2,500 keyword × country × store checks a month (roughly 83 per day), before the tiny start fees. The 40-row example above uses under half of that. Unused credit does not roll over (Apify pricing: "$5 to spend in Apify Store or on your own Actors", no credit card required on the free plan).
No coding required
You can do the whole daily setup in the Apify Console, without writing any code:
- Open the actor in the Apify Console. Set Mode to
keyword-ranks, fill in Keywords, Countries (ISO-2) and Tracked apps (ids or URLs), pick Stores, and turn on Only tracked apps (omit top list) so you only pay for the rows you care about. - Click Save as a new task and run it once.
- Add a daily schedule for the task under Schedules (Apify schedules).
- Export: download the run's dataset as CSV, or wire Apify's Google Sheets integration in append mode so each day adds rows under the previous ones.
Apple ids are numeric (Calm is 571800810, Headspace is 493145008). Google Play uses package names (com.calm.android, com.getsomeheadspace.android). When the ids differ per store, prefix them: apple:… and google:….
Run it from code
Python, using the official apify-client package (version 3 or later). This is the input we actually ran on 5 October 2026:
# pip install "apify-client>=3"
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
"mode": "keyword-ranks",
"keywords": ["meditation", "sleep sounds", "mindfulness", "sleep"],
"countries": ["us", "gb"],
"stores": ["apple", "google"],
"trackedApps": [
"apple:571800810", # Calm (App Store)
"apple:493145008", # Headspace (App Store)
"google:com.calm.android", # Calm (Google Play)
"google:com.getsomeheadspace.android", # Headspace (Google Play)
],
"onlyTrackedApps": True, # one row per keyword × country × store (cheapest)
"maxResultsPerKeyword": 50, # Apple up to 200; Google Play caps at 30
"maxPpeChargeUSD": 0.04, # hard cap per run
"deltaStoreName": "aso-tutorial-blog-20261005",
}
run = client.actor("carmentara/aso-rank-tracker").call(run_input=run_input)
for item in client.dataset(run.default_dataset_id).iterate_items():
store = item["store"]
country = item["country"]
keyword = item["keyword"]
for t in item.get("tracked") or []:
rank = f"#{t['rank']}" if t.get("rank") is not None else "not ranked"
title = t.get("title") or t["appId"]
print(f"{store:<6} {country} {keyword:<14} {title:<32} {rank:<11} change: {t.get('rankChange')}")
We ran that input twice on the same day so previousRank / rankChange would fill in (a real multi-day history needs a daily schedule). Second-run excerpt for the US storefronts:
# apple us mindfulness Calm #5 change: 0
# apple us mindfulness Headspace: Sleep & Meditation #1 change: 0
# apple us meditation Calm #3 change: 0
# apple us meditation Headspace: Sleep & Meditation #2 change: 0
# apple us sleep sounds Calm #5 change: 0
# apple us sleep sounds 493145008 not ranked change: None
# apple us sleep Calm #4 change: None
# apple us sleep Headspace: Sleep & Meditation #8 change: -3
# google us meditation Calm - Sleep, Meditate, Relax #7 change: 0
# google us meditation Headspace: Sleep & Meditation #3 change: 0
# google us sleep sounds Calm - Sleep, Meditate, Relax #6 change: 0
# google us sleep sounds Headspace: Sleep & Meditation #14 change: 0
# google us mindfulness Calm - Sleep, Meditate, Relax #10 change: 0
# google us mindfulness Headspace: Sleep & Meditation #4 change: 0
# google us sleep Calm - Sleep, Meditate, Relax #8 change: 0
# google us sleep Headspace: Sleep & Meditation #9 change: 0
change is None on the first sighting of a keyword × app (and when an app is not ranked). On this second pass, Headspace dropped three places for "sleep" on the US App Store (rankChange: -3) between two runs a few minutes apart — storefront results move, which is exactly why a filter on the delta matters more than a single screenshot. The same 16 keyword × country × store rows also covered Great Britain; each run cost 16 × $0.002 + $0.00005 = $0.03205.

What a row looks like
A real keyword-ranks row from that run (US App Store, "meditation"):
{
"store": "apple",
"country": "us",
"language": "en",
"keyword": "meditation",
"checkedAt": "2026-10-05T11:15:54.134Z",
"resultsCount": 50,
"tracked": [
{
"appId": "571800810",
"title": "Calm",
"rank": 3,
"previousRank": 3,
"rankChange": 0,
"status": "same"
},
{
"appId": "493145008",
"title": "Headspace: Sleep & Meditation",
"rank": 2,
"previousRank": 2,
"rankChange": 0,
"status": "same"
}
],
"sourceUrl": "https://apps.apple.com/us/search?term=meditation"
}
Turn Only tracked apps off (and set Top N details per check) if you also want the top list of competitors in each row. Those still bill as one keyword-rank row per keyword × country × store; you are not charged per competitor inside top.
Or pull today's ranks with curl
Once the input is saved as a task, fetch the latest successful dataset as CSV:
curl -fsS "https://api.apify.com/v2/actor-tasks/your-username~your-task-name/runs/last/dataset/items?status=SUCCEEDED&format=csv" \
-H "Authorization: Bearer $APIFY_TOKEN" > todays-aso-ranks.csv
In Google Sheets, the same URL works with =IMPORTDATA("...&token=YOUR_APIFY_TOKEN"). Anyone who can edit the sheet can see the token, so prefer the Google Sheets integration in append mode for a real history.
Make it daily, and get alerts on drops
- In the Apify Console, open the actor, enter the input above and click Save as a new task.
- On the task, add a Schedule that runs once a day, for example
0 7 * * *in your timezone (Apify schedules). - Keep the keyword spelling and the Delta store name stable, so each run compares with the previous one.
Get an alert when a rank drops. Apify's Slack integration and webhooks fire on run events (succeeded / failed). On their own, they tell you that a run finished, not that a rank moved. To alert only when a tracked app drops — for example rankChange <= -3, or when an app leaves the top 10 (rank was ≤ 10 and is now empty / worse) — route the finished task's dataset through Zapier, Make or n8n. Trigger on the finished task run, fetch the dataset items, add a filter (Zapier filters, Make filters, n8n Filter node), then send the matching rows to Slack or email. Nested tracked[] fields flatten in most of those tools; filter on the tracked-app rank change, not only the parent row.
Bonus: keyword suggestions and top charts
The same actor has two other modes we smoke-tested the same day:
keyword-suggestions— seed a word likesleepand get autocomplete ideas from both stores (we got 15 suggestions for US App Store + Google Play for $0.00755).top-charts— snapshot top-free / top-paid (Apple also supports top-grossing; Google Play top-grossing is not supported and is skipped with a warning).


Limitations (read these before you rely on a number)
- Google Play search depth is capped at about 30 results per query by the public endpoints. If your app is deeper than that, the row will say it is not ranked even though a logged-in user might still find it further down.
- Google Play top-grossing is not supported (the public library returns top-free). The actor skips it with a warning.
- Ranks are what a logged-out visitor sees. Storefront personalisation, A/B tests and ads can make a logged-in phone look different.
- Apple top charts stop at 100 entries (Apple's RSS limit).
rankChangeis positive when an app moved up. Empty /nullmeans there was nothing to compare yet, or the app was not ranked.
Tips for useful tracking
- Track the words users type, not only your brand name. Start with category head terms, then the jobs ("sleep sounds", "breathing exercises").
- Add one or two competitors per store. Your own rank means more when you can see who moved around you.
- Use
apple:/google:prefixes when the identifiers differ per store (numeric Apple id vs Play package name). - Cap spend with
maxPpeChargeUSDon every task, so a bad input cannot surprise you. - Watch the trend, not one day. A three-place move between two runs a few minutes apart can be noise. A drop that holds for a week is worth a look — that is what the daily schedule is for.
Ready to automate App Store & Google Play keyword tracking?
For two keywords in one country, a private window and a spreadsheet work. For a daily rank history on both stores that you don't have to maintain:
- Open the App Store & Google Play Keyword Rank Tracker (ASO) on Apify.
- Pick
keyword-ranksmode, enter your keywords, countries and tracked app ids, and turn on Only tracked apps. - Schedule it daily. Setups like the 10-keyword / 2-country example above stay inside Apify's free $5 monthly credit.
Questions or feature requests? Open an issue on the actor's Issues tab.