Dlaczego date picker w Google Search Console pokazuje rok 1969
Awaria selektora dat w raporcie skuteczności Google Search Console ujawnia słabość polegania wyłącznie na interfejsie webowym. 13 października 2026 roku Barry Schwartz opisał w Search Engine Roundtable, że przy niestandardowym zakresie dat raport odrzuca formularz komunikatem Date can't be after 1969-12-31, czyli: data nie może być późniejsza niż 31 grudnia 1969 roku. Selektor naprawiono, 14 października zepsuł się ponownie, a po kilku godzinach znów działał. Na czas awarii analitycy tracą możliwość filtrowania danych historycznych z poziomu przeglądarki.
Google nie podał przyczyny, ale sama data dużo mówi. W standardzie POSIX czas uniksowy to liczba sekund od północy 1 stycznia 1970 roku UTC. Jeśli pole daty w JavaScripcie dostanie wartość null, pusty ciąg albo nieprzygotowany obiekt, łatwo kończy się na wartości 0. W strefach czasowych z ujemnym przesunięciem względem UTC punkt zerowy wypada 31 grudnia 1969 roku, w strefach amerykańskich między 16:00 a 19:00. Dlatego najbardziej prawdopodobnym wyjaśnieniem jest znacznik czasu równy zeru, który trafił do walidatora jako górna granica zakresu.
Przy takim założeniu walidator porównuje wybraną datę z granicą ustawioną na 1969 rok. Każda realna data jest późniejsza, więc reguła uznaje zakres za niepoprawny i blokuje wykres.
Dla serwisów o dużej skali operacyjnej, sklepów e-commerce i agencji zarządzających dziesiątkami portali taka awaria oznacza paraliż bieżącej diagnostyki. Praca zespołów SEO nie może zależeć od stabilności pojedynczej aplikacji jednostronicowej (SPA). Rozwiązaniem zapewniającym pełną ciągłość analityczną jest wdrożenie bezpośredniej integracji z oficjalnym Google Search Console API.
Architektura dostępu do danych Search Console
[Interfejs Webowy GSC] [Search Analytics API v1]
│ │
Błędy UI, limit 1000 wierszy Stabilny endpoint REST
Problem Unix Epoch (1969) 25 000 wierszy w żądaniu
Brak eksportu automatycznego 50 000 wierszy dziennie
│ │
▼ ▼
[Przeglądarka www] [Lokalna baza / BigQuery]Jakie są limity interfejsu Google Search Console w porównaniu do API
Interfejs graficzny Google Search Console został zaprojektowany z myślą o szybkim podglądzie podstawowych wskaźników, a nie o zaawansowanej inżynierii danych. Nawet gdy selektor dat działa prawidłowo, panel webowy nakłada ograniczenia, które zniekształcają obraz widoczności domeny.
Pierwszym ograniczeniem jest sztywny limit prezentacji danych. Tabela w panelu Search Console wyświetla maksymalnie 1000 wierszy. W przypadku portali posiadających setki podstron i dziesiątki tysięcy zapytań z długiego ogona (long-tail keywords) użytkownik widzi tylko część fraz, które przynoszą ruch.
Drugim problemem jest retencja danych. Google Search Console usuwa rekordy starsze niż 16 miesięcy. Jeśli portal nie posiada zautomatyzowanego mechanizmu archiwizacji, porównanie wyników rok do roku w ujęciu wieloletnim jest niemożliwe. Trzecim czynnikiem jest anonimizacja rzadkich zapytań (anonymized queries): Google odfiltrowuje je ze względu na prywatność w panelu, w API i w eksporcie do BigQuery. API nie ma za to limitu 1000 wierszy tabeli, a zapytania wykonywane osobno dla każdego dnia zwracają do 50 000 wierszy dziennie na typ wyszukiwania (dokumentacja Google).
| Cecha | Interfejs webowy GSC | Search Analytics API |
|---|---|---|
| Limit wierszy na żądanie | 1 000 wierszy | 25 000 wierszy |
| Maksymalna liczba wierszy | 1 000 (sztywny limit tabeli) | 50 000 dziennie na typ wyszukiwania (stronicowanie przez startRow) |
| Okres przechowywania danych | 16 miesięcy | 16 miesięcy, dłużej po zapisaniu we własnej bazie |
| Podatność na błędy frontendu | Wysoka (błędy UI, blokady JS) | Brak warstwy UI (odpowiedź JSON z endpointu REST) |
| Automatyzacja raportowania | Wymaga ręcznego klikania | Pełna automatyzacja (cron, workers, CI) |
| Rozbicie wielowymiarowe | Ograniczone filtry wzajemne | Pełna kombinacja: page + query + device + country |
Wykorzystanie API uniezależnia raportowanie od błędów frontendu Google i daje programistom kontrolę nad archiwum danych.
Jak skonfigurować service account dla Google Search Console API
Aby uzyskać dostęp do Search Analytics API bez konieczności każdorazowego logowania się przez przeglądarkę z użyciem protokołu OAuth2 dla użytkowników końcowych, należy skonfigurować konto usługi (Service Account) w Google Cloud Platform. Konto usługi działa jako autonomiczny podmiot techniczny posiadający własną parę kluczy kryptograficznych.
Krok pierwszy to utworzenie projektu w konsoli Google Cloud (console.cloud.google.com). Po utworzeniu projektu przechodzimy do sekcji APIs & Services, wyszukujemy Google Search Console API i klikamy przycisk włączający usługę.
Krok drugi to wygenerowanie konta usługi w sekcji IAM & Admin, w podsekcji Service Accounts. Nadajemy kontu unikalną nazwę, na przykład gsc-analytics-collector. Po utworzeniu konta przechodzimy do zakładki Keys i tworzymy nowy klucz prywatny w formacie JSON. Plik ten zostanie pobrany na dysk lokalny. Zawiera on identyfikator klucza, adres e-mail konta usługi oraz klucz prywatny RSA w formacie PEM.
{
"type": "service_account",
"project_id": "twoj-projekt-gsc",
"private_key_id": "9b1c7a8d5e2f3a4b",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3...\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "109876543210987654321",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}Krok trzeci to autoryzacja konta usługi w panelu Google Search Console. Kopiujemy adres client_email i otwieramy Search Console dla weryfikowanej domeny. Przechodzimy do zakładki Ustawienia (Settings), wybieramy Użytkownicy i uprawnienia (Users and permissions), a następnie dodajemy nowy adres e-mail z rolą Ograniczone uprawnienia (Restricted) lub Pełne uprawnienia (Full). Od tej chwili konto usługi może odpytywać endpointy Search Console dla tej właściwości.
Plik JSON z kluczem prywatnym nigdy nie może trafić do publicznego repozytorium kodu. Należy dodać go do pliku .gitignore lub przekazywać jego zawartość jako zaszyfrowaną zmienną środowiskową na serwerze produkcyjnym.
Jak sprawdzić, do których właściwości Search Console ma dostęp konto usługi
Najczęstszy powód błędu 403 po skonfigurowaniu konta usługi to rozjazd między adresem właściwości w skrypcie a tym, który zna Search Console. Zamiast zgadywać, warto zapytać API. Metoda sites.list zwraca listę właściwości, do których dane konto ma dostęp, a każda pozycja zawiera dwa pola: siteUrl w dokładnie takim zapisie, jakiego oczekują pozostałe metody, oraz permissionLevel (dokumentacja zasobu sites).
Pole permissionLevel przyjmuje jedną z czterech wartości: siteOwner (właściciel), siteFullUser (pełne uprawnienia), siteRestrictedUser (ograniczone uprawnienia) i siteUnverifiedUser (użytkownik bez zweryfikowanej właściwości). Jeśli właściwości nie ma na liście, adres e-mail konta usługi nie został dodany w ustawieniach Search Console albo trafił do innej właściwości, na przykład do prefiksu https://www. zamiast do właściwości domenowej.
const { data } = await searchconsole.sites.list();
for (const site of data.siteEntry || []) {
console.log(site.siteUrl, site.permissionLevel);
}
// sc-domain:wppoland.com siteFullUserTen sam zasób ma metody add, delete i get. Do samego pobierania danych wystarczy zakres webmasters.readonly i list uruchamiane raz przy starcie skryptu: jeśli zwrócony siteUrl różni się od skonfigurowanego, skrypt może zakończyć pracę z czytelnym komunikatem, zanim wykona setki zapytań z błędem.
Jak pobrać dane z sc-domain i prefiksu URL w Node.js
Podczas pracy z Search Console API programiści często napotykają błąd 403 lub 404 wynikający z błędnego formatu identyfikatora witryny (siteUrl). Google obsługuje dwa odmienne typy właściwości:
- Właściwość domenowa (Domain Property): agreguje wszystkie protokoły i subdomeny. W API jej identyfikator musi mieć prefiks
sc-domain:, na przykładsc-domain:wppoland.com. - Właściwość z prefiksem URL (URL-prefix Property): dotyczy konkretnego protokołu i ścieżki. W API jej identyfikator to pełny adres URL z ukośnikiem na końcu, na przykład
https://wppoland.com/pl/.
W wielojęzycznych serwisach najlepszą praktyką jest odpytywanie właściwości prefiksowych dla poszczególnych wersji językowych lub właściwości domenowej i późniejsze filtrowanie ścieżek URL w kodzie.
Poniższy skrypt napisany w Node.js prezentuje kompletną implementację pobierania danych analitycznych z obsługą paginacji i zapisu do pliku JSON. Wykorzystuje oficjalną bibliotekę googleapis.
import fs from "node:fs";
import path from "node:path";
import { google } from "googleapis";
async function exportSearchConsoleData() {
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";
// Obliczenie zakresu dat z pominieciem ostatnich 3 dni ze wzgledu na opoznienia danych
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(`Pobieranie danych dla ${siteUrl} od ${startDate} do ${endDate}`);
const allRows = [];
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 || [];
allRows.push(...rows);
console.log(`Pobrano partie: ${rows.length} wierszy (lacznie: ${allRows.length})`);
if (rows.length < rowLimit) {
break;
}
startRow += rowLimit;
}
const outputPath = "./reports/gsc-performance-data.json";
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, JSON.stringify(allRows, null, 2), "utf8");
console.log(`Eksport zakonczony. Zapisano ${allRows.length} rekordow do ${outputPath}`);
}
exportSearchConsoleData().catch((err) => {
console.error("Blad pobierania danych z Search Console API:", err.message);
process.exit(1);
});Kod ten iteruje po kolejnych stronach wyników za pomocą parametru startRow. Gdy odpowiedź API zawiera mniej wierszy niż zadeklarowany limit 25 000, pętla kończy działanie. Pozwala to pobrać pełną historię zapytań bez utraty danych z długiego ogona.
Jak pobrać dane z Google Search Console w języku Python
Dla inżynierów danych i zespołów korzystających ze środowisk Pandas oraz Jupyter, skrypt w języku Python stanowi najwygodniejsze narzędzie do ekstrakcji i czyszczenia danych z Search Console. Poniższy przykład demonstruje pobranie danych z podziałem na dni i zapis do zoptymalizowanego formatu Parquet, który zajmuje mniej miejsca niż CSV i zachowuje typy kolumn.
Do uruchomienia skryptu wymagane są biblioteki google-api-python-client, google-auth oraz pandas i pyarrow:
pip install google-api-python-client google-auth pandas pyarrowImplementacja skryptu:
import json
import datetime
import pandas as pd
from google.oauth2 import service_account
from googleapiclient.discovery import build
def fetch_search_console_records(site_url: str, days_back: 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_back + 3)).strftime("%Y-%m-%d")
collected_rows = []
start_row = 0
row_limit = 25000
while True:
request_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=request_body
).execute()
rows = response.get("rows", [])
if not rows:
break
for r in rows:
collected_rows.append({
"date": r["keys"][0],
"page": r["keys"][1],
"query": r["keys"][2],
"country": r["keys"][3],
"device": r["keys"][4],
"clicks": r["clicks"],
"impressions": r["impressions"],
"ctr": r["ctr"],
"position": round(r["position"], 2)
})
print(f"Pobrano {len(rows)} wierszy (startRow={start_row})")
if len(rows) < row_limit:
break
start_row += row_limit
df = pd.DataFrame(collected_rows)
return df
if __name__ == "__main__":
property_id = "sc-domain:wppoland.com"
df_results = fetch_search_console_records(property_id, days_back=60)
if not df_results.empty:
df_results["date"] = pd.to_datetime(df_results["date"])
df_results.to_parquet("reports/gsc_performance.parquet", index=False)
print(f"Zapisano {len(df_results)} rekordow w formacie Parquet.")
else:
print("Brak danych do zapisu.")Zapis do formatu kolumnowego Parquet pozwala na szybkie agregacje i grupowanie zapytań według kategorii produktów czy szablonów stron w WordPressie bez obciążania pamięci operacyjnej serwera.
Jak pobrać świeże i godzinowe dane z Search Console API
Skrypty powyżej pomijają ostatnie trzy dni, bo Google podaje, że dane są zwykle dostępne po 2 do 3 dniach (poradnik pobierania wszystkich danych). Ten sam poradnik zaleca prosty test: zapytanie z wymiarem date za ostatnie 10 dni pokazuje, który dzień jest ostatnim dostępnym.
O tym, czy API zwróci dane wstępne, decyduje parametr dataState (dokumentacja metody query):
Wartość dataState | Co zwraca API |
|---|---|
final (lub brak parametru) | Tylko dane sfinalizowane |
all | Także świeże dane, które mogą się jeszcze zmienić |
hourly_all | Dane z podziałem godzinowym, częściowe, do użycia z wymiarem hour |
Przy dataState: "all" i grupowaniu po dacie odpowiedź zawiera w metadanych pole first_incomplete_date. Wszystkie wartości po tej dacie mogą się jeszcze wyraźnie zmienić, więc skrypt archiwizujący powinien zapisywać je osobno albo nadpisywać przy kolejnym uruchomieniu. Czasy w metadanych są podawane w strefie America/Los_Angeles, co ma znaczenie przy łączeniu danych z polskimi logami serwera.
Wymiar hour Google dodał 9 kwietnia 2025 roku. Panel pokazuje dane godzinowe tylko dla ostatnich 24 godzin, a API zwraca je dla maksymalnie 10 dni (ogłoszenie na Google Search Central Blog). Pozwala to porównać ostatni dzień z tym samym dniem tygodnia sprzed tygodnia, na przykład po publikacji wpisu albo po wdrożeniu nowego szablonu.
{
"startDate": "2026-10-15",
"endDate": "2026-10-15",
"dataState": "hourly_all",
"dimensions": ["hour"]
}Klucze w odpowiedzi mają postać 2026-10-15T00:00:00-07:00, czyli godzinę w strefie Los Angeles z przesunięciem. Przed zapisem do bazy warto przeliczyć je na UTC.
Jak filtrować strony i zapytania w Search Analytics API
Zapytanie bez filtrów zwraca cały ruch właściwości. W serwisie wielojęzycznym zwykle potrzebny jest wycinek, na przykład tylko adresy z /pl/. Służy do tego tablica dimensionFilterGroups. Filtrować można po wymiarach country, device, page, query i searchAppearance, a dostępne operatory to (dokumentacja metody query):
equals(domyślny) inotEquals: dokładne dopasowanie, dlapageiqueryz rozróżnianiem wielkości liter,containsinotContains: dopasowanie fragmentu bez rozróżniania wielkości liter,includingRegexiexcludingRegex: wyrażenie regularne w składni RE2.
{
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"dimensions": ["page", "query"],
"type": "web",
"dimensionFilterGroups": [{
"filters": [
{ "dimension": "page", "operator": "includingRegex", "expression": "^https://wppoland\\.com/pl/" },
{ "dimension": "query", "operator": "notContains", "expression": "wppoland" }
]
}],
"rowLimit": 25000
}Drugi filtr odcina zapytania brandowe, co jest typowym krokiem przed analizą długiego ogona. Parametr type zastąpił przestarzały searchType i przyjmuje wartości web (domyślna, karta Wszystko bez Discover i Google News), image, video, news (karta Wiadomości), discover oraz googleNews. Każdy typ trzeba pobrać osobnym zapytaniem.
Parametr aggregationType decyduje, czy metryki są liczone według właściwości (byProperty) czy według adresu URL (byPage); domyślne auto zostawia wybór Google. Wartość byProperty nie działa dla typów discover i googleNews.
Dlaczego suma z API nie zgadza się z wykresem w Search Console
Po pierwszym eksporcie prawie każdy zespół zauważa, że suma kliknięć z wierszy jest niższa niż liczba na wykresie w panelu. To nie jest błąd skryptu. Google opisuje trzy przyczyny (poradnik pobierania wszystkich danych):
- API udostępnia najwyżej 50 000 wierszy dziennie na typ wyszukiwania, posortowanych według kliknięć. Przy dużych serwisach ogon zapytań poniżej tego progu nie trafia do odpowiedzi.
- Przy grupowaniu po wymiarach
pagelubquerysystem może pominąć część danych, żeby obliczenia pozostały wykonalne. - Wyświetlenia, kliknięcia, pozycja i CTR są liczone inaczej przy agregacji według strony niż według właściwości.
Do tego dochodzą zapytania anonimizowane, których nie ma w żadnym źródle. Praktyczny wniosek: sumy ruchu bierz z zapytania bez wymiarów page i query (tylko date), a rozbicie na frazy traktuj jako próbkę uporządkowaną według kliknięć. Google zaleca też pobieranie danych dzień po dniu i stronicowanie co 25 000 wierszy aż do pustej strony, osobno dla każdego typu wyszukiwania.
Jak zapisywać dane z Google Search Console w Google BigQuery
Najbardziej odpornym na awarie rozwiązaniem dla dużych serwisów jest automatyczny eksport danych z Google Search Console do hurtowni Google BigQuery. Google uruchomił Bulk Data Export 21 lutego 2023 roku: mechanizm codziennie przesyła zrzut danych o skuteczności do wskazanego projektu BigQuery (ogłoszenie na Google Search Central Blog).
Zalety tego podejścia:
- Brak ograniczenia 16 miesięcy: zapisane w BigQuery dane zostają tak długo, jak ich potrzebujesz.
- Eksport nie podlega dziennemu limitowi wierszy API. Google odfiltrowuje tylko zapytania anonimizowane, ze względu na prywatność.
- Możliwość łączenia danych z Search Console z tabelami bazy danych WordPressa (zamówienia WooCommerce, koszyki, konwersje).
Eksport konfiguruje się w dwóch krokach. W Google Cloud Console włączasz BigQuery API w projekcie i nadajesz uprawnienia kontu usługi Search Console. W ustawieniach Search Console podajesz identyfikator projektu i lokalizację zbioru danych. Eksport może ustawić tylko właściciel właściwości. Search Console najpierw symuluje eksport; jeśli symulacja się uda, wszyscy właściciele dostają e-mail, a regularne eksporty startują w ciągu 48 godzin. Przy błędzie alert pojawia się od razu.
Eksport tworzy trzy tabele:
searchdata_site_impression: dane zagregowane według właściwości, z zapytaniem, krajem, typem wyszukiwania i urządzeniem.searchdata_url_impression: dane zagregowane według adresu URL, ze szczegółami zapytań i wyników z elementami rozszerzonymi.ExportLog: zapis tego, jakie dane trafiły do bazy danego dnia. Nieudane eksporty nie są tu odnotowywane.
Przykładowe zapytanie SQL w BigQuery identyfikujące podstrony o wysokiej liczbie wyświetleń i niskim współczynniku CTR (kandydaci do optymalizacji meta tagów i nagłówków):
SELECT
url,
query,
SUM(impressions) AS total_impressions,
SUM(clicks) AS total_clicks,
ROUND(SAFE_DIVIDE(SUM(clicks), SUM(impressions)) * 100, 2) AS avg_ctr,
ROUND(SUM(sum_position) / SUM(impressions) + 1, 1) AS avg_position
FROM
`twoj-projekt.searchconsole.searchdata_url_impression`
WHERE
data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND country = 'pol'
GROUP BY
url,
query
HAVING
total_impressions > 500
AND avg_ctr < 2.0
AND avg_position BETWEEN 3.0 AND 10.0
ORDER BY
total_impressions DESC
LIMIT 100;W tabeli searchdata_url_impression są między innymi kolumny data_date, url, query, country, search_type, impressions, clicks i sum_position, a także flagi typów wyników rozszerzonych (przykład Google filtruje search_type = 'WEB'). Kolumna sum_position liczy pozycję od zera, dlatego średnią pozycję oblicza się jako SUM(sum_position) / SUM(impressions) + 1, tak jak w przykładzie Google. Zapytanie typuje frazy z pierwszej strony wyników, które dają poniżej 2 procent kliknięć. To kandydaci do testu nowego tytułu, nagłówka H1 i opisu; efekt zmiany trzeba zmierzyć w kolejnych tygodniach, a nie zakładać z góry.
Potok analityczny Search Console do BigQuery
[Google Search Console] ──(Bulk Export)──> [Google BigQuery]
│
├──> [Looker Studio Dashboard]
├──> [Audyt spadkow pozycji]
└──> [Korelacja z zamowieniami WooCommerce]Kiedy wybrać Search Analytics API, a kiedy Bulk Data Export
Oba źródła dają te same metryki, ale rozwiązują inne problemy. Google pisze w ogłoszeniu eksportu, że jest on szczególnie przydatny dla dużych serwisów z dziesiątkami tysięcy stron albo z ruchem z dziesiątek tysięcy zapytań dziennie, a małe i średnie serwisy mają dostęp do wszystkich swoich danych przez interfejs, konektor Looker Studio lub Search Analytics API (Google Search Central Blog).
| Kryterium | Search Analytics API | Bulk Data Export |
|---|---|---|
| Limit wierszy | 25 000 na żądanie, 50 000 dziennie na typ wyszukiwania | Bez dziennego limitu wierszy |
| Zapytania anonimizowane | Pominięte | Pominięte |
| Świeżość | Dane wstępne (all) i godzinowe (hourly_all) | Codzienny zrzut |
| Kto może skonfigurować | Każdy użytkownik z dostępem do właściwości | Tylko właściciel właściwości |
| Gdzie trafiają dane | Tam, gdzie zapisze je Twój skrypt | Do Twojego projektu BigQuery |
W praktyce oba podejścia się uzupełniają. Eksport do BigQuery zostaje archiwum i źródłem analiz długiego ogona, a lekki skrypt na API z dataState: "hourly_all" pilnuje ostatnich godzin po publikacji lub wdrożeniu. Dla małego bloga na WordPressie sam skrypt z poprzednich sekcji, uruchamiany raz dziennie przez cron, zwykle wystarcza.
Jak połączyć dane z Search Console z bazą WordPressa
Surowe dane z Search Console zyskują największą wartość biznesową, gdy zostaną powiązane z wewnętrzną strukturą treści w systemie CMS. Tradycyjny eksport z Search Console zawiera jedynie pełne adresy URL w postaci ciągów tekstowych. Nie informuje o typie wpisu (post, produkt WooCommerce, strona lądowania), dacie ostatniej modyfikacji w edytorze Gutenberg ani o autorze tekstu.
Integracja danych polega na przetłumaczeniu ścieżki URL na identyfikator obiektu w WordPressie (post_id). W środowisku WordPressa służy do tego funkcja url_to_postid($url) lub bezpośrednie dopasowanie sluga w tabeli wp_posts.
Poniższy fragment kodu w PHP ilustruje, jak zaimportowane z API dane o zapytaniach przypisać do właściwego produktu lub artykułu w bazie danych:
<?php
/**
* Integracja danych analitycznych Search Console z wp_posts
*/
declare(strict_types=1);
namespace WPPoland\Analytics;
class SearchConsoleIngestor {
public function correlateUrlWithPost(string $fullUrl, int $clicks, int $impressions, float $position): void {
$parsedPath = wp_parse_url($fullUrl, PHP_URL_PATH);
if (!$parsedPath) {
return;
}
// Pobranie post_id na podstawie adresu URL
$postId = url_to_postid($fullUrl);
if ($postId === 0) {
// Obsluga stron glownych taksonomii lub archiwow produktow
$postId = $this->resolveTermOrArchive($parsedPath);
}
if ($postId > 0) {
// Zapisanie zagregowanych metryk w tabeli wp_postmeta lub wlasnej tabeli analitycznej
update_post_meta($postId, '_gsc_last_30d_clicks', $clicks);
update_post_meta($postId, '_gsc_last_30d_impressions', $impressions);
update_post_meta($postId, '_gsc_avg_position', $position);
update_post_meta($postId, '_gsc_updated_at', current_time('mysql', true));
}
}
private function resolveTermOrArchive(string $path): int {
global $wpdb;
$slug = trim($path, '/');
$slugParts = explode('/', $slug);
$leafSlug = end($slugParts);
$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",
$leafSlug
));
return $termId ? (int)$termId : 0;
}
}Dzięki takiemu powiązaniu zespół redakcyjny w panelu WordPressa widzi dokładną liczbę wyświetleń i pozycję każdej podstrony. Jeśli po aktualizacji szablonu lub wdrożeniu nowego kodu średnia pozycja artykułu spada z miejsca 4 na 18, system może automatycznie wygenerować powiadomienie na kanale Slack lub utworzyć zadanie audytowe dla programisty.
W przypadku architektur opartych na headless WordPress lub statycznych generatorach stron, dane z API Search Console mogą być pobierane na etapie budowania serwisu (build time) i wykorzystywane do generowania dynamicznych sekcji najpopularniejszych tematów oraz powiązanych artykułów.
Jak sprawdzić stan indeksowania adresów z eksportu przez URL Inspection API
Spadek kliknięć wpisu w danych z Search Analytics nie mówi, czy strona nadal jest w indeksie. Odpowiedź daje URL Inspection API, które korzysta z tego samego konta usługi i tych samych zakresów (webmasters lub webmasters.readonly). Żądanie to POST https://searchconsole.googleapis.com/v1/urlInspection/index:inspect z polami inspectionUrl (pełny adres, który musi należeć do właściwości), siteUrl (w tym samym formacie co wcześniej: sc-domain: albo prefiks ze slashem) i opcjonalnym languageCode (dokumentacja metody inspect).
Wynik indexStatusResult zawiera między innymi (opis UrlInspectionResult):
| Pole | Co oznacza |
|---|---|
verdict | Ogólny wynik: czy adres jest zaindeksowany |
coverageState | Czy Google znalazł i zaindeksował stronę |
indexingState | Czy indeksowanie blokuje reguła noindex |
robotsTxtState | Czy robots.txt blokuje crawlowanie |
lastCrawlTime | Kiedy główny crawler ostatnio pobrał adres |
googleCanonical i userCanonical | Kanoniczny wybrany przez Google i zadeklarowany na stronie |
W WordPressie dobrze łączy się to z klasą SearchConsoleIngestor z poprzedniej sekcji: gdy pozycja wpisu spada, skrypt sprawdza jego adres i zapisuje coverageState oraz googleCanonical w meta wpisu. Rozjazd między kanonicznym Google a tym z wtyczki SEO zwykle tłumaczy spadek szybciej niż analiza fraz. Limit jest jednak niski: 2000 zapytań dziennie i 600 na minutę na witrynę (limity Search Console API), więc sprawdzaj tylko adresy z sygnałem spadku, nie całą mapę witryny. Metoda pokazuje wersję z indeksu Google i nie testuje adresu na żywo. Różnicę między tym narzędziem a Indexing API opisuje nasz przewodnik po GSC i Indexing API.
Jak radzić sobie z limitami zapytań i błędami API
Search Analytics API ma limity opisane w dokumentacji Google. Przekroczenie kończy się błędem kwoty, a skrypt musi poczekać:
| Zakres limitu | Wartość |
|---|---|
| Na witrynę (zapytania o tę samą właściwość) | 1 200 zapytań na minutę |
| Na użytkownika (zapytania z tego samego konta) | 1 200 zapytań na minutę |
| Na projekt Google Cloud | 40 000 zapytań na minutę i 30 000 000 dziennie |
Osobno działa limit obciążenia (load quota), liczony w oknach 10-minutowych i dobowych. Według Google zapytania są kosztowne, gdy grupujesz lub filtrujesz po stronie albo zapytaniu, a obciążenie rośnie wraz z długością zakresu dat. Google wprost odradza wielokrotne pobieranie tych samych danych, na przykład całego poprzedniego miesiąca przy każdym uruchomieniu. Skrypt archiwizujący powinien więc dociągać tylko brakujące dni.
Aby zapewnić niezawodność skryptów pobierających miliony rekordów dla rozbudowanych serwisów e-commerce, należy zaimplementować trzy mechanizmy obronne:
- Wykładnicze opóźnienie ponawiania (Exponential Backoff): w przypadku otrzymania kodu błędu 429 lub 503, skrypt nie przerywa pracy, lecz czeka rosnącą liczbę sekund (2 do potęgi n plus losowy jitter) przed ponowieniem żądania.
- Dzielenie zapytań według pojedynczych dni (Daily Date Chunking): zamiast pobierać dane dla całego roku w jednym wywołaniu z ogromną paginacją, dzielimy zadanie na pętle jednodniowe. Jeśli jedno z zapytań ulegnie awarii z powodu chwilowego błędu sieci, ponawiamy wyłącznie brakujący dzień.
- Buforowanie tokenów autoryzacyjnych: biblioteki klienckie Google automatycznie odświeżają token OAuth2 na podstawie klucza konta usługi, jednak w środowiskach bezserwerowych (AWS Lambda, Cloudflare Workers) należy upewnić się, że token nie jest generowany na nowo dla każdego pojedynczego żądania HTTP.
Poniższy fragment kodu ilustruje mechanizm odpornego odpytywania z obsługą ponowień:
async function resilientApiQuery(queryFn, maxRetries = 5) {
let attempt = 0;
while (attempt < maxRetries) {
try {
return await queryFn();
} catch (error) {
attempt++;
const isRateLimit = error.code === 429 || error.status === 429;
const isServerError = error.code >= 500 && error.code < 600;
if ((isRateLimit || isServerError) && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500;
console.warn(`Blad ${error.code}. Ponawianie za ${Math.round(delay)} ms (proba ${attempt}/${maxRetries})...`);
await new Promise((resolve) => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
}Te trzy mechanizmy sprawiają, że archiwizacja danych SEO działa niezależnie od błędów w przeglądarkowym panelu Google Search Console.
Jak zarządzać danymi SEO z Search Console API
Błędy interfejsu graficznego, takie jak awaria selektora dat i cofanie kalendarza do roku 1969, przypominają, że dane analityczne witryny powinny leżeć w systemie właściciela serwisu, a nie tylko w zewnętrznym panelu.
Wdrożenie niezależnego pipeline opartego na Search Console API przynosi wymierne korzyści:
- Do 50 000 wierszy dziennie na typ wyszukiwania zamiast 1000 wierszy tabeli w panelu, a z Bulk Data Export pełny zbiór bez zapytań anonimizowanych.
- Raportowanie niezależne od błędów frontendu Google.
- Ochrona przed utratą danych historycznych po upływie 16 miesięcy dzięki archiwizacji w BigQuery lub bazie relacyjnej.
- Możliwość bezpośredniej korelacji ruchu z przychodami sklepu WooCommerce oraz zmianami w kodzie szablonu WordPressa.
W celu wdrożenia zaawansowanej analityki, automatyzacji raportowania lub audytu technicznego serwisu warto skonsultować się z programistą WordPress, który połączy potok danych z architekturą Twojego sklepu i zabezpieczy go przed awariami. W sprawach doradztwa technicznego zapraszamy do kontaktu z naszym zespołem.







