Wants to build their own rank tracker rather than pay $99-500/month for a dashboard tool they can only partly customise?
This guide covers everything you need: what a rank tracker API actually returns, how to query it across multiple search engines, how to handle the data, and how to build a simple scheduled rank checker in under 100 lines of Python.
API used throughout: Serpent API
What a rank tracker API actually returns (and what yours should)
Not all rank tracker APIs return the same data. The minimum viable response for a rank checker contains: keyword, search engine, target domain, rank position (integer), URL of the ranked page, title and snippet.
A modern rank tracker API in 2026 should also return:
- AI Overview presence flag: is there a generative AI block above organic results for this query?
- AI Overview cited sources: does your domain appear inside the AI Overview?
- Pixel Y coordinate: where on the rendered page does your result actually appear?
- Position 1 can be below the fold if an AI Overview occupies 300px above it.
SERP features above your result: ads, Local Pack, Shopping — how many elements push your result down?
Device and location context: mobile vs desktop rankings differ; country-level targeting affects results
Why pixel position matters specifically in 2026: AI Overviews appear on ~55% of informational Google queries and occupy 200-400px before organic results begin. A rank tracker that only returns "position 1" without telling you whether position 1 is above or below the fold is giving you incomplete data.
The keyword cannibalization problem (and why two rank tracker APIs are better than one)
A critical finding from building rank tracking systems: if you query Google rank position and AI rank citation position separately, you get two different signals that together tell a much more complete story.
Signal 1 — Traditional rank position (this article): where does your domain appear in the organic list? This is what all rank trackers have measured since 2005.
Signal 2 — AI citation position (separate endpoint): does your brand appear in the AI-generated answer above the organic list? A page can rank position 1 organically and not be cited in the AI Overview. A page can rank position 15 and be cited in the AI Overview. These are independent signals.
Building the rank tracker: Python implementation
Here is the complete implementation for a multi-engine keyword rank checker. It queries Google, Bing, Yahoo, and DuckDuckGo simultaneously and returns a structured rank report.
1. Setup and authentication
pip install requests python-dotenv schedule pandas
# config.pyimport osfrom dotenv import load_dotenvload_dotenv()API_KEY = os.getenv("SERPENT_API_KEY")BASE_URL = "https://apiserpent.com/api"
2. The core rank checker function
# rank_tracker.pyimport requestsfrom dataclasses import dataclassfrom typing import Optional@dataclassclass RankResult: keyword: str engine: str domain: str rank: Optional[int] # None = not in top 100 url: Optional[str] pixel_y: Optional[int] # vertical position on page above_fold: Optional[bool] # True = visible without scroll in_ai_overview: bool # cited in AI Overview ai_overview_present: bool # AIO exists for this queryclass RankChecker: ENGINES = ["google", "bing", "yahoo", "duckduckgo"] FOLD_THRESHOLD_PX = 600 # typical laptop fold line def __init__(self, api_key: str): self.session = requests.Session() self.session.headers["X-API-Key"] = api_key def check_rank( self, keyword: str, domain: str, engine: str = "google", country: str = "us", device: str = "desktop" ) -> RankResult: """Check rank for a single keyword on a single engine.""" resp = self.session.get( f"https://apiserpent.com/api/search", params={ "q": keyword, "engine": engine, "gl": country, "device": device, "num": 100 # check top 100 results }, timeout=20 ) resp.raise_for_status() data = resp.json() # Find domain in organic results rank = None ranked_url = None pixel_y = None above_fold = None for result in data.get("organic_results", []): if domain.lower() in result.get("link", "").lower(): rank = result["position"] ranked_url = result["link"] pos = result.get("pixel_position", {}) pixel_y = pos.get("y") if pixel_y is not None: above_fold = pixel_y < self.FOLD_THRESHOLD_PX break # Check AI Overview aio = data.get("ai_overview", {}) ai_overview_present = bool(aio) in_ai_overview = any( domain.lower() in source.get("link", "").lower() for source in aio.get("sources", []) ) return RankResult( keyword=keyword, engine=engine, domain=domain, rank=rank, url=ranked_url, pixel_y=pixel_y, above_fold=above_fold, in_ai_overview=in_ai_overview, ai_overview_present=ai_overview_present
3. Multi-engine rank report
def check_all_engines( self, keyword: str, domain: str, engines: list = None ) -> list[RankResult]: """Check rank across multiple engines simultaneously.""" import concurrent.futures engines = engines or self.ENGINES results = [] with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: futures = { executor.submit( self.check_rank, keyword, domain, engine ): engine for engine in engines } for future in concurrent.futures.as_completed(futures): try: results.append(future.result()) except Exception as e: print(f"Error on {futures[future]}: {e}") return results def bulk_check( self, keywords: list[str], domain: str, engines: list = None ) -> dict: """ Check multiple keywords across engines. Returns dict keyed by keyword. """ return { kw: self.check_all_engines(kw, domain, engines) for kw in keywords }
4. Reporting and output
import pandas as pdfrom datetime import datetimedef results_to_dataframe( results: dict, domain: str) -> pd.DataFrame: """Convert rank results to a pandas DataFrame.""" rows = [] for keyword, engine_results in results.items(): for r in engine_results: rows.append({ "date": datetime.now().date(), "keyword": r.keyword, "engine": r.engine, "domain": domain, "rank": r.rank or "Not in top 100", "url": r.url or "—", "pixel_y": r.pixel_y, "above_fold": r.above_fold, "in_ai_overview": r.in_ai_overview, "ai_overview_present": r.ai_overview_present }) return pd.DataFrame(rows)# Usage examplefrom config import API_KEYchecker = RankChecker(API_KEY)keywords = [ "rank tracker api", "keyword ranking api", "google rank checker api", "serp tracker google yahoo bing"]print(f"Checking {len(keywords)} keywords across 4 engines...")results = checker.bulk_check(keywords, "apiserpent.com")df = results_to_dataframe(results, "apiserpent.com")df.to_csv(f"rank_report_{datetime.now().date()}.csv", index=False)# Summaryfor kw, engine_results in results.items(): print(f"\n{kw}:") for r in engine_results: status = f"rank {r.rank}" if r.rank else "not found" fold = "above fold" if r.above_fold else "below fold" if r.above_fold is False else "n/a" aio = "in AIO" if r.in_ai_overview else ("AIO present" if r.ai_overview_present else "no AIO") print(f" {r.engine:12} → {status:20} {fold:12} {aio}")
5. Scheduled daily tracking
import scheduleimport timedef daily_rank_check(): """Run daily at 6am and save results.""" checker = RankChecker(API_KEY) results = checker.bulk_check(KEYWORDS, TARGET_DOMAIN) df = results_to_dataframe(results, TARGET_DOMAIN) # Append to master CSV master_path = "rank_history.csv" try: existing = pd.read_csv(master_path) pd.concat([existing, df]).to_csv(master_path, index=False) except FileNotFoundError: df.to_csv(master_path, index=False) print(f"[{datetime.now()}] Daily rank check complete. {len(df)} rows saved.")schedule.every().day.at("06:00").do(daily_rank_check)while True: schedule.run_pending() time.sleep(60)
Cost calculation for your rank tracker
Before building, estimate your monthly API cost. The formula:
# Cost calculatorkeywords = 500 # keywords you trackengines = 4 # Google, Bing, Yahoo, DuckDuckGochecks_per_day = 1 # daily trackingdays = 30monthly_calls = keywords * engines * checks_per_day * days# = 500 × 4 × 1 × 30 = 60,000 calls# Serpent API Scale tier: $0.03 per 10,000 pagescost_per_call = 0.03 / 10000 * 10 # $0.03 per 10K, so per 1K = $0.003monthly_cost = monthly_calls * cost_per_call / 1000print(f"Monthly calls: {monthly_calls:,}")print(f"Monthly cost: ${monthly_cost:.2f}")# → Monthly calls: 60,000# → Monthly cost: $0.18
Decision matrix: when to use a rank tracker API vs a rank tracking tool
| Factor | Use an API | Use a dashboard tool |
|---|---|---|
| Custom dashboard | Yes ✓ — build your own | No — use theirs |
| Multiple clients | Yes ✓ — white label possible | Expensive at scale |
| Technical setup | Required — code needed | No code needed |
| Data ownership | Yes ✓ — export freely | Locked in platform |
| Cost at 500 keywords | Serpent: ~$0.18/mo | Tools: $99-500/mo |
| AI Overview tracking | Yes ✓ — returned in base response | Rare, often add-on |
| Pixel positions | Yes ✓ — unique to Serpent | Not available anywhere |
Top comments (0)