DEV Community

dodou
dodou

Posted on

You don't need a $99/month SaaS to track 1,000 keywords. You need a Python script, a Postgres table, and a SERP API.

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)
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Cron (every Monday 06:00 UTC):

0 6 * * 1 cd /home/tracker && ./venv/bin/python track.py >> ranktracker.log 2>&1
Enter fullscreen mode Exit fullscreen mode

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)