SEO rank tracking requires checking where your pages appear in Google search results for target keywords on a regular schedule. Commercial SEO tools charge hundreds of dollars per month for this capability. The Scavio API lets you build your own rank tracker with a simple Python script that stores daily positions in a CSV or database and alerts you to significant changes. This tutorial covers building a rank tracking pipeline that checks up to 20 keywords per run and logs position history.
Prerequisites
- Python 3.8 or higher
- requests library installed
- A Scavio API key
- A list of target keywords and your domain to track
Walkthrough
Step 1: Define keywords and target domain
Create a list of keyword strings and the domain you want to track. The script will scan results for any URL containing your domain.
KEYWORDS = ["python web framework", "fastapi tutorial", "django vs flask"]
TARGET_DOMAIN = "mysite.com"Step 2: Fetch SERP and find rank
For each keyword, POST to the Scavio Google endpoint and scan organic_results for a URL matching your domain. Return the position if found.
def get_rank(keyword: str, domain: str) -> int | None:
data = search_google(keyword)
for r in data.get("organic_results", []):
if domain in r.get("link", ""):
return r["position"]
return NoneStep 3: Save results to CSV
Append each keyword's rank result with a timestamp to a CSV file so you can track positions over time.
import csv
from datetime import date
def save_rank(keyword: str, rank: int | None) -> None:
with open("rankings.csv", "a", newline="") as f:
writer = csv.writer(f)
writer.writerow([date.today().isoformat(), keyword, rank or "not ranked"])Step 4: Run the daily tracking job
Iterate over all keywords, fetch ranks, save results, and print a summary. Schedule this script to run daily with cron or a task scheduler.
def run_tracker():
for kw in KEYWORDS:
rank = get_rank(kw, TARGET_DOMAIN)
save_rank(kw, rank)
status = f"#{rank}" if rank else "not ranked"
print(f"{kw}: {status}")
run_tracker()Python Example
import os
import csv
import requests
from datetime import date
API_KEY = os.environ.get("SCAVIO_API_KEY", "your_scavio_api_key")
ENDPOINT = "https://api.scavio.dev/api/v1/search"
KEYWORDS = ["python web framework", "fastapi tutorial"]
TARGET_DOMAIN = "mysite.com"
def search_google(q: str) -> dict:
r = requests.post(ENDPOINT, headers={"x-api-key": API_KEY},
json={"query": q, "country_code": "us"})
r.raise_for_status()
return r.json()
def get_rank(keyword: str) -> int | None:
data = search_google(keyword)
for r in data.get("organic_results", []):
if TARGET_DOMAIN in r.get("link", ""):
return r["position"]
return None
def run():
with open("rankings.csv", "a", newline="") as f:
w = csv.writer(f)
for kw in KEYWORDS:
rank = get_rank(kw)
w.writerow([date.today().isoformat(), kw, rank or ""])
print(f"{kw}: {'#' + str(rank) if rank else 'not ranked'}")
if __name__ == "__main__":
run()JavaScript Example
const API_KEY = process.env.SCAVIO_API_KEY || "your_scavio_api_key";
const ENDPOINT = "https://api.scavio.dev/api/v1/search";
const fs = require("fs");
const KEYWORDS = ["python web framework", "fastapi tutorial"];
const TARGET_DOMAIN = "mysite.com";
async function getRank(keyword) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query: keyword, country_code: "us" })
});
const data = await res.json();
const match = (data.organic_results || []).find(r => r.link.includes(TARGET_DOMAIN));
return match ? match.position : null;
}
async function run() {
const today = new Date().toISOString().slice(0, 10);
for (const kw of KEYWORDS) {
const rank = await getRank(kw);
const line = `${today},${kw},${rank || ""}\n`;
fs.appendFileSync("rankings.csv", line);
console.log(`${kw}: ${rank ? "#" + rank : "not ranked"}`);
}
}
run().catch(console.error);Expected Output
python web framework: #4
fastapi tutorial: #2
rankings.csv preview:
2026-04-16,python web framework,4
2026-04-16,fastapi tutorial,2