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.