# Błędy, odmowy robots.txt i ponowienia

> Kody błędów WebReaderAPI: odmowy robots.txt i witryny, przekroczenia limitu czasu i błędy kredytów, które warto ponowić i dlaczego żaden nie jest rozliczany.

- URL: https://webreaderapi.com/pl/docs/errors/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: WebReaderAPI (https://webreaderapi.com)

Każdy błąd ma status HTTP, stabilny `code` i czytelny `message`. Żaden z nich nie zużywa kredytu. Niektóre warto ponowić; odmowy pochodzące z witryny są ostateczne i nigdy ich nie obchodzimy.

**Szybka odpowiedź — Co oznaczają kody błędów WebReaderAPI i które warto ponowić?**

Błędy **WebReaderAPI** występują w trzech grupach i żaden nigdy nie jest rozliczany. Problemy z zapytaniem (`invalid_url`, `invalid_api_key`, `insufficient_credits`) wymagają poprawki po Twojej stronie. Decyzje witryny (`blocked_by_robots`, `blocked_by_site_policy`, `target_refused`) są ostateczne. Przejściowe awarie (`fetch_failed`, `fetch_timeout`, `concurrency_limit`) można ponowić z backoffem. Kredyty zaczynają się od 0,07 USD/1K stron, płatne kryptowalutą bez KYC.

## Które kody błędów może zwrócić API?

Treść to zawsze `{"error": {"code", "message"}}`, plus `target_status`, gdy odmowa pochodziła z witryny.

| HTTP | `code` | Przyczyna | Ponowić? |
| --- | --- | --- | --- |
| 400 | `invalid_url` | Nie jest to http ani https, adres jest zbyt długi albo to prywatny, lokalny lub chmurowy adres metadanych | Nie: popraw URL |
| 400 | `invalid_parameter` | Parametr ma wartość poza swoją listą lub zakresem | Nie: popraw zapytanie |
| 401 | `invalid_api_key` | Klucz brakuje, jest błędny lub unieważniony | Nie: sprawdź klucz |
| 402 | `insufficient_credits` | Na Twoim saldzie nie ma już kredytów | Po zakupie pakietu |
| 403 | `blocked_by_robots` | robots.txt strony nie zezwala na tę stronę dla WebReaderAPI ani dla wszystkich robotów | Nie: ostatecznie |
| 403 | `blocked_by_site_policy` | Strona deklaruje `Content-Signal: ai-input=no` | Nie: ostatecznie |
| 422 | `unsupported_content` | Typ, którego nie odczytujemy (wideo, archiwum), albo plik zbyt duży | Nie |
| 429 | `concurrency_limit` | Zbyt wiele Twoich zapytań działa jednocześnie | Tak, z backoffem |
| 502 | `target_refused` | Strona odmówiła: 403, 429, strona wyzwania lub ściana logowania; jej status jest w `target_status` | Nie: ostatecznie |
| 502 | `fetch_failed` | Błąd DNS, TLS lub połączenia albo strona odpowiedziała 404 lub 410 | Raz lub dwa razy, z backoffem |
| 504 | `fetch_timeout` | Strona nie odpowiedziała w ciągu `timeout` | Tak, z backoffem lub dłuższym timeoutem |

## Dlaczego robots.txt i odmowy strony są ostateczne?

Bo to decyzja właściciela strony, a WebReaderAPI jest zbudowany tak, by ją respektować. Respektujemy robots.txt: nasz crawler identyfikuje się jako WebReaderAPI, przestrzega reguł napisanych dla niego lub dla wszystkich robotów i nie czyta strony, której witryna zabrania. Odrzucone strony nie są obciążane opłatą. Jeśli witryna zadeklaruje Content-Signal: ai-input=no w swoim robots.txt, nie czytamy jej stron dla Ciebie.

Gdy strona odrzuca zapytanie w inny sposób — przez 403, 429, stronę wyzwania lub formularz logowania — otrzymujesz `target_refused` i własny status strony w `target_status`. Jeśli witryna odrzuci nasze zapytanie, zwracamy jej odpowiedź: nigdy nie rozwiązujemy CAPTCHA, nie logujemy się do kont ani nie próbujemy obejść blokady lub paywalla. Ponowna próba tego samego URL-a minutę później zwykle da tę samą odpowiedź, więc traktuj te kody jako wynik, a nie błąd: zapisz je w logach i idź dalej.

## Jak sprawdzić, dlaczego strona została odrzucona?

Otwórz `/robots.txt` strony i poszukaj grupy, która wymienia `WebReaderAPI`, albo grupy `*`, która dotyczy wszystkich robotów. Linia `Disallow` pasująca do ścieżki wyjaśnia `blocked_by_robots`. Linia taka jak `Content-Signal: ai-input=no` wyjaśnia `blocked_by_site_policy`.

Trzymamy kopię każdego robots.txt przez maksymalnie 24 godziny. Jeśli strona właśnie otworzyła sekcję dla robotów, zmiana może zacząć obowiązywać dopiero po upływie dnia. Właściciele stron mogą przeczytać, jak zachowuje się nasz crawler i jak go dozwolić lub zablokować, na [naszej stronie o crawlerze](https://webreaderapi.com/pl/bot/).

## Jak ponawiać przejściowe błędy?

Ponawiaj tylko `concurrency_limit`, `fetch_timeout` i raz lub dwa razy `fetch_failed`. Przed każdą nową próbą odczekaj i za każdym razem podwajaj czas oczekiwania, z odrobiną losowego jittera, żeby równolegli workerzy nie ponawiali jednocześnie. Zatrzymaj się po 4 lub 5 próbach.

W przypadku `concurrency_limit` lepszym rozwiązaniem jest wysyłanie mniejszej liczby zapytań naraz: Każde konto ma limit liczby stron czytanych jednocześnie; Twój dashboard go pokazuje, a support może go podnieść. W przypadku `fetch_timeout` zwiększ `timeout` do 60 sekund, zanim ponowisz próbę dla wolnej strony. Ponowienia nic nie kosztują, bo nieudana próba nigdy nie jest rozliczana.

*Python: ponawiaj tylko błędy, które mogą się zmienić*

```python
import os
import random
import time

import requests

API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}
RETRY = {"concurrency_limit", "fetch_timeout", "fetch_failed"}
FINAL = {"blocked_by_robots", "blocked_by_site_policy", "target_refused", "unsupported_content"}


def read_page(url, attempts=5, **options):
    wait = 1.0
    for attempt in range(1, attempts + 1):
        resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
        if resp.status_code == 200:
            return resp.json()
        error = resp.json().get("error", {})
        code = error.get("code", "unknown")
        if code in FINAL:
            return {"url": url, "skipped": code, "target_status": error.get("target_status")}
        if code not in RETRY or attempt == attempts or (code == "fetch_failed" and attempt >= 3):
            raise RuntimeError(f"{code}: {error.get('message')}")
        time.sleep(wait + random.uniform(0, wait / 2))
        wait = min(wait * 2, 30)
    raise RuntimeError("unreachable")


if __name__ == "__main__":
    print(read_page("https://example.com/blog/first-post", max_chars=20000).get("title"))

```

*Node.js: ta sama zasada z wykładniczym backoffem*

```javascript
const RETRY = new Set(['concurrency_limit', 'fetch_timeout', 'fetch_failed']);
const FINAL = new Set(['blocked_by_robots', 'blocked_by_site_policy', 'target_refused', 'unsupported_content']);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

export async function readPage(url, options = {}, attempts = 5) {
  let wait = 1000;
  for (let attempt = 1; attempt <= attempts; attempt++) {
    const params = new URLSearchParams({ url, ...options });
    const res = await fetch(`https://webreaderapi.com/api/v1/read?${params}`, {
      headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
    });
    const body = await res.json();
    if (res.ok) return body;
    const code = body.error?.code ?? 'unknown';
    if (FINAL.has(code)) return { url, skipped: code, target_status: body.error.target_status };
    if (!RETRY.has(code) || attempt === attempts) throw new Error(`${code}: ${body.error?.message}`);
    await sleep(wait + Math.random() * (wait / 2));
    wait = Math.min(wait * 2, 30000);
  }
}

```

## Co z błędami wewnątrz batcha lub crawlu?

Zadanie nie zatrzymuje się, bo jedna strona zawiodła. Ta strona pojawia się w `results` jako `{url, error}` z tymi samymi kodami co powyżej i nic nie kosztuje. Crawling, który napotka `blocked_by_robots` w sekcji, po prostu czyta resztę witryny.

Na końcu zadania policz kody błędów. Wiele `target_refused` w jednej witrynie oznacza, że witryna nie akceptuje zautomatyzowanych czytników; wiele `fetch_timeout` oznacza, że jest wolna, a dłuższy `timeout` w opcjach zadania może pomóc.

## Błędy: najczęstsze pytania

### Czy płacę za stronę blokowaną przez robots.txt?

Nie. 1 kredyt = 1 odczytana strona bez przeglądarki; 2 kredyty, gdy strona wymaga przeglądarki do wyrenderowania JavaScript. Zapytanie, które się nie powiedzie lub które zostanie odrzucone przez robots.txt witryny, nie jest obciążane opłatą. To samo dotyczy każdego innego błędu na tej stronie.

### Czy możecie odczytać stronę, która blokuje wasz crawler, jeśli o to poproszę?

Nie. Respektujemy wybór właściciela witryny, a polityka jest taka sama dla każdego klienta. Jeśli jesteś właścicielem witryny, dodaj regułę dla `WebReaderAPI` w swoim robots.txt; [strona crawlera](https://webreaderapi.com/pl/bot/) pokazuje jak.

### Dlaczego dostaję fetch_failed dla strony, która istnieje?

Witryna mogła być niedostępna, wolno akceptować połączenie TLS lub zwracać 404 zautomatyzowanym klientom. Ponów próbę raz lub dwa z backoffem. Jeśli nadal się nie udaje, sprawdź adres w przeglądarce.

### Co mój agent powinien powiedzieć użytkownikowi po odmowie?

Coś prostego, np. że witryna nie pozwala na zautomatyzowane odczytywanie tej strony. Następnie pozwól modelowi skorzystać z innych źródeł. Zwrócenie kodu błędu jako wyniku narzędzia wystarcza większości modeli.

---

WebReaderAPI: WebReaderAPI to API czytnika stron internetowych dla agentów AI, które zamienia każdą publiczną stronę internetową w czysty Markdown, z płatnością kryptowalutą i bez przesyłania dokumentów tożsamości. Przedpłacone kredyty od 0,07 USD/1K stron nigdy nie wygasają, a robots.txt jest respektowany. Bez KYC — bez dowodu, bez selfie, bez dokumentów. Płatność kryptowalutą.
