--- title: "Błędy" source: https://docs.senuto.com/types/errors --- --- title: Błędy sidebarTitle: Błędy ------------------- # Błędy Gdy żądanie się nie powiedzie, API zwraca kopertę z `success: false` i obiektem `error`. Struktura jest **spójna w całym API**. ## Koperta błędu ```ts type ErrorResponse = { /** Zawsze `false` dla odpowiedzi błędnej */ success: false; data: { error: { /** Kategoria błędu — np. invalid_data, invalid_filtering, unreachable_data, timeout, database, unknown */ type: string; /** Czytelny komunikat */ message: string; /** * Tylko dla `invalid_data`: mapa pole → reguła → komunikat. * Np. `{ "fetch_mode": { "_required": "This field is required" } }` */ params?: Record>; }; }; } export default ErrorResponse ``` ## Status HTTP `418` — najważniejsze > **Błąd:** > Status **`418`** to w tym API **generyczny błąd walidacji żądania**, a **nie** wyłącznie przekroczenie limitu zapytań. Zdecydowana większość `418` to `type: "invalid_data"` — brakujące lub nieprawidłowe pole. Sprawdzaj `data.error.type` i `data.error.params`, zanim uznasz błąd za rate limit. Typowe `418` (`invalid_data`) — brak wymaganego pola: ```json filename="brak fetch_mode" { "success": false, "data": { "error": { "type": "invalid_data", "message": "Given data is invalid, please check the documentation or contact Administrator", "params": { "fetch_mode": { "_required": "This field is required" } } } } } ``` ## Status HTTP `404` — trzy różne przyczyny `404` w tym API **nie** oznacza po prostu „zły adres". Rozróżnisz przypadki po tym, co przyszło w treści: | Treść odpowiedzi | Znaczenie | Co zrobić | | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `{"success": false, "message": ""}` | Trasa nie istnieje **albo** żądanie jest nieuwierzytelnione (wtedy poprzedza je `302` na `…/users/login`) | Sprawdź pisownię ścieżki (niżej), a potem token | | Strona **HTML** z nagłówkiem „Error / Not Found" | Trasa istnieje, ale wskazany zasób nie — albo akcja w ogóle nie dostała identyfikatora | Sprawdź, czy parametr idzie tam, gdzie trzeba (query string vs ciało) i czy zasób należy do Twojego konta | > **Błąd:** > **Nieważny lub brakujący token daje `302`, a po przekierowaniu `404` — nie `401`.** Żądanie bez > nagłówka `Authorization` oraz z tokenem po terminie ważności dostaje `302` z nagłówkiem `Location` > na `/api//users/login`, a pod tym adresem czeka `404 {"success": false, "message": ""}` — > dokładnie to samo, co po literówce w ścieżce. Klient podążający za przekierowaniami (`curl -L`, > `requests`, `axios`) pokaże `404`; Postman z wyłączonym podążaniem zatrzyma się na `302`. > Zanim zaczniesz debugować ścieżkę, pobierz świeży token (patrz [Autoryzacja](/authorization)). > Komunikat jest pusty celowo, produkcyjne API nie ujawnia szczegółów wyjątku. ### Konwencja adresów — najczęstsza przyczyna `404` Ścieżka ma postać `/api////`, przy czym **człony i akcja rządzą się różnymi regułami**: - **Moduł, sekcja i kontroler:** `snake_case` — `visibility_analysis`, `ai_overviews`, `serp_analysis`. - **Akcja:** `camelCase` — `getDomainStatistics`, `getKeywordHistory`. **Zamiana akcji na `snake_case` albo `kebab-case` daje `404`**, mimo że endpoint istnieje. - **Myślnik zamiast podkreślenia w module** (`visibility-analysis`) też kończy się `404`. - **Pominięty człon sekcji** (`/api/visibility_analysis/dashboard/…` zamiast `…/reports/dashboard/…`) — `404`. ```text filename="ta sama akcja, cztery adresy" /api/visibility_analysis/reports/dashboard/getDomainStatistics → 200 /api/visibility_analysis/reports/dashboard/get_domain_statistics → 404 akcja w snake_case /api/visibility-analysis/reports/dashboard/getDomainStatistics → 404 myślnik w module /api/visibility_analysis/dashboard/getDomainStatistics → 404 brak członu "reports" ``` > **Informacja:** > Sama **wielkość liter w nazwie akcji** akurat nie ma znaczenia (`getdomainstatistics` też odpowie `200`) — > decyduje brak separatorów. Nie opieraj się jednak na tym: trzymaj się pisowni podanej na stronie endpointu. ## Kategorie błędów (`type`) | `type` | Znaczenie | Częsta przyczyna | | -------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------- | | `invalid_data` | Żądanie nie przeszło walidacji | Brak wymaganego pola, zła wartość (np. nieznany `fetch_mode`, `country_id`, `aggregation_type`) | | `invalid_filtering` | Nieprawidłowy parametr `filtering` | Klucz filtra nieobsługiwany przez ten endpoint | | `unauthorized` / `Unauthorized access` | Brak dostępu do zasobu | Cudzy lub nieistniejący `project_id` / `group_id` / `keyword_id` | | `unreachable_data` | Dane chwilowo niedostępne | Problem po stronie źródła danych | | `timeout` | Przekroczono czas przetwarzania | Bardzo szeroki zakres / duża domena | | `database` | Błąd warstwy danych | Bywa zwracany także jako `HTTP 500` z surową stroną HTML (patrz niżej) | ## Częste pułapki > **Ostrzeżenie:** > **Cudzy/nieistniejący zasób → `418 Unauthorized access`, nie `404`.** Podanie `project_id` lub `group_id` spoza konta zwraca błąd walidacji dostępu, a nie „nie znaleziono”. Konto administratora (`role_id = 1`) omija część kontroli dostępu. > **Ostrzeżenie:** > **Zła metoda HTTP.** Część akcji akceptuje wyłącznie `GET` (parametry w query stringu) — wysłanie ich metodą `POST` w ciele JSON zwraca `405` (Method Not Allowed) albo `418`, bo walidator widzi puste pola. Metoda jest podana na stronie każdego endpointu. > **Ostrzeżenie:** > **Konto bez dostępu do API → `403`, nie `418`.** Ważny token na koncie bez aktywnego planu albo bez > dodatku Dostęp do API daje `403 {"success": false, "message": ""}`. Treść jest pusta, więc ten przypadek > rozpoznajesz po samym statusie (patrz [Dostęp do API](/access)). > **Ostrzeżenie:** > **`HTTP 500` z surową stroną HTML.** Niektóre endpointy zwracają obecnie nieobsłużony wyjątek (`500`, treść to strona HTML, nie JSON) niezależnie od poprawności żądania — to znane usterki backendu, nie błąd Twojej integracji. Buduj klienta odpornie: gdy odpowiedź nie jest JSON-em, potraktuj ją jako błąd serwera. > **Uwaga:** > **Odwrócony komunikat zakresu dat.** W części raportów przy `date_min > date_max` komunikat błędu bywa odwrócony (mówi o `date_max`, choć problem dotyczy `date_min`). Waliduj kolejność dat po swojej stronie. ## Odpowiedź `200`, ale dane są puste lub zerowe To **nie jest błąd** — żądanie przeszło walidację, tylko dla podanych kryteriów nie ma danych. Kolejność sprawdzania, od najczęstszej przyczyny: 1. **Zły `fetch_mode`.** Host z subdomeną (`blog.example.com`) w trybie `topLevelDomain` liczy wyłącznie frazy, na których rankuje ten konkretny host — a tych zwykle nie ma. Zwalidowane: `blog.senuto.com` w `topLevelDomain` zwraca `0`, a w `subdomain` — 316 fraz w TOP10. Patrz [`fetch_mode`](/types/fetch-mode). 2. **Domena spoza indeksu.** Senuto liczy widoczność na własnym zbiorze fraz per rynek. Domena, która nie rankuje w TOP50 na żadną frazę z bazy, zwróci zera — tak samo jak domena nieistniejąca (sprawdzone: dla wymyślonej domeny raport dashboardu odpowiada `200` z samymi zerami, nie błędem). 3. **Zły rynek (`country_id`).** Bez tego pola raporty liczą na bazie domyślnej (Polska 1.0). Domena czeska sprawdzana na polskiej bazie da zera. Część danych istnieje **wyłącznie w bazie PL 2.0 (`country_id: 200`)** — np. raport intencji AI Overviews na `country_id: 1` zwraca pustą tablicę dla każdej domeny. 4. **Zakres dat sprzed pojawienia się domeny w bazie** — raporty historyczne zwrócą pustą serię dla okresu, w którym nie było jeszcze pomiarów. 5. **`filtering` odcina wszystko.** Filtr niepasujący do żadnego wiersza daje pustą tablicę, a nie błąd — sprawdź go, usuwając filtry po kolei. > **Informacja:** > Rozróżnienie, które warto zaszyć w kliencie: `data: []` (albo same zera) = **brak danych dla kryteriów**, > `success: false` = **odrzucone żądanie**. Pierwsze nie jest powodem do retry — powtórzone da to samo. ## `418` w trakcie długiego pobierania albo w pętli Typowy zgłaszany scenariusz: integracja (n8n, Make, własny skrypt) pobiera dane od kilku minut i nagle zaczyna dostawać `418`, choć te same żądania wcześniej działały. Prawdziwe przyczyny, w kolejności: - **Wyczerpany limit dobowy.** Najczęstsza. Komunikat wygląda tak: `You have reached daily limit of queries. Daily limit value is N`. Limity są **per konto**, nie per token, więc pętla w integracji rywalizuje o ten sam licznik co praca w aplikacji. Stan sprawdzisz przez [`GET /api/users/getLimits`](/rate-limits) — ten odczyt nic nie zużywa. - **Zmienna w pętli, która czasem jest pusta.** Iterując po liście domen czy fraz łatwo trafić na wiersz z pustą wartością — walidator zwróci `418` z `params` wskazującym pole. Zaloguj `data.error.params`, a znajdziesz sprawcę w jednym kroku. - **Zasób spoza konta.** `project_id` albo `group_id` innego użytkownika daje `418 Unauthorized access` (a nie `404`), co w pętli po projektach wygląda jak losowa awaria. > **Ostrzeżenie:** > Nie myl tego z **wygaśnięciem tokenu** — token po terminie ważności daje `302` na `…/users/login`, > a po przekierowaniu `404` z pustym `message`, nie `418`. Jeśli integracja działała miesiąc i nagle > „wszystkie endpointy zniknęły", to token (patrz [Status HTTP `404`](#status-http-404--trzy-różne-przyczyny)). Klienci HTTP i platformy no-code często **opakowują** treść błędu we własny komunikat (np. n8n pokazuje „Your request is invalid"). Zawsze zaglądaj do surowego ciała odpowiedzi — `data.error.type` i `data.error.params` mówią, co naprawdę odrzucono. ## `500` / `503` — awaria czy mój błąd? | Objaw | Najpewniej | Co zrobić | | ------------------------------------------------------------------------ | ----------------------------------------------------- | ---------------------------------------- | | `500` na **jednym** endpoincie, inne działają | znana usterka tego endpointu (patrz wyżej) | zgłoś do supportu, obejdź innym raportem | | `500`/`503` na **wszystkich** endpointach, także `GET /api/users/whoami` | awaria po stronie API | ponów z backoffem, potem zgłoś | | `418`/`404`/`302` na wszystkich | to **nie** awaria — patrz sekcje wyżej (limit, token) | sprawdź token i limity | Najprostszy test rozstrzygający: wywołaj `GET /api/users/whoami`. To najlżejszy endpoint w API — jeśli on odpowiada `200`, backend żyje i problem jest w konkretnym żądaniu. Bieżące informacje o awariach i pracach serwisowych publikujemy na **[komunikat.senuto.com](https://komunikat.senuto.com)** (strona tymczasowa) — zajrzyj tam, zanim zaczniesz debugować własny kod. Przy masowych `5xx` ponawiaj z **wykładniczym backoffem** (np. 1 s, 2 s, 4 s, 8 s… do kilku minut), a nie w pętli co sekundę. Gdy po kilkunastu minutach nic się nie zmienia, zgłoś do supportu i podaj: pełny URL żądania, metodę, czas z dokładnością do minuty (ze strefą), ciało żądania bez tokenu oraz surową odpowiedź z nagłówkami. Z tym zestawem sprawa idzie od razu do zespołu API zamiast wracać z prośbą o szczegóły. ## Obsługa po stronie klienta — wzorzec ```jsonc filename="obsługa błędu (pseudokod)" // odpowiedź = wyślij żądanie // jeśli odpowiedź nie jest JSON-em → błąd serwera (np. 500 HTML) // w przeciwnym razie: // jeśli odpowiedź.success == false: // switch (odpowiedź.data.error.type): // "invalid_data" → popraw pola z error.params (mapa pole → reguła) // "invalid_filtering"→ sprawdź klucze filtra dla tego endpointu // "unauthorized" → sprawdź dostęp do project_id / zasobu // "timeout" → zawęź zakres i ponów // inne → potraktuj jak błąd tymczasowy / zgłoś ```