Keyword-Rankings mit Python überwachen

Ein minimaler Rank-Tracker in Python: Abfrage, Position aus organic_results ziehen, in SQLite speichern, und die Credit-Rechnung hinter der Tracking-Tiefe.

Ein Rank-Tracker muss kein Produkt sein. Für eine Handvoll Keywords und eine Domain ist es eine Schleife, eine Datenbankdatei und eine Möglichkeit, den Tag zu erkennen, an dem sich etwas bewegt hat. Hier ist das Ganze in etwa sechzig Zeilen.

Der Aufbau

Vier Schritte, nach Zeitplan ausgeführt: jedes Keyword mit fester Country und Location durch /serp schicken, deine Domain in organic_results finden, die Position in SQLite schreiben und mit der Zeile von gestern vergleichen.

import sqlite3
import requests
from datetime import date
from urllib.parse import urlparse

API_KEY = "faf_live_..."
DB_PATH = "rankings.db"
DOMAIN = "fastaf.co"
COUNTRY = "us"

KEYWORDS = [
    "web scraping api",
    "serp api pricing",
    "google search api alternative",
]


def init_db():
    conn = sqlite3.connect(DB_PATH)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS rankings (
            checked_on TEXT,
            keyword TEXT,
            domain TEXT,
            position INTEGER,
            PRIMARY KEY (checked_on, keyword, domain)
        )
    """)
    return conn

Eine Position abrufen

organic_results kommt mit einem rank-Feld schon an jedem Eintrag zurück, deine eigene zu finden ist also nur ein Abgleich des Hostnamens von link mit der Domain, exakt oder als Subdomain (ein einfaches domain in link trifft auch https://example.org/review/fastaf.co). Tiefe ist eine Schleife über page, da Google nicht mehr 100 Ergebnisse in einem Request liefert:

def on_domain(link, domain):
    host = urlparse(link).hostname or ""
    return host == domain or host.endswith("." + domain)


def find_position(keyword, domain, country=COUNTRY, location=None, max_pages=1):
    for page in range(max_pages):
        params = {"query": keyword, "country": country, "page": page}
        if location:
            params["location"] = location

        resp = requests.get(
            "https://api.fastaf.co/v1/serp",
            params=params,
            headers={"Authorization": f"Bearer {API_KEY}"},
            timeout=60,
        )
        resp.raise_for_status()

        for result in resp.json().get("organic_results", []):
            if on_domain(result.get("link", ""), domain):
                return result["rank"]

    return None  # nicht gefunden innerhalb max_pages

location grenzt die Suche auf einen bestimmten Ort ein (zum Beispiel eine Stadt) statt nur auf ein Land, was bei allem mit lokaler Absicht zählt. page ist die Tiefensteuerung: jede Seite ist ein separater, abrechenbarer Request, jeweils zehn Ergebnisse.

Eine Liste tracken und den Tageslauf speichern

def track(conn, keywords, domain, max_pages=1):
    today = date.today().isoformat()
    for keyword in keywords:
        position = find_position(keyword, domain, max_pages=max_pages)
        conn.execute(
            "INSERT OR REPLACE INTO rankings VALUES (?, ?, ?, ?)",
            (today, keyword, domain, position),
        )
        print(f"{keyword}: {position}")
    conn.commit()


if __name__ == "__main__":
    conn = init_db()
    track(conn, KEYWORDS, DOMAIN)

Lauf das einmal am Tag (Cron, eine geplante Funktion, was auch immer du schon hast) und rankings.db sammelt eine Zeile pro Keyword pro Tag.

Das Tages-Delta berechnen

Der Sinn, Verlauf zu speichern, ist das Delta, nicht der Schnappschuss. Zieh die letzten zwei Zeilen jedes Keywords und bilde die Differenz:

def deltas(conn, domain):
    rows = conn.execute("""
        SELECT keyword, checked_on, position
        FROM rankings
        WHERE domain = ?
        ORDER BY keyword, checked_on
    """, (domain,)).fetchall()

    by_keyword = {}
    for keyword, checked_on, position in rows:
        by_keyword.setdefault(keyword, []).append((checked_on, position))

    for keyword, history in by_keyword.items():
        if len(history) < 2:
            continue
        (_, prev), (_, curr) = history[-2], history[-1]
        if prev is None or curr is None:
            continue
        change = prev - curr  # positiv bedeutet, es ist nach oben gerutscht
        if change != 0:
            print(f"{keyword}: {prev} -> {curr} ({change:+d})")

Die Credit-Rechnung hinter „Tiefe”

Das ist der Teil, der ein Credit-Budget still und leise ruiniert, wenn er nicht vorab durchgerechnet wird. Ein /serp-Request kostet unabhängig von der Tiefe 5 Credits, aber „Top 100” sind zehn separate Requests, nicht einer, da Google nicht mehr einen einzelnen Request mit 100 Ergebnissen zulässt.

Setup Requests/Tag Credits/Tag Credits (30 Tage) Passendes Paket
500 Keywords, täglich, Tiefe 10 (nur Seite 0) 500 2.500 75.000 Growth, 29 $
500 Keywords, täglich, Tiefe 100 (10 Seiten) 5.000 25.000 750.000 Pro, 299 $

Tiefe 10 prüft, ob du überhaupt auf der ersten Seite rankst, was für die meisten Tracking-Zwecke reicht. Tiefe 100 brauchst du, wenn du auch wissen willst, wo du auf den Seiten 2 bis 10 stehst, und das kostet zehnmal so viel für dieselbe Keyword-Liste. Setz max_pages bewusst, nicht aus Gewohnheit: ein Tracker, der still von Tiefe 10 auf Tiefe 100 hochgesetzt wird, ist eine 10-fache Credit-Rechnung für dieselbe Keyword-Liste.

Zweimal am Tag statt einmal zu prüfen verdoppelt jede Zahl in dieser Tabelle; wöchentlich statt täglich zu prüfen teilt sie durch sieben. Wähl eine Frequenz, die die Ranking-Bewegung, die dich interessiert, tatsächlich braucht, nicht das kürzeste Intervall, das dein Scheduler anbietet.

Kurz zusammengefasst

Ein Rank-Tracker ist ein /serp-Aufruf, ein Domain-Abgleich gegen organic_results und eine SQLite-Tabelle, nichts Exotischeres als das. Die Zahl, die deine Rechnung bestimmt, ist Tiefe mal Frequenz mal Keyword-Anzahl, nicht das Tool. Rechne das durch, bevor du den Job planst: Preise zeigen die Credit-Kosten pro Paket, und die Docs decken die vollständige Parameterliste von /serp ab, einschließlich location und device.