How to track your Shopify app's keyword rankings daily (App Store SEO)

Stop guessing your App Store SEO performance. Track organic position history for your app and competitors automatically, for under $2 a month.
Most merchants find apps by typing a few words into the Shopify App Store search box: "email marketing", "upsell", "abandoned cart". Where your app lands in those results decides whether merchants see it at all. But Shopify's Partner Dashboard shows installs, merchants and earnings, not your organic position for a keyword. The only keyword reports Shopify gives you are for paid App Store ads.
So if you change your listing title, ship a big release or see a competitor climb, you have no record of what happened to your rank and when.
This guide covers two levels:
- Free: check by hand
- Least effort: a scheduled Apify actor that builds the rank history for you
Pick the level that fits you. If you track two or three keywords, level 1 may be enough.
Level 1: Check by hand (free, 5 minutes a day)
- Open a private browser window, so you see what a logged-out visitor sees.
- Search apps.shopify.com for each of your keywords.
- Count down to your app. Skip cards marked Ad, because they're paid placements, not organic rank.
- Write the position, the date and the top competitors into a spreadsheet.
This works, but it's easy to miscount once you're past the first page, and nobody keeps it up every day for 15 keywords. Results can also differ between a logged-in merchant and a private window.
What about a script of your own?
You could script it yourself. The App Store has no public search API, though, so a script has to read the search pages, and that's where the work is. Shopify changes the markup of those pages without notice, rate-limits repeated requests and blocks traffic it doesn't like. A tracker that worked last month can quietly return nothing, or the wrong positions. On top of that you need pagination, ad handling, stored history and a scheduler, plus someone to fix it when it breaks. It's doable, but it's a small product you then have to maintain.
Level 2: A scheduled actor that keeps the history
We built and maintain that tracker as an Apify actor, so you don't have to: Shopify App Store Keyword Rank Tracker & Scraper. Its keyword-ranks mode returns where any app ranks in App Store search for your keywords. What it does:
- Reads up to 240 organic results per keyword (24 = the first page) across pages, with polite delays and retries.
- Counts organic positions only. Ads are flagged separately (
isSponsored,sponsoredSlots) and never shift the count. - Gives you one
targetrow per keyword × tracked app, including"found": falsewhen the app isn't in the scanned results. - Remembers the previous run, so each row has
previousPositionandrankChange(positive = moved up). - Needs no Shopify account or API key.

Pricing: $0.002 per keyword-rank row, plus a $0.00005 start fee per run at the default 512 MB. There's no monthly fee, and platform usage isn't charged separately. For example, tracking 10 keywords for your app and 2 competitors with Only output tracked apps on is 30 rows a day. Over a 30-day month that's 900 rows × $0.002 = $1.80, plus $0.0015 in start fees.
Apify's free plan includes $5 a month in credit ("$5 to spend in Apify Store or on your own Actors"), and no credit card is required (Apify pricing). That covers the example above, but only while your total Apify use for the month, this actor plus anything else you run, stays under $5. Unused credit doesn't roll over to the next month.
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. In the input form, set Mode to
keyword-ranks, fill in Search keywords and Your / competitor apps to track, and turn on Only output tracked apps. - Click Save as a new task and run it once.
- Add a daily schedule for the task under Schedules (Apify schedules).
The code below does the same thing, if you'd rather script it.
Run it from code
Python, using the official apify-client package (version 3 or later):
# pip install "apify-client>=3"
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {
"mode": "keyword-ranks",
"keywords": ["email marketing", "sms marketing", "abandoned cart"],
"targetAppHandles": ["omnisend", "klaviyo-email-marketing"], # your app + competitors
"onlyTargetApps": True, # one row per keyword x tracked app (cheapest)
"maxResultsPerKeyword": 48, # scan the first two pages
}
run = client.actor("carmentara/shopify-app-intel").call(run_input=run_input)
for item in client.dataset(run.default_dataset_id).iterate_items():
r = item["keywordRank"]
where = f"#{r['position']}" if r["found"] else f"not in top {r['scannedResults']}"
print(f"{r['keyword']:<16} {r['handle']:<26} {where:<16} change: {r['rankChange']}")
Output from our test run on 4 October 2026 (the client also streams the run log first):
# email marketing omnisend #2 change: 0
# email marketing klaviyo-email-marketing #1 change: 0
# sms marketing omnisend #3 change: 0
# sms marketing klaviyo-email-marketing #1 change: 0
# abandoned cart omnisend not in top 48 change: None
# abandoned cart klaviyo-email-marketing not in top 48 change: None

change is None on the first run for a keyword and when an app wasn't found. The same input works in the Apify Console form, so you don't need code to start.
What a row looks like
A real target row from the same day:
{
"mode": "keyword-ranks",
"timestamp": "2026-10-04T14:50:14.907Z",
"keywordRank": {
"keyword": "email marketing",
"rowType": "target",
"position": 2,
"previousPosition": 2,
"rankChange": 0,
"page": 1,
"pagePosition": 2,
"isSponsored": false,
"handle": "omnisend",
"name": "Omnisend Email Marketing & SMS",
"rating": 4.7,
"reviews": 3182,
"pricingText": "Free plan available",
"builtForShopify": false,
"isTarget": true,
"found": true,
"sponsoredSlots": [],
"scannedResults": 24,
"totalResults": 3996,
"url": "https://apps.shopify.com/omnisend",
"searchUrl": "https://apps.shopify.com/search?q=email+marketing"
}
}
Turn Only output tracked apps off and you also get one result row for every app in the scanned results, in order. That's useful for seeing which competitors sit above you, but each of those rows is also billed at $0.002.

One limitation to know about: Shopify doesn't show ads to the servers Apify runs on, so sponsoredSlots is normally empty. Organic positions aren't affected.
Make it daily
- 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. See Apify schedules. - Keep the keyword list stable. Each run compares with the previous one for the same keyword, and each run's dataset becomes one day in your history.
Chart your rank history in Python
This script collects the target rows from your last 60 successful runs and draws one line per keyword and app:
# pip install "apify-client>=3" pandas matplotlib
import os
import matplotlib
matplotlib.use("Agg") # write a PNG, no display needed
import matplotlib.pyplot as plt
import pandas as pd
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
actor = client.actor("carmentara/shopify-app-intel")
# Your last 60 successful runs of the actor (scheduled or manual), newest first.
runs = actor.runs().list(desc=True, limit=60, status="SUCCEEDED").items
rows = []
for run in runs:
for item in client.dataset(run.default_dataset_id).iterate_items():
r = item.get("keywordRank")
if item.get("mode") != "keyword-ranks" or r["rowType"] != "target":
continue
rows.append({
"date": item["timestamp"][:10],
"keyword": r["keyword"],
"app": r["handle"],
"position": r["position"], # None = not found in the scanned results
})
df = pd.DataFrame(rows)
if df.empty:
raise SystemExit("No keyword-rank target rows yet: run keyword-ranks mode with targetAppHandles first.")
df["position"] = pd.to_numeric(df["position"]) # None -> NaN (a gap in the chart)
# One value per day, keyword and app (the latest run of the day wins).
history = (
df.drop_duplicates(["date", "keyword", "app"], keep="first") # runs are newest first
.pivot(index="date", columns=["keyword", "app"], values="position")
.sort_index()
)
history.to_csv("rank_history.csv")
print(history.tail(10))
ax = history.plot(marker="o", figsize=(10, 5), title="Shopify App Store keyword rank (1 = top)")
ax.invert_yaxis() # rank 1 at the top
ax.set_ylabel("Organic position")
plt.tight_layout()
plt.savefig("rank_history.png")
print("Saved rank_history.csv and rank_history.png")
If you only want the runs of one task, replace the runs = ... line with runs = client.task("your-username/your-task-name").runs().list(desc=True, limit=60, status="SUCCEEDED").items. Dates are in UTC.
Or pull today's ranks into a spreadsheet
Every run's results are available as CSV. This gets the latest successful run of your task, one row per keyword × app:
curl -fsS "https://api.apify.com/v2/actor-tasks/your-username~your-task-name/runs/last/dataset/items?status=SUCCEEDED&format=csv&fields=timestamp,keywordRank&unwind=keywordRank" \
-H "Authorization: Bearer $APIFY_TOKEN" > todays-ranks.csv
In Google Sheets, the same URL works with =IMPORTDATA("...&token=YOUR_APIFY_TOKEN"). Keep in mind that anyone who can edit the sheet can see the token. To build a history in Sheets instead of a daily snapshot, add Apify's Google Sheets integration to the task in append mode, so each run adds its rows below the previous ones.
Get an alert when a rank moves. Apify's Slack integration and webhooks fire on run events, such as a run that succeeded or failed. On their own, they tell you that a run finished, not that a rank changed. To get an alert only when rankChange isn't 0, or when an app drops out of the scanned results (found: false), route the run'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, Discord or email. rankChange is empty when an app wasn't found, so check found as well.
Tips for useful tracking
- Track the words merchants type, not your brand name. Start with your category's head terms ("email marketing"), then the specific jobs ("abandoned cart email", "back in stock alert").
- Add two or three competitors per keyword. Your own rank means more when you can see who moved around you.
- Scan deep enough. If you're usually on page 2 or 3, set
maxResultsPerKeywordto 48 or 72 so you get a number instead of "not found". - Write down your listing changes. Put the date of every title, subtitle or screenshot change next to the chart, so you can tell which change moved the rank.
- Watch the trend, not one day. A one-place move for a day is noise. A drop that holds for a week is worth a look.
Ready to automate your Shopify App Store SEO tracking?
For two or three keywords, a private window and a spreadsheet work. For a daily rank history of your app and your competitors that you don't have to maintain:
- Open the Shopify App Store Keyword Rank Tracker & Scraper on Apify.
- Pick
keyword-ranksmode and enter your keywords and app handles. - Schedule it daily. For small setups like the 10-keyword example above, it's covered by Apify's free $5 monthly credit.
Questions or feature requests? Open an issue on the actor's Issues tab.