Błąd date pickera w Google Search Console: jak pobierać dane przez API

Błąd date pickera w Google Search Console: jak pobierać dane przez API

Ostatnio zweryfikowano: 16 października 2026
21 min czytania
Przewodnik
Techniczne SEO

#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).

CechaInterfejs webowy GSCSearch Analytics API
Limit wierszy na żądanie1 000 wierszy25 000 wierszy
Maksymalna liczba wierszy1 000 (sztywny limit tabeli)50 000 dziennie na typ wyszukiwania (stronicowanie przez startRow)
Okres przechowywania danych16 miesięcy16 miesięcy, dłużej po zapisaniu we własnej bazie
Podatność na błędy frontenduWysoka (błędy UI, blokady JS)Brak warstwy UI (odpowiedź JSON z endpointu REST)
Automatyzacja raportowaniaWymaga ręcznego klikaniaPełna automatyzacja (cron, workers, CI)
Rozbicie wielowymiaroweOgraniczone filtry wzajemnePeł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 siteFullUser

Ten 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:

  1. Właściwość domenowa (Domain Property): agreguje wszystkie protokoły i subdomeny. W API jej identyfikator musi mieć prefiks sc-domain:, na przykład sc-domain:wppoland.com.
  2. 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 pyarrow

Implementacja 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ść dataStateCo zwraca API
final (lub brak parametru)Tylko dane sfinalizowane
allTakże świeże dane, które mogą się jeszcze zmienić
hourly_allDane 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) i notEquals: dokładne dopasowanie, dla page i query z rozróżnianiem wielkości liter,
  • contains i notContains: dopasowanie fragmentu bez rozróżniania wielkości liter,
  • includingRegex i excludingRegex: 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):

  1. 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.
  2. Przy grupowaniu po wymiarach page lub query system może pominąć część danych, żeby obliczenia pozostały wykonalne.
  3. 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:

  1. Brak ograniczenia 16 miesięcy: zapisane w BigQuery dane zostają tak długo, jak ich potrzebujesz.
  2. Eksport nie podlega dziennemu limitowi wierszy API. Google odfiltrowuje tylko zapytania anonimizowane, ze względu na prywatność.
  3. 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).

KryteriumSearch Analytics APIBulk Data Export
Limit wierszy25 000 na żądanie, 50 000 dziennie na typ wyszukiwaniaBez dziennego limitu wierszy
Zapytania anonimizowanePominiętePominię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ściTylko właściciel właściwości
Gdzie trafiają daneTam, gdzie zapisze je Twój skryptDo 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):

PoleCo oznacza
verdictOgólny wynik: czy adres jest zaindeksowany
coverageStateCzy Google znalazł i zaindeksował stronę
indexingStateCzy indeksowanie blokuje reguła noindex
robotsTxtStateCzy robots.txt blokuje crawlowanie
lastCrawlTimeKiedy główny crawler ostatnio pobrał adres
googleCanonical i userCanonicalKanoniczny 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 limituWartość
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 Cloud40 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:

  1. 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.
  2. 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ń.
  3. 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.

Następny krok

Przekuj artykuł w realne wdrożenie

Pod tym wpisem dokładam linki, które domykają intencję użytkownika i prowadzą dalej w strukturze serwisu.

Chcesz wdrożyć ten temat na swojej stronie?

Jeśli zależy Ci na widoczności w Google i systemach AI, mogę przygotować architekturę treści, FAQ, schema i linkowanie pod GEO, AEO i SEO.

Powiązany klaster

Sprawdź inne usługi WordPress i bazę wiedzy

Wzmocnij swój biznes dzięki profesjonalnemu wsparciu technicznemu w kluczowych obszarach ekosystemu WordPress.

Dlaczego selektor dat w Google Search Console blokuje się na dacie 1969-12-31#
Google nie opublikował przyczyny. Sama data wskazuje na znacznik czasu równy zeru, czyli północ 1 stycznia 1970 roku UTC (Unix epoch), który w strefach czasowych na zachód od Greenwich wyświetla się jako 31 grudnia 1969 roku. Błąd opisał Search Engine Roundtable 13 października 2026 roku; selektor naprawiono, 14 października zepsuł się ponownie i tego samego dnia znów zaczął działać.
Ile wierszy danych pozwala pobrać Google Search Console API#
Pojedyncze zapytanie do metody searchanalytics.query zwraca do 25 000 wierszy (domyślnie 1000). Parametr startRow pozwala stronicować wyniki, ale Google udostępnia przez API najwyżej 50 000 wierszy dziennie na typ wyszukiwania, posortowanych według kliknięć. Pełniejszy zbiór, bez zapytań anonimizowanych, daje Bulk Data Export do BigQuery.
Czym różni się odpytywanie właściwości sc-domain od prefiksu adresu URL#
Właściwość sc-domain agreguje ruch ze wszystkich subdomen i protokołów (http, https, www, m). Odpytanie jej przez API wymaga podania identyfikatora w formacie sc-domain:twojadomena.pl. Właściwość prefiksowa wymaga dokładnego adresu bazowego, na przykład https://twojadomena.pl/.
Jak długo Google przechowuje dane w Search Console i dlaczego warto je archiwizować#
Google przechowuje dane o kliknięciach i wyświetleniach tylko przez 16 miesięcy. Po tym czasie najstarsze rekordy są bezpowrotnie usuwane. Eksport do bazy BigQuery lub PostgreSQL pozwala prowadzić analizy wieloletnie rok do roku (YoY).
Jak połączyć dane z Google Search Console API z WordPressem#
Skrypt pobierający rekordy z API mapuje pole page z Search Console na identyfikatory wpisów wp_posts za pomocą funkcji url_to_postid lub dopasowania tabeli rewrite rules. Pozwala to śledzić spadki pozycji konkretnych artykułów i kategorii bezpośrednio w panelu administracyjnym.

Potrzebujesz FAQ dopasowanego do branży i rynku? Przygotujemy wersję pod Twoje cele biznesowe.

Porozmawiajmy

Polecane artykuły