Warum zeigt der Date Picker der Google Search Console das Jahr 1969 an
Der Ausfall der Datumsauswahl im Leistungsbericht der Google Search Console zeigt, wie riskant die ausschließliche Abhängigkeit von einer Weboberfläche ist. Am 13. Oktober 2026 berichtete Barry Schwartz bei Search Engine Roundtable, dass der Bericht bei einem benutzerdefinierten Zeitraum das Formular mit der Meldung Date can't be after 1969-12-31 ablehnt, also: Das Datum darf nicht nach dem 31. Dezember 1969 liegen. Die Auswahl wurde repariert, fiel am 14. Oktober erneut aus und funktionierte einige Stunden später wieder. Während des Ausfalls können Teams historische Daten im Browser nicht filtern.
Google hat keine Ursache genannt, aber das Datum selbst ist aufschlussreich. Nach POSIX zählt die Unix-Zeit die Sekunden seit Mitternacht des 1. Januar 1970 UTC. Erhält ein Datumsfeld in JavaScript einen Nullwert, einen leeren String oder ein noch nicht geladenes Objekt, landet es leicht beim Wert 0. In Zeitzonen mit negativem Versatz gegenüber UTC fällt dieser Nullpunkt auf den 31. Dezember 1969, in den US-Zeitzonen zwischen 16:00 und 19:00 Uhr. Die wahrscheinlichste Erklärung ist daher ein Zeitstempel mit dem Wert null, der als obere Grenze des Zeitraums im Validator ankam.
Unter dieser Annahme vergleicht der Validator das gewählte Datum mit einer Grenze im Jahr 1969. Jedes reale Datum liegt später, also wertet die Regel den Zeitraum als ungültig und blockiert das Diagramm.
Für große Portale, Onlineshops und Agenturen mit Dutzenden betreuten Websites bedeutet ein solcher Ausfall, dass die laufende Diagnose stillsteht. Die Arbeit eines SEO-Teams darf nicht von der Stabilität einer einzelnen Single-Page-Application (SPA) abhängen. Die Lösung für durchgehende Analysen ist eine direkte Anbindung an die offizielle Google Search Console API.
Architektur fuer den Datenzugriff auf die Search Console
[GSC Web-Oberflaeche] [Search Analytics API v1]
│ │
Frontend-Fehler & Blockaden Direkter REST-Endpunkt
1.000 Zeilen Limit 25.000 Zeilen pro Abfrage
Unix-Epoch-Validierungsfehler 50.000 Zeilen pro Tag
│ │
▼ ▼
[Webbrowser] [Lokale DB / BigQuery]Welche Grenzen hat die Oberfläche der Google Search Console im Vergleich zur API
Die grafische Oberfläche der Google Search Console ist für einen schnellen Blick auf Basiskennzahlen gebaut, nicht für Data Engineering. Selbst wenn die Datumsauswahl fehlerfrei läuft, setzt das Webpanel Grenzen, die das Bild der Sichtbarkeit verzerren.
Die erste Grenze ist das feste Anzeigelimit. Die Tabelle in der Search Console zeigt höchstens 1.000 Zeilen. Bei Websites mit Hunderten Unterseiten und Zehntausenden Long-Tail-Suchanfragen sieht man nur einen Teil der Begriffe, die tatsächlich Traffic bringen.
Das zweite Problem ist die Aufbewahrung. Die Search Console löscht Daten, die älter als 16 Monate sind. Ohne automatisierte Archivierung ist ein mehrjähriger Vergleich (Year-over-Year) nicht möglich. Der dritte Faktor ist die Anonymisierung seltener Suchanfragen (anonymized queries): Google filtert sie aus Datenschutzgründen im Panel, in der API und im BigQuery-Export heraus. Die API kennt dafür das Tabellenlimit von 1.000 Zeilen nicht, und Abfragen, die für jeden Tag einzeln laufen, liefern bis zu 50.000 Zeilen pro Tag und Suchtyp (Google-Dokumentation).
| Kriterium | Weboberfläche der GSC | Search Analytics API |
|---|---|---|
| Zeilenlimit pro Anfrage | 1.000 Zeilen | 25.000 Zeilen |
| Maximale Zeilenzahl | 1.000 (feste Tabellengrenze) | 50.000 pro Tag und Suchtyp (Paginierung über startRow) |
| Aufbewahrung der Daten | 16 Monate | 16 Monate, länger nach Speicherung in eigener Datenbank |
| Anfälligkeit für Frontend-Fehler | Hoch (UI-Fehler, JavaScript-Blockaden) | Keine UI-Schicht (JSON-Antwort eines REST-Endpunkts) |
| Automatisierung des Reportings | Manuelles Klicken nötig | Vollständig automatisierbar (Cron, Worker, CI) |
| Mehrdimensionale Aufschlüsselung | Eingeschränkte Filterkombinationen | Volle Kombination: page + query + device + country |
Die Nutzung der API macht das Reporting unabhängig von Frontend-Fehlern bei Google und gibt Entwicklern die Kontrolle über das Datenarchiv.
Wie richtet man einen Service Account für die Google Search Console API ein
Um ohne wiederholtes Browser-Login per OAuth2 für Endnutzer auf die Search Analytics API zuzugreifen, richtet man in der Google Cloud Platform einen Service Account ein. Das Dienstkonto arbeitet als eigenständige technische Identität mit eigenem kryptografischem Schlüsselpaar.
Der erste Schritt ist ein Projekt in der Google Cloud Console (console.cloud.google.com). Danach öffnet man den Bereich APIs & Services, sucht die Google Search Console API und aktiviert sie.
Im zweiten Schritt legt man im Bereich IAM & Admin, Unterbereich Service Accounts, ein Dienstkonto mit einem eindeutigen Namen an, zum Beispiel gsc-analytics-exporter. Anschließend wechselt man zum Reiter Keys und erstellt über Add Key und Create new key einen neuen privaten Schlüssel im JSON-Format. Die Datei wird lokal heruntergeladen. Sie enthält die Schlüssel-ID, die E-Mail-Adresse des Dienstkontos und den privaten RSA-Schlüssel im PEM-Format.
{
"type": "service_account",
"project_id": "enterprise-seo-pipeline",
"private_key_id": "7f8a9b1c2d3e4f5a",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9...\n-----END PRIVATE KEY-----\n",
"client_email": "gsc-analytics-exporter@enterprise-seo-pipeline.iam.gserviceaccount.com",
"client_id": "104958673829104958673",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}Im dritten Schritt autorisiert man das Dienstkonto in der Google Search Console. Man kopiert die Adresse aus client_email, öffnet die Search Console für die bestätigte Property, wählt Einstellungen (Settings) und dann Nutzer und Berechtigungen (Users and permissions) und fügt die E-Mail-Adresse mit der Rolle Eingeschränkt (Restricted) oder Vollständig (Full) hinzu. Ab diesem Moment darf das Dienstkonto die Endpunkte der Search Console für diese Property abfragen.
Die JSON-Datei mit dem privaten Schlüssel darf nie in einem öffentlichen Repository landen. Sie gehört in die .gitignore oder wird auf dem Produktionsserver als verschlüsselte Umgebungsvariable übergeben.
Wie prüft man, auf welche Search Console Properties ein Service Account Zugriff hat
Der häufigste Grund für einen 403-Fehler nach der Einrichtung eines Dienstkontos ist eine Abweichung zwischen der Property-Adresse im Skript und der, die die Search Console kennt. Statt zu raten, lohnt sich eine Abfrage der API. Die Methode sites.list liefert die Liste der Properties, auf die das Konto Zugriff hat. Jeder Eintrag enthält zwei Felder: siteUrl in genau der Schreibweise, die die übrigen Methoden erwarten, und permissionLevel (Dokumentation der Ressource sites).
Das Feld permissionLevel nimmt einen von vier Werten an: siteOwner (Inhaber), siteFullUser (vollständige Berechtigung), siteRestrictedUser (eingeschränkte Berechtigung) und siteUnverifiedUser (Nutzer ohne bestätigte Property). Fehlt die Property in der Liste, wurde die E-Mail-Adresse des Dienstkontos in den Einstellungen der Search Console nicht hinzugefügt oder bei einer anderen Property eingetragen, zum Beispiel beim Präfix https://www. statt bei der Domain-Property.
const { data } = await searchconsole.sites.list();
for (const site of data.siteEntry || []) {
console.log(site.siteUrl, site.permissionLevel);
}
// sc-domain:wppoland.com siteFullUserDieselbe Ressource hat die Methoden add, delete und get. Für den reinen Datenabruf reichen der Scope webmasters.readonly und ein list-Aufruf beim Start des Skripts: Weicht die zurückgegebene siteUrl von der konfigurierten ab, kann das Skript mit einer klaren Meldung abbrechen, bevor es Hunderte fehlerhafte Anfragen sendet.
Wie ruft man Daten für sc-domain und URL-Präfixe in Node.js ab
Bei der Arbeit mit der Search Console API stoßen Entwickler oft auf einen 403- oder 404-Fehler, der auf ein falsches Format der Property-Kennung (siteUrl) zurückgeht. Google unterscheidet zwei Arten von Properties:
- Domain-Property: fasst alle Protokolle und Subdomains zusammen. In der API braucht ihre Kennung das Präfix
sc-domain:, zum Beispielsc-domain:wppoland.com. - URL-Präfix-Property: bezieht sich auf ein bestimmtes Protokoll und einen Pfad. In der API ist ihre Kennung die vollständige URL mit abschließendem Schrägstrich, zum Beispiel
https://wppoland.com/de/.
Bei mehrsprachigen Websites fragt man entweder die URL-Präfix-Properties der einzelnen Sprachversionen ab oder die Domain-Property und filtert die URL-Pfade anschließend im Code.
Das folgende Node.js-Skript zeigt eine vollständige Implementierung des Datenabrufs mit Paginierung und Speicherung in einer JSON-Datei. Es nutzt die offizielle Bibliothek googleapis.
import fs from "node:fs";
import path from "node:path";
import { google } from "googleapis";
async function exportSearchConsoleMetrics() {
const credentialsPath = path.resolve("./scripts/gsc-service-account.json");
const creds = JSON.parse(fs.readFileSync(credentialsPath, "utf8"));
if (creds.private_key) {
creds.private_key = creds.private_key.replace(/\\n/g, "\n");
}
const auth = new google.auth.GoogleAuth({
credentials: creds,
scopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
});
const client = await auth.getClient();
const searchconsole = google.searchconsole({ version: "v1", auth: client });
const siteUrl = "sc-domain:wppoland.com";
// Drei Tage Puffer einplanen, da Google-Performancedaten nachtraeglich konsolidiert werden
const endDate = new Date(Date.now() - 3 * 864e5).toISOString().slice(0, 10);
const startDate = new Date(Date.now() - 33 * 864e5).toISOString().slice(0, 10);
console.log(`Starte Datenabruf fuer ${siteUrl} von ${startDate} bis ${endDate}`);
const allRecords = [];
let startRow = 0;
const rowLimit = 25000;
while (true) {
const response = await searchconsole.searchanalytics.query({
siteUrl,
requestBody: {
startDate,
endDate,
dimensions: ["page", "query", "device", "country"],
rowLimit,
startRow,
},
});
const rows = response.data.rows || [];
allRecords.push(...rows);
console.log(`Stapel geladen: ${rows.length} Zeilen (Gesamt: ${allRecords.length})`);
if (rows.length < rowLimit) {
break;
}
startRow += rowLimit;
}
const targetFile = "./reports/gsc-performance-data.json";
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
fs.writeFileSync(targetFile, JSON.stringify(allRecords, null, 2), "utf8");
console.log(`Export abgeschlossen. ${allRecords.length} Datensaetze gespeichert in ${targetFile}`);
}
exportSearchConsoleMetrics().catch((err) => {
console.error("Fehler beim Abruf der Search Console API:", err.message);
process.exit(1);
});Der Code durchläuft die Ergebnisseiten über den Parameter startRow. Enthält die Antwort weniger Zeilen als das Limit von 25.000, endet die Schleife. So lässt sich die Historie der Suchanfragen bis zum Tageslimit der API vollständig abrufen.
Wie ruft man Daten aus der Google Search Console mit Python ab
Für Data Engineers und Teams, die mit Pandas und Jupyter arbeiten, ist ein Python-Skript das bequemste Werkzeug, um Daten aus der Search Console zu extrahieren und zu bereinigen. Das folgende Beispiel ruft Daten mit Aufschlüsselung nach Tagen ab und speichert sie im spaltenorientierten Format Parquet, das weniger Platz als CSV belegt und die Spaltentypen erhält.
Für das Skript werden die Bibliotheken google-api-python-client, google-auth sowie pandas und pyarrow benötigt:
pip install google-api-python-client google-auth pandas pyarrowImplementierung des Skripts:
import datetime
import pandas as pd
from google.oauth2 import service_account
from googleapiclient.discovery import build
def fetch_search_console_dataframe(site_url: str, days: int = 30) -> pd.DataFrame:
key_path = "scripts/gsc-service-account.json"
credentials = service_account.Credentials.from_service_account_file(
key_path,
scopes=["https://www.googleapis.com/auth/webmasters.readonly"]
)
service = build("searchconsole", "v1", credentials=credentials)
end_date = (datetime.date.today() - datetime.timedelta(days=3)).strftime("%Y-%m-%d")
start_date = (datetime.date.today() - datetime.timedelta(days=days + 3)).strftime("%Y-%m-%d")
collected_items = []
start_row = 0
row_limit = 25000
while True:
body = {
"startDate": start_date,
"endDate": end_date,
"dimensions": ["date", "page", "query", "country", "device"],
"rowLimit": row_limit,
"startRow": start_row
}
response = service.searchanalytics().query(
siteUrl=site_url,
body=body
).execute()
rows = response.get("rows", [])
if not rows:
break
for item in rows:
collected_items.append({
"date": item["keys"][0],
"page": item["keys"][1],
"query": item["keys"][2],
"country": item["keys"][3],
"device": item["keys"][4],
"clicks": item["clicks"],
"impressions": item["impressions"],
"ctr": item["ctr"],
"position": round(item["position"], 2)
})
print(f"Stapel verarbeitet: {len(rows)} Zeilen (startRow={start_row})")
if len(rows) < row_limit:
break
start_row += row_limit
df = pd.DataFrame(collected_items)
return df
if __name__ == "__main__":
property_id = "sc-domain:wppoland.com"
df_data = fetch_search_console_dataframe(property_id, days=60)
if not df_data.empty:
df_data["date"] = pd.to_datetime(df_data["date"])
df_data.to_parquet("reports/gsc_performance.parquet", index=False)
print(f"{len(df_data)} Datensaetze erfolgreich als Parquet gesichert.")
else:
print("Keine Daten empfangen.")Das spaltenorientierte Parquet-Format erlaubt schnelle Aggregationen und Gruppierungen von Suchanfragen nach Produktkategorien oder WordPress-Templates, ohne den Arbeitsspeicher des Servers zu belasten.
Wie ruft man aktuelle und stündliche Daten über die Search Console API ab
Die Skripte oben lassen die letzten drei Tage aus, weil Google angibt, dass Daten in der Regel nach 2 bis 3 Tagen verfügbar sind (Leitfaden zum Abruf aller Daten). Derselbe Leitfaden empfiehlt einen einfachen Test: Eine Abfrage mit der Dimension date über die letzten 10 Tage zeigt, welcher Tag der letzte verfügbare ist.
Ob die API vorläufige Daten liefert, steuert der Parameter dataState (Dokumentation der Methode query):
Wert von dataState | Was die API liefert |
|---|---|
final (oder kein Parameter) | Nur finalisierte Daten |
all | Auch frische Daten, die sich noch ändern können |
hourly_all | Stündlich aufgeschlüsselte, unvollständige Daten, zu verwenden mit der Dimension hour |
Bei dataState: "all" und Gruppierung nach Datum enthält die Antwort in den Metadaten das Feld first_incomplete_date. Alle Werte nach diesem Datum können sich noch deutlich ändern. Ein Archivierungsskript sollte sie daher getrennt speichern oder beim nächsten Lauf überschreiben. Zeitangaben in den Metadaten stehen in der Zeitzone America/Los_Angeles, was beim Zusammenführen mit Serverlogs in mitteleuropäischer Zeit wichtig ist.
Die Dimension hour hat Google am 9. April 2025 eingeführt. Das Panel zeigt Stundendaten nur für die letzten 24 Stunden, die API liefert sie für bis zu 10 Tage (Ankündigung im Google Search Central Blog). So lässt sich der letzte Tag mit demselben Wochentag der Vorwoche vergleichen, etwa nach der Veröffentlichung eines Beitrags oder nach dem Deployment eines neuen Themes.
{
"startDate": "2026-10-15",
"endDate": "2026-10-15",
"dataState": "hourly_all",
"dimensions": ["hour"]
}Die Schlüssel in der Antwort haben die Form 2026-10-15T00:00:00-07:00, also die Stunde in der Zeitzone von Los Angeles mit Versatz. Vor dem Speichern in der Datenbank lohnt sich die Umrechnung in UTC.
Wie filtert man Seiten und Suchanfragen in der Search Analytics API
Eine Abfrage ohne Filter liefert den gesamten Traffic der Property. Auf einer mehrsprachigen Website braucht man meist einen Ausschnitt, etwa nur Adressen unter /de/. Dafür gibt es das Array dimensionFilterGroups. Gefiltert werden kann nach den Dimensionen country, device, page, query und searchAppearance. Verfügbare Operatoren sind (Dokumentation der Methode query):
equals(Standard) undnotEquals: exakte Übereinstimmung, beipageundquerymit Unterscheidung von Groß- und Kleinschreibung,containsundnotContains: Teilübereinstimmung ohne Unterscheidung von Groß- und Kleinschreibung,includingRegexundexcludingRegex: regulärer Ausdruck in RE2-Syntax.
{
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"dimensions": ["page", "query"],
"type": "web",
"dimensionFilterGroups": [{
"filters": [
{ "dimension": "page", "operator": "includingRegex", "expression": "^https://wppoland\\.com/de/" },
{ "dimension": "query", "operator": "notContains", "expression": "wppoland" }
]
}],
"rowLimit": 25000
}Der zweite Filter schneidet Markensuchen ab, ein typischer Schritt vor der Long-Tail-Analyse. Der Parameter type ersetzt das veraltete searchType und nimmt die Werte web (Standard, der Tab Alle ohne Discover und Google News), image, video, news (Tab News), discover und googleNews an. Jeder Typ braucht eine eigene Abfrage.
Der Parameter aggregationType legt fest, ob Kennzahlen nach Property (byProperty) oder nach URL (byPage) berechnet werden; der Standardwert auto überlässt Google die Wahl. Der Wert byProperty funktioniert nicht für die Typen discover und googleNews.
Warum stimmt die Summe aus der API nicht mit dem Diagramm in der Search Console überein
Nach dem ersten Export bemerkt fast jedes Team, dass die Summe der Klicks aus den Zeilen niedriger ist als die Zahl im Diagramm des Panels. Das ist kein Fehler im Skript. Google nennt drei Ursachen (Leitfaden zum Abruf aller Daten):
- Die API liefert höchstens 50.000 Zeilen pro Tag und Suchtyp, sortiert nach Klicks. Bei großen Websites fehlt der Long Tail unterhalb dieser Schwelle in der Antwort.
- Bei Gruppierung nach den Dimensionen
pageoderquerykann das System einen Teil der Daten weglassen, damit die Berechnung machbar bleibt. - Impressionen, Klicks, Position und CTR werden bei Aggregation nach Seite anders berechnet als bei Aggregation nach Property.
Dazu kommen anonymisierte Suchanfragen, die in keiner Quelle enthalten sind. Praktische Folge: Gesamtwerte des Traffics holt man aus einer Abfrage ohne die Dimensionen page und query (nur date), die Aufschlüsselung nach Suchbegriffen behandelt man als nach Klicks geordnete Stichprobe. Google empfiehlt außerdem, die Daten Tag für Tag abzurufen und in Schritten von 25.000 Zeilen zu paginieren, bis eine leere Seite zurückkommt, und zwar getrennt für jeden Suchtyp.
Wie speichert man Daten aus der Google Search Console in Google BigQuery
Die ausfallsicherste Lösung für große Websites ist der automatische Export der Search-Console-Daten in das Data Warehouse Google BigQuery. Google hat den Bulk Data Export am 21. Februar 2023 eingeführt: Der Mechanismus überträgt täglich einen Abzug der Leistungsdaten in das angegebene BigQuery-Projekt (Ankündigung im Google Search Central Blog).
Vorteile dieses Ansatzes:
- Keine 16-Monats-Grenze: In BigQuery gespeicherte Daten bleiben so lange erhalten, wie man sie braucht.
- Der Export unterliegt nicht dem täglichen Zeilenlimit der API. Google filtert nur anonymisierte Suchanfragen aus Datenschutzgründen heraus.
- Die Daten aus der Search Console lassen sich mit Tabellen der WordPress-Datenbank verknüpfen (WooCommerce-Bestellungen, Warenkörbe, Conversions).
Der Export wird in zwei Schritten eingerichtet. In der Google Cloud Console aktiviert man die BigQuery API im Projekt und erteilt dem Dienstkonto der Search Console Berechtigungen. In den Einstellungen der Search Console gibt man die Projekt-ID und den Speicherort des Datasets an. Den Export kann nur der Inhaber der Property einrichten. Die Search Console simuliert zuerst einen Export; gelingt die Simulation, erhalten alle Inhaber eine E-Mail, und die regelmäßigen Exporte starten innerhalb von 48 Stunden. Bei einem Fehler erscheint sofort eine Warnung.
Der Export legt drei Tabellen an:
searchdata_site_impression: nach Property aggregierte Daten, mit Suchanfrage, Land, Suchtyp und Gerät.searchdata_url_impression: nach URL aggregierte Daten, mit Details zu Suchanfragen und Rich Results.ExportLog: Protokoll darüber, welche Daten an einem Tag gespeichert wurden. Fehlgeschlagene Exporte werden hier nicht erfasst.
Beispielabfrage in BigQuery, die Unterseiten mit vielen Impressionen und niedriger CTR findet (Kandidaten für die Optimierung von Title-Tags und Überschriften):
SELECT
url,
query,
SUM(impressions) AS total_impressions,
SUM(clicks) AS total_clicks,
ROUND(SAFE_DIVIDE(SUM(clicks), SUM(impressions)) * 100, 2) AS calculated_ctr,
ROUND(SUM(sum_position) / SUM(impressions) + 1, 1) AS avg_position
FROM
`enterprise-project.searchconsole.searchdata_url_impression`
WHERE
data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND country = 'deu'
GROUP BY
url,
query
HAVING
total_impressions > 500
AND calculated_ctr < 2.0
AND avg_position BETWEEN 3.0 AND 10.0
ORDER BY
total_impressions DESC
LIMIT 100;Die Tabelle searchdata_url_impression enthält unter anderem die Spalten data_date, url, query, country, search_type, impressions, clicks und sum_position sowie Flags für Rich-Result-Typen (das Beispiel von Google filtert search_type = 'WEB'). Die Spalte sum_position zählt die Position ab null, deshalb berechnet man die durchschnittliche Position als SUM(sum_position) / SUM(impressions) + 1, wie im Beispiel von Google. Die Abfrage findet Suchbegriffe auf der ersten Ergebnisseite, die unter 2 Prozent Klicks bringen. Das sind Kandidaten für einen Test mit neuem Title, neuer H1 und neuer Beschreibung; die Wirkung der Änderung muss man in den folgenden Wochen messen, statt sie vorauszusetzen.
Search Console zu BigQuery Pipeline
[Google Search Console] ──(Bulk Export)──> [Google BigQuery]
│
├──> [Looker Studio Dashboard]
├──> [Ranking-Verlust-Monitoring]
└──> [WooCommerce-Umsatz-Korrektur]Wann wählt man die Search Analytics API und wann den Bulk Data Export
Beide Quellen liefern dieselben Kennzahlen, lösen aber unterschiedliche Probleme. Google schreibt in der Ankündigung des Exports, dass er besonders für große Websites mit Zehntausenden Seiten oder mit Traffic aus Zehntausenden Suchanfragen pro Tag nützlich ist, während kleine und mittlere Websites über die Oberfläche, den Looker-Studio-Connector oder die Search Analytics API Zugriff auf alle ihre Daten haben (Google Search Central Blog).
| Kriterium | Search Analytics API | Bulk Data Export |
|---|---|---|
| Zeilenlimit | 25.000 pro Anfrage, 50.000 pro Tag und Suchtyp | Kein tägliches Zeilenlimit |
| Anonymisierte Suchanfragen | Ausgelassen | Ausgelassen |
| Aktualität | Vorläufige (all) und stündliche (hourly_all) Daten | Täglicher Abzug |
| Wer kann es einrichten | Jeder Nutzer mit Zugriff auf die Property | Nur der Inhaber der Property |
| Wohin die Daten gehen | Dorthin, wo Ihr Skript sie speichert | In Ihr BigQuery-Projekt |
In der Praxis ergänzen sich beide Ansätze. Der Export nach BigQuery dient als Archiv und Quelle für Long-Tail-Analysen, ein schlankes API-Skript mit dataState: "hourly_all" überwacht die letzten Stunden nach einer Veröffentlichung oder einem Deployment. Für ein kleines WordPress-Blog reicht meist das Skript aus den vorherigen Abschnitten, einmal täglich per Cron ausgeführt.
Wie verknüpft man Search Console Daten mit der WordPress-Datenbank
Rohdaten aus der Search Console gewinnen den größten geschäftlichen Wert, wenn man sie mit der internen Inhaltsstruktur des CMS verbindet. Ein klassischer Export aus der Search Console enthält nur vollständige URLs als Zeichenketten. Er sagt nichts über den Beitragstyp (Beitrag, WooCommerce-Produkt, Landingpage), das Datum der letzten Änderung im Gutenberg-Editor oder den Autor.
Die Integration besteht darin, den URL-Pfad in die Objekt-ID in WordPress (post_id) zu übersetzen. Dafür gibt es in WordPress die Funktion url_to_postid($url) oder den direkten Abgleich des Slugs in der Tabelle wp_posts.
Der folgende PHP-Code zeigt, wie man die per API importierten Suchdaten dem richtigen Produkt oder Beitrag in der Datenbank zuordnet:
<?php
/**
* Verknuepfung von Search Console Performancedaten mit Beitraegen
*/
declare(strict_types=1);
namespace WPPoland\Analytics;
class SearchConsoleIngestor {
public function syncUrlMetrics(string $url, int $clicks, int $impressions, float $position): void {
$path = wp_parse_url($url, PHP_URL_PATH);
if (!$path) {
return;
}
$postId = url_to_postid($url);
if ($postId === 0) {
$postId = $this->resolveTaxonomyTerm($path);
}
if ($postId > 0) {
update_post_meta($postId, '_gsc_clicks_30d', $clicks);
update_post_meta($postId, '_gsc_impressions_30d', $impressions);
update_post_meta($postId, '_gsc_position_avg', $position);
update_post_meta($postId, '_gsc_last_synced', current_time('mysql', true));
}
}
private function resolveTaxonomyTerm(string $path): int {
global $wpdb;
$slug = trim($path, '/');
$parts = explode('/', $slug);
$termSlug = end($parts);
$termId = $wpdb->get_var($wpdb->prepare(
"SELECT t.term_id FROM {$wpdb->terms} t
JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
WHERE t.slug = %s LIMIT 1",
$termSlug
));
return $termId ? (int)$termId : 0;
}
}Mit dieser Verknüpfung sieht das Redaktionsteam im WordPress-Backend die genaue Zahl der Impressionen und die Position jeder Unterseite. Fällt die durchschnittliche Position eines Beitrags nach einem Theme-Update oder einem neuen Deployment von Platz 4 auf Platz 18, kann das System automatisch eine Benachrichtigung in Slack senden oder eine Audit-Aufgabe für den Entwickler anlegen.
Bei Architekturen mit Headless WordPress oder statischen Seitengeneratoren lassen sich die Daten aus der Search Console API zur Build-Zeit abrufen und für dynamische Bereiche mit beliebten Themen und verwandten Beiträgen nutzen.
Wie prüft man den Indexierungsstatus exportierter URLs mit der URL Inspection API
Ein Klickrückgang eines Beitrags in den Search-Analytics-Daten sagt nicht, ob die Seite noch im Index ist. Die Antwort liefert die URL Inspection API, die dasselbe Dienstkonto und dieselben Scopes (webmasters oder webmasters.readonly) nutzt. Die Anfrage lautet POST https://searchconsole.googleapis.com/v1/urlInspection/index:inspect mit den Feldern inspectionUrl (vollständige Adresse, die zur Property gehören muss), siteUrl (im selben Format wie zuvor: sc-domain: oder Präfix mit Schrägstrich) und dem optionalen languageCode (Dokumentation der Methode inspect).
Das Ergebnis indexStatusResult enthält unter anderem (Beschreibung von UrlInspectionResult):
| Feld | Bedeutung |
|---|---|
verdict | Gesamtergebnis: ob die Adresse indexiert ist |
coverageState | Ob Google die Seite gefunden und indexiert hat |
indexingState | Ob eine noindex-Regel die Indexierung blockiert |
robotsTxtState | Ob robots.txt das Crawling blockiert |
lastCrawlTime | Wann der Haupt-Crawler die Adresse zuletzt abgerufen hat |
googleCanonical und userCanonical | Von Google gewählte und auf der Seite deklarierte kanonische URL |
In WordPress lässt sich das gut mit der Klasse SearchConsoleIngestor aus dem vorherigen Abschnitt verbinden: Fällt die Position eines Beitrags, prüft das Skript seine Adresse und speichert coverageState und googleCanonical in den Post-Meta. Eine Abweichung zwischen der kanonischen URL von Google und der des SEO-Plugins erklärt einen Rückgang oft schneller als eine Analyse der Suchbegriffe. Das Limit ist allerdings niedrig: 2.000 Anfragen pro Tag und 600 pro Minute pro Website (Limits der Search Console API). Prüfen Sie daher nur Adressen mit einem Rückgangssignal, nicht die ganze Sitemap. Die Methode zeigt die Version aus dem Google-Index und testet die Adresse nicht live. Den Unterschied zwischen diesem Werkzeug und der Indexing API beschreibt unser Leitfaden zu GSC und Indexing API.
Wie geht man mit Abfragelimits und Fehlern der API um
Die Search Analytics API hat Limits, die in der Google-Dokumentation beschrieben sind. Eine Überschreitung endet mit einem Kontingentfehler, und das Skript muss warten:
| Geltungsbereich | Wert |
|---|---|
| Pro Website (Abfragen derselben Property) | 1.200 Abfragen pro Minute |
| Pro Nutzer (Abfragen desselben Kontos) | 1.200 Abfragen pro Minute |
| Pro Google-Cloud-Projekt | 40.000 Abfragen pro Minute und 30.000.000 pro Tag |
Zusätzlich gilt ein Lastkontingent (load quota), gemessen in 10-Minuten- und Tagesfenstern. Laut Google sind Abfragen teuer, wenn man nach Seite oder Suchanfrage gruppiert oder filtert, und die Last steigt mit der Länge des Zeitraums. Google rät ausdrücklich davon ab, dieselben Daten wiederholt abzurufen, etwa bei jedem Lauf den gesamten Vormonat. Ein Archivierungsskript sollte daher nur fehlende Tage nachladen.
Damit Skripte, die Millionen Datensätze für große Onlineshops abrufen, zuverlässig laufen, braucht es drei Schutzmechanismen:
- Exponentielles Backoff (Exponential Backoff): Bei einem Fehlercode 429 oder 503 bricht das Skript nicht ab, sondern wartet vor dem nächsten Versuch eine wachsende Zahl von Sekunden (2 hoch n plus zufälliger Jitter).
- Aufteilung nach einzelnen Tagen (Daily Date Chunking): Statt die Daten eines ganzen Jahres in einem Aufruf mit riesiger Paginierung abzurufen, teilt man die Aufgabe in Tagesschleifen. Scheitert eine Abfrage an einem kurzen Netzwerkfehler, wiederholt man nur den fehlenden Tag.
- Zwischenspeichern der Zugriffstoken: Die Google-Clientbibliotheken erneuern das OAuth2-Token anhand des Dienstkonto-Schlüssels automatisch, in Serverless-Umgebungen (AWS Lambda, Cloudflare Workers) sollte man aber sicherstellen, dass das Token nicht für jede einzelne HTTP-Anfrage neu erzeugt wird.
Der folgende Code zeigt eine Abfrage, die fehlgeschlagene Anfragen automatisch wiederholt:
async function executeResilientQuery(queryAction, maxRetries = 5) {
let attempt = 0;
while (attempt < maxRetries) {
try {
return await queryAction();
} catch (err) {
attempt++;
const isRateLimited = err.code === 429 || err.status === 429;
const isServerIssue = err.code >= 500 && err.code < 600;
if ((isRateLimited || isServerIssue) && attempt < maxRetries) {
const delayMs = Math.pow(2, attempt) * 1000 + Math.random() * 500;
console.warn(`Fehler ${err.code}. Neuer Versuch in ${Math.round(delayMs)} ms (Versuch ${attempt}/${maxRetries})...`);
await new Promise((resolve) => setTimeout(resolve, delayMs));
} else {
throw err;
}
}
}
}Diese drei Mechanismen sorgen dafür, dass die Archivierung der SEO-Daten unabhängig von Fehlern im Browser-Panel der Google Search Console läuft.
Wie verwaltet man SEO-Daten aus der Search Console API
Fehler der grafischen Oberfläche, wie der Ausfall der Datumsauswahl und der Sprung des Kalenders ins Jahr 1969, erinnern daran, dass die Analysedaten einer Website im System des Betreibers liegen sollten und nicht nur in einem externen Panel.
Eine eigene Pipeline auf Basis der Search Console API bringt messbare Vorteile:
- Bis zu 50.000 Zeilen pro Tag und Suchtyp statt 1.000 Tabellenzeilen im Panel, mit dem Bulk Data Export der vollständige Datensatz ohne anonymisierte Suchanfragen.
- Reporting unabhängig von Frontend-Fehlern bei Google.
- Schutz vor dem Verlust historischer Daten nach 16 Monaten durch Archivierung in BigQuery oder einer relationalen Datenbank.
- Direkte Korrelation des Traffics mit den Umsätzen eines WooCommerce-Shops und mit Änderungen im Code des WordPress-Themes.
Für fortgeschrittene Analysen, automatisiertes Reporting oder ein technisches Audit lohnt sich ein Gespräch mit einem WordPress-Entwickler, der die Datenpipeline mit der Architektur Ihres Shops verbindet und gegen Ausfälle absichert. Für technische Beratung können Sie unser Team kontaktieren.







