Most rank trackers fall into one of two failure modes: the SaaS ones charge $50-200/month and lock your data behind a dashboard, and the DIY scrapers break the moment Google changes its SERP layout. The middle path is "API in the middle, my own database on top, my own dashboard in front." That gives you API reliability and full control of the data model.
import os, time, requests, psycopg2
from datetime import datetime
API_KEY = os.environ["SERPBASE_KEY"]
ENDPOINT = "https://api.serpbase.dev/google/search"
def fetch_serp(keyword, hl="en", gl="us"):
r = requests.post(
ENDPOINT,
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"q": keyword, "hl": hl, "gl": gl, "num": 10},
timeout=10,
)
r.raise_for_status()
return r.json()
def extract_positions(payload, target):
hits = []
for i, item in enumerate(payload.get("organic", []), start=1):
if target in item.get("link", ""):
hits.append({"position": i, "url": item["link"], "title": item.get("title")})
return hits
def run():
target = "example.com"
with psycopg2.connect(os.environ["DB_DSN"]) as conn, conn.cursor() as cur:
cur.execute("SELECT id, keyword FROM tracked_keywords WHERE active = true")
keywords = cur.fetchall()
for kw_id, kw in keywords:
payload = fetch_serp(kw)
for hit in extract_positions(payload, target):
cur.execute(
"INSERT INTO rank_history (keyword_id, position, url, title, checked_at) "
"VALUES (%s, %s, %s, %s, %s)",
(kw_id, hit["position"], hit["url"], hit["title"], datetime.utcnow()),
)
time.sleep(0.5)
Schema (two tables, three indexes, done):
CREATE TABLE tracked_keywords (
id SERIAL PRIMARY KEY, keyword TEXT NOT NULL, active BOOLEAN DEFAULT true
);
CREATE TABLE rank_history (
id BIGSERIAL PRIMARY KEY, keyword_id INT REFERENCES tracked_keywords(id),
position INT NOT NULL, url TEXT NOT NULL, title TEXT, checked_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX rank_history_kw_time_idx ON rank_history (keyword_id, checked_at DESC);
Cron (every Monday 06:00 UTC):
0 6 * * 1 cd /home/tracker && ./venv/bin/python track.py >> ranktracker.log 2>&1
Cost math. 1,000 keywords × 1 check/week × 4 weeks = 4,000 searches/month. At $0.30 per 1,000 searches, that's $1.20/month. The SerpBase Starter Boost tier — $3 for 10,000 searches, expiring one month after purchase — covers this workload for 2.5 months. Scale to daily checks (30k/month) and the Growth tier ($50 / 125k credits) gives you 4 months of daily tracking. Each /google/search call is 1 credit; P50 latency sits at ~1.4s; SLA is 99.9%.
The dashboard is a SQL view plus Metabase or Grafana on top. Full repo with retries, CSV export, and a Notion-friendly markdown summary: [github.com//rank-tracker].
Top comments (0)