cd /news/ai-search/building-a-keyword-rank-tracker-that… · home › topics › ai-search › article
[ARTICLE · art-161558] src=dev.to ↗ pub= topic=ai-search verified=true sentiment=↑ positive

Building a Keyword Rank Tracker That Also Flags AI Overviews (Python)

SteadyScrape built a Python-based keyword rank tracker that records organic and absolute positions, ranking URLs, and SERP features including AI Overviews, flagging cannibalization when a different page of the same site starts ranking. The design diffs successive runs to report position changes, new SERP features, and marks runs not comparable when result depth changes, and is packaged as the steady-scrape/google-rank-tracker Apify Actor built on a licensed SERP data API. The company disclosed it earns revenue when the hosted tool is used.

by read5 min views2 publishedOct 31, 2026

Rank tracking sounds simple: search a keyword, find your site, write down the position. The useful part is everything after that. Did it move? Did a different page of yours start ranking (cannibalization)? Did Google add an AI Overview that pushed your "position 2" below the fold?

This post walks through the design of a small rank tracker: what to store per keyword, how to diff runs, and which SERP features to record. The code works with any source of SERP data. At the end there's a hosted option.

Don't scrape Google results yourself. It's against Google's terms, gets blocked quickly, and results vary by IP location. In practice everyone uses a SERP API: you send keyword + location + language + device and get back parsed results. Whatever provider you choose, make sure it returns:

Store one record per keyword, keyed by everything that changes the SERP:

def key(keyword, location, language, device):
    return f"{keyword.lower()}|{location}|{language}|{device}"

def snapshot_row(keyword, serp, target_domain):
    mine = [r for r in serp["organic"] if r["domain"] == target_domain
            or r["domain"].endswith("." + target_domain)]
    best = mine[0] if mine else None
    return {
        "keyword": keyword,
        "position": best["position"] if best else None,
        "url": best["url"] if best else None,
        "otherUrls": [r["url"] for r in mine[1:]],
        "features": sorted(serp.get("features", [])),
        "top3": [r["domain"] for r in serp["organic"][:3]],
    }

Count subdomains as you (make it a setting), and keep the URL, not just the position: most rank-tracking insights come from URL changes.

def compare(prev, cur):
    if prev is None:
        return {"changeStatus": "first_check", "positionChange": None}
    p, c = prev["position"], cur["position"]
    if p is None and c is None:
        status, change = "not_ranked", None
    elif p is None:
        status, change = "new", None
    elif c is None:
        status, change = "lost", None
    else:
        change = p - c                       # positive = moved up
        status = "up" if change > 0 else "down" if change < 0 else "same"
    return {
        "changeStatus": status,
        "positionChange": change,
        "rankingUrlChanged": bool(p and c and prev["url"] != cur["url"]),
        "newFeatures": sorted(set(cur["features"]) - set(prev["features"])),
    }

rankingUrlChanged is your cannibalization alert: two of your pages are competing for one keyword, and Google is swapping between them. newFeatures tells you when, say, ai_overview or local_pack appears on a SERP where it wasn't before. Also, mark a run not_comparable if the depth changed (top 10 vs top 100) instead of reporting false "lost" rankings.

An organic position doesn't tell you how visible the result is. Position 2 under an AI Overview, a Local Pack and four ads sits far lower on the page than position 2 on a plain SERP. Record the absolute position too, and flag the features that matter to you: AI Overview, People Also Ask, featured snippet, Local Pack, shopping, video and forums.

Disclosure: we (SteadyScrape) built the tool below and get paid when it's used.

Google Rank Tracker API: Daily Keyword Positions (steady-scrape/google-rank-tracker) is an Apify Actor that implements this design on top of a licensed SERP data API. You give it a target (a domain, or an exact URL if only that page should count), up to 2,000 keywords, and a location, language and device. Each keyword comes back with its organic and absolute position, the ranking URL and any other URLs of yours on the page, the SERP features (and whether your site appears inside them), the top 3 competing domains, positions for up to 10 competitors you name, and, if you reuse the same trackerId, the change since the last run.

import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("steady-scrape/google-rank-tracker").call(run_input={
    "target": "apify.com",
    "keywords": ["scrape amazon prices", "instagram scraper", "web scraping api"],
    "location": "United States",
    "language": "English",
    "device": "desktop",
    "depth": "10",
    "queueMode": "standard",
    "trackerId": "apify-us-desktop",
})
for r in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(r["keyword"], r["position"], r["changeStatus"], r["serpFeatures"], r["aiOverviewCited"])

That's the Actor's example input. Here's what our test run of it returned on October 31, 2026:

Run it again with the same trackerId, or put it on an Apify schedule, and every row gets positionChange, changeStatus (up, down, same, new, lost and so on), previousRankingUrl and rankingUrlChanged. Set loadAiOverview: true to also load AI Overviews that Google shows asynchronously and check whether they cite you.

A few practical notes:

You pay per keyword that was checked successfully. Errors, invalid keywords and timeouts are free.

Event Price Apify Bronze / Silver / Gold plans
Keyword checked, top 10 $0.003 ($3 per 1,000) $0.0027 / $0.0024 / $0.00225
Keyword checked, top 30 $0.007 $0.0063 / $0.0056 / $0.00525
Keyword checked, top 100 $0.025 $0.0225 / $0.02 / $0.01875
Priority queue surcharge $0.0015 per 10 results checked same
Live mode surcharge $0.003 per 10 results checked same
AI Overview citations (only when one was loaded) $0.002 per keyword same
Run start (only after the first successful check) $0.015 same

Apify also adds its own $0.00005 start fee per run.

Examples at list price:

You can set a maximum cost per run. The Actor works out how many keywords fit before it starts and marks the rest as skipped, free of charge.

Whatever data source you use, the design is what makes a tracker useful: key by keyword + location + language + device, store the URL as well as the position, diff run over run, and record SERP features. AI Overviews are changing what "ranking #2" is worth, and a tracker that doesn't record them will report rankings that look better than they are.

── more in #ai-search 4 stories · sorted by recency
── more on @steadyscrape 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-a-keyword-r…] indexed:0 read:5min 2026-10-31 · —