Der Google Keyword Planner ist praktisch – aber bei großen Keyword-Listen, Automatisierung oder API-Nutzung stößt du schnell an Grenzen.
Wenn du 10.000 oder sogar 130.000 Keywords analysieren willst, brauchst du eine skalierbare, bezahlbare und flexible Lösung.
Genau hier kommt DataForSEO ins Spiel – in Kombination mit Python kannst du dir dein eigenes Keyword-Tool bauen. Schau dir hier das Video dazu an:
In diesem Tutorial zeige ich dir Schritt für Schritt, wie du das machst – inklusive Batching, Kostenkontrolle und Excel-Export.
Warum eine Alternative zum Keyword Planner?
Der Google Keyword Planner ist zwar solide, aber:
- stark limitiert bei Keyword-Mengen
- wenig flexibel für automatisierte Workflows
- API-Zugang nur mit aktivem Google Ads Account
Wenn du also große Keyword-Listen verarbeiten willst (z. B. aus BigQuery, SEO-Tools oder eigenen Datenquellen), brauchst du eine skalierbare Lösung – wie DataForSEO + Python.
Voraussetzungen
Python-Pakete installieren
pip install pandas requests openpyxl
DataForSEO-Zugang
Registriere dich auf DataForSEO und hole dir deine Zugangsdaten (Login & Passwort).
Damit kannst du ihre leistungsfähige API nutzen, die speziell für große Abfragen gedacht ist – die perfekte Keyword Planner Alternative für Entwickler:innen und SEOs.
API-Zugang
Registriere dich unter DataForSEO und hol dir:
- Login
- Passwort
Diese brauchst du zur Authentifizierung via Basic Auth
.
Logik des Setups
- Keywords aus Excel-Datei laden
- In Batches à 1.000 an die API senden
- Ergebnisse inkl. CPC, Suchvolumen und monatlicher Entwicklung abrufen
- Fortschritt, API-Kosten und Ergebnisse sichern (Pickle + Excel-Export)
- Nach Abbruch nahtlos wieder starten
Wichtige Features im Code
Batching:
Keywords werden in Gruppen à 1000 an die API geschickt, um Limits einzuhalten.
Pickle & Fortschritts-Tracking:
Falls das Skript abstürzt, kannst du nahtlos weitermachen – inklusive der Kosten, Fortschritte und Ergebnisse.
API-Kostenübersicht:
Nach jedem Batch werden die Kosten protokolliert – und am Ende als Tabelle in eine Excel-Datei exportiert.
Keyword-Filterung:
- Max. 80 Zeichen
- Max. 10 Wörter
- Bereinigung von Zeichen wie
™
,©
,®
etc.
Ordnerstruktur (empfohlen)
projekt/
│
├── keywords.xlsx ← Deine Keyword-Liste
├── get_sv_data4seo.py ← Hauptskript
├── backup_results.pkl ← automatische Zwischenspeicherung
├── final_results.xlsx ← Exportiertes Ergebnis (3 Sheets)
├── invalid_keywords.txt ← gefilterte / unbrauchbare Keywords
└── progress.txt (optional) ← optionales Fortschrittstracking
Vorteile gegenüber dem Keyword Planner
Google Keyword Planner | DataForSEO + Python (dieses Tutorial) |
---|---|
Limit bei Massenabfragen | Beliebig viele Batches möglich |
Unflexibel, Weboberfläche | Voll automatisiert via Code |
Kaum Clustering oder Export | Eigene Analysen & Filter möglich |
API nur mit Google Ads | API direkt & transparent nutzbar |
👉 Du bekommst also eine echte Alternative zum Keyword Planner, wenn du tiefere, schnellere oder umfangreichere Keyword-Daten brauchst.
Skript:
Es sind Funktionen im Skript (wie die advanced cleaning Methode der Keywords) enthalten, die ich nicht veröffentlichen kann, sofern wenn du Zugang möchtest bzw. Hilfe brauchst bei der Einrichtung dann buche dir hier einen Support Termin.
import os
import time
import json
import base64
import pandas as pd
import requests
import numpy as np
from dotenv import load_dotenv
load_dotenv()
## import word_frequency as wf advanced cleaning
import re
import unicodedata
# --- Konfiguration ---
PICKLE_BACKUP = "backup_results.pkl"
PROGRESS_FILE = "progress.txt"
BATCH_SIZE = 1000
LOCATION_CODE = 2276
LANGUAGE_CODE = "de"
MAX_RETRIES = 3
RETRY_DELAY = 3 # Sekunden
USERNAME = "login"
PASSWORD = "password"
AUTH_HEADER = os.getenv("d4seo") #base64.b64encode(f"{USERNAME}:{PASSWORD}".encode()).decode()
API_URL = "https://api.dataforseo.com/v3/keywords_data/google_ads/search_volume/live"
def return_path():
EXCEL_FILE = "keywords.xslx" # muss eine Spalte "keyword" enthalten
return EXCEL_FILE
def clean_keywords(keywords):
cleaned = keywords ## wf.basic_cleaning(keywords)
return cleaned
FORBIDDEN_CHARS = set("™©®✓•→←≠∞¿¡§¶…")
def is_valid_keyword(kw: str) -> bool:
try:
kw = kw.strip()
if not kw:
return False
# Entferne verbotene Zeichen
for char in FORBIDDEN_CHARS:
kw = kw.replace(char, "")
kw_norm = unicodedata.normalize("NFKC", kw)
# Entferne Steuerzeichen
kw_cleaned = ''.join(c for c in kw_norm if unicodedata.category(c)[0] != 'C')
# Prüfe verdächtige Zeichen mit Unicode-Name
for char in kw_cleaned:
try:
name = unicodedata.name(char)
if not any(part in name for part in ["LATIN", "DIGIT", "SPACE", "DASH", "HYPHEN"]):
return False
except ValueError:
return False
# Filter kaputter Kodierungen
if re.search(r"[ãåÓ€]", kw_cleaned):
return False
# Zeichenverhältnis
valid_chars = re.findall(r"[a-zA-Z0-9äöüßéèêáàâíìîóòôúùûčšžăîâăëç\- ]", kw_cleaned.lower())
ratio = len(valid_chars) / max(len(kw_cleaned), 1)
if ratio < 0.7:
return False
# Mindestens 3 Buchstaben
if len(re.findall(r"[a-zA-Z]", kw_cleaned)) < 3:
return False
return True
except Exception:
return False
# ----------------- Hilfsfunktionen -----------------
def load_keywords_from_excel(filepath: str) -> list:
df = pd.read_excel(filepath)
return df["keyword"].dropna().astype(str).tolist()
def batch_keywords(keywords: list, size: int) -> list:
return [keywords[i:i + size] for i in range(0, len(keywords), size)]
def send_batch_request(keywords_batch: list) -> dict:
payload = [{
"keywords": keywords_batch,
"location_code": LOCATION_CODE,
"language_code": LANGUAGE_CODE,
"sort_by": "relevance"
}]
headers = {
'Authorization': f'Basic {AUTH_HEADER}',
'Content-Type': 'application/json'
}
response = requests.post(API_URL, headers=headers, data=json.dumps(payload))
if response.status_code == 200:
return response.json()
else:
raise Exception(f"HTTP {response.status_code}: {response.text}")
def parse_response(response_json: dict) -> tuple[pd.DataFrame, pd.DataFrame]:
results = []
monthly_results = []
error_keywords = []
tasks = response_json.get("tasks", [])
for task in tasks:
items = task.get("result", [])
for item in items:
try:
keyword = item.get("keyword")
results.append({
"keyword": keyword,
"search_volume": item.get("search_volume"),
"competition": item.get("competition"),
"cpc": item.get("cpc"),
"currency": item.get("currency")
})
monthly_searches = item.get("monthly_searches")
if isinstance(monthly_searches, list):
for ms in monthly_searches:
monthly_results.append({
"keyword": keyword,
"year": ms.get("year"),
"month": ms.get("month"),
"search_volume": ms.get("search_volume")
})
except Exception as e:
error_keywords.append((item.get("keyword"), str(e)))
print(f"Fehler beim Verarbeiten von Keyword '{item.get('keyword')}': {e}")
return pd.DataFrame(results), pd.DataFrame(monthly_results)
def save_progress(index: int):
with open(PROGRESS_FILE, "w") as f:
f.write(str(index))
def load_progress() -> int:
if os.path.exists(PROGRESS_FILE):
with open(PROGRESS_FILE, "r") as f:
return int(f.read())
return 0
def load_existing_results() -> tuple[pd.DataFrame, pd.DataFrame]:
if os.path.exists(PICKLE_BACKUP):
data = pd.read_pickle(PICKLE_BACKUP)
return (
data.get("results", pd.DataFrame()),
data.get("monthly", pd.DataFrame()),
data.get("api_costs", pd.DataFrame()),
data.get("status", {}).get("progress_index", 0),
data.get("status", {}).get("total_cost", 0.0)
)
else:
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), 0, 0.0
def save_all_to_pickle(results_df, monthly_df, api_costs_df, progress_index, total_api_cost):
data = {
"results": results_df,
"monthly": monthly_df,
"api_costs": api_costs_df,
"status": {
"progress_index": progress_index,
"total_cost": total_api_cost
}
}
pd.to_pickle(data, PICKLE_BACKUP)
# ----------------- Filter Keywords -----------------'''
# dataforseo: max. 80 Zeichen
# max. 10 Wörter
# Umwandlung in Kleinbuchstaben
# :param keywords:
# :param log_file:
# :return:
# '''
def filter_keywords(keywords: list[str], log_file: str = "invalid_keywords.txt") -> list[str]:
filtered = []
invalid = []
for kw in keywords:
kw_clean = kw.strip().lower()
if (
len(kw_clean) <= 80
and len(kw_clean.split()) <= 10
and is_valid_keyword(kw_clean)
):
filtered.append(kw_clean)
else:
invalid.append(kw.strip())
if invalid:
with open(log_file, "w", encoding="utf-8") as f:
f.write("Ungültige Keywords (zu lang, zu viele Wörter oder ungültige Zeichen):\n\n")
for kw in invalid:
f.write(f"{kw}\n")
print(f"{len(invalid)} ungültige Keywords wurden ignoriert und in '{log_file}' gespeichert.")
return filtered
def clean_keyword_simple(kw: str) -> str:
"""
Entfernt gezielt geschützte Zeichen wie ™, ©, ® etc. aus dem Keyword.
Gibt das bereinigte Keyword zurück.
"""
FORBIDDEN_CHARS = set("™©®✓•→←≠∞¿¡§¶…")
kw_cleaned = kw.strip()
for char in FORBIDDEN_CHARS:
kw_cleaned = kw_cleaned.replace(char, "")
return kw_cleaned
def main():
print("Lade Keywords aus Excel-Datei...")
excel_file = return_path()
all_keywords = load_keywords_from_excel(excel_file)
all_keywords = [clean_keyword_simple(kw) for kw in all_keywords] ## clean keywords from trademark and copyright
all_keywords = filter_keywords(all_keywords) ## filter too long keywords
keyword_batches = batch_keywords(all_keywords, BATCH_SIZE)
start_index = load_progress()
results_df, monthly_df, api_costs_df, start_index, total_api_cost = load_existing_results()
#monthly_df = pd.DataFrame()
total_api_cost = 0.0
api_costs_list = []
print(f"{len(all_keywords)} Keywords geladen. Starte ab Batch {start_index + 1} von {len(keyword_batches)}.")
for idx in range(start_index, len(keyword_batches)):
batch = keyword_batches[idx]
print(f"\nVerarbeite Batch {idx + 1}/{len(keyword_batches)} ({len(batch)} Keywords)...")
success = False
for attempt in range(1, MAX_RETRIES + 1):
try:
## Clean keywords from apostrophe
cleaned_batch = clean_keywords(batch)
response_json = send_batch_request(cleaned_batch)
time.sleep(1)
# API-Kosten extrahieren
batch_cost = response_json.get("cost", 0.0)
total_api_cost += batch_cost
api_costs_list.append({
"batch_index": idx + 1,
"keyword_count": len(batch),
"cost_usd": batch_cost
})
print(f"API-Kosten für diesen Batch: {batch_cost:.3f} USD")
df_keywords, df_monthly = parse_response(response_json)
results_df = pd.concat([results_df, df_keywords], ignore_index=True)
monthly_df = pd.concat([monthly_df, df_monthly], ignore_index=True)
save_all_to_pickle(results_df, monthly_df, api_costs_df, idx + 1, total_api_cost)
print(f"Fortschritt gespeichert – Batch {idx + 1}, Gesamtkosten: {total_api_cost:.3f} USD")
save_progress(idx + 1)
success = True
break
except Exception as e:
print(f"Fehler bei Batch {idx + 1} (Versuch {attempt}): {e}")
if attempt < MAX_RETRIES:
print(f"Warte {RETRY_DELAY} Sekunden vor erneutem Versuch...")
time.sleep(RETRY_DELAY)
else:
print("Maximale Anzahl an Versuchen erreicht. Skript wird abgebrochen.")
return
if success:
time.sleep(1.5) # API-Rate-Limiting
print(f"\nGesamte API-Kosten: {total_api_cost:.3f} USD")
# API-Kosten-Tabelle
api_costs_df = pd.DataFrame(api_costs_list)
api_costs_df.loc[len(api_costs_df.index)] = {
"batch_index": "Gesamt",
"keyword_count": api_costs_df["keyword_count"].sum(),
"cost_usd": total_api_cost
}
# Export in Excel
with pd.ExcelWriter("final_results.xlsx") as writer:
results_df.to_excel(writer, sheet_name="Keyword-Daten", index=False)
monthly_df.to_excel(writer, sheet_name="Monatliche Suche", index=False)
api_costs_df.to_excel(writer, sheet_name="API-Kosten", index=False)
print("\nExport abgeschlossen: final_results.xlsx")
if __name__ == "__main__":
main()
Exportiertes Ergebnis
Am Ende dieses Tutorials hast du eine Excel-Datei mit:
- Keyword-Übersicht: Suchvolumen, Wettbewerb, CPC
- Zeitreihe: monatliche Volumenentwicklung
- API-Kostenübersicht je Batch
Du kannst daraus z. B. Cluster bilden, saisonale Trends analysieren oder dein eigenes Keyword-Tool bauen.
Fazit
Dieses Setup ist ideal für:
- datengetriebene SEO-Analysen
- Performance-Marketing mit großen Keyword-Mengen
- Reporting, Forecasting & Clustering
Wenn du nach einer Alternative zum Keyword Planner suchst, die du vollständig steuern und skalieren kannst – dann ist Python + DataForSEO genau das Richtige.