# Erros, recusas por robots.txt e retentativas

> Todos os códigos de erro do WebReaderAPI: recusas por robots.txt, recusas do site, timeouts e erros de crédito, quais tentar de novo e por que nenhum é cobrado.

- URL: https://webreaderapi.com/pt/docs/errors/
- Última atualização: 2026-10-22
- Editor: WebReaderAPI (https://webreaderapi.com)

Cada erro tem um status HTTP, um `code` estável e uma `message` legível. Nenhum deles consome um crédito. Alguns valem uma retentativa; as recusas que vêm de um site são definitivas, e nós nunca contornamos isso.

**Resposta rápida — O que significam os códigos de erro do WebReaderAPI, e quais devo tentar de novo?**

Os erros **WebReaderAPI** vêm em três grupos, e nenhum deles é jamais cobrado. Problemas na requisição (`invalid_url`, `invalid_api_key`, `insufficient_credits`) exigem uma correção do seu lado. Decisões do site (`blocked_by_robots`, `blocked_by_site_policy`, `target_refused`) são definitivas. Falhas temporárias (`fetch_failed`, `fetch_timeout`, `concurrency_limit`) podem ser tentadas de novo com backoff. Os créditos começam em US$ 0,07/1K páginas, pagos com cripto e sem KYC.

## Quais códigos de erro a API pode retornar?

O corpo é sempre `{"error": {"code", "message"}}`, mais `target_status` quando a recusa veio do site.

| HTTP | `code` | Causa | Tentar novamente? |
| --- | --- | --- | --- |
| 400 | `invalid_url` | Não é http ou https, muito longo, ou um endereço privado, local ou de metadados de nuvem | Não: corrija a URL |
| 400 | `invalid_parameter` | Um parâmetro tem um valor fora da lista ou do intervalo | Não: corrija a requisição |
| 401 | `invalid_api_key` | Chave ausente, errada ou revogada | Não: verifique a chave |
| 402 | `insufficient_credits` | Seu saldo não tem mais créditos | Depois de comprar um pacote |
| 403 | `blocked_by_robots` | O robots.txt do site não permite a página para o WebReaderAPI ou para todos os robôs | Não: definitivo |
| 403 | `blocked_by_site_policy` | O site declara `Content-Signal: ai-input=no` | Não: definitivo |
| 422 | `unsupported_content` | Um tipo que não lemos (vídeo, arquivo) ou um arquivo grande demais | Não |
| 429 | `concurrency_limit` | Muitas das suas requisições estão rodando ao mesmo tempo | Sim, com backoff |
| 502 | `target_refused` | O site recusou: 403, 429, uma página de desafio ou uma barreira de login; o status está em `target_status` | Não: definitivo |
| 502 | `fetch_failed` | Erro de DNS, TLS ou conexão, ou o site respondeu 404 ou 410 | Uma ou duas vezes, com backoff |
| 504 | `fetch_timeout` | O site não respondeu dentro de `timeout` | Sim, com backoff ou um timeout maior |

## Por que robots.txt e recusas do site são definitivos?

Porque são a decisão do dono do site, e o WebReaderAPI é feito para segui-la. Respeitamos o robots.txt: nosso crawler se identifica como WebReaderAPI, segue as regras escritas para ele ou para todos os robôs e não lê uma página que o site proíbe. Páginas recusadas não são cobradas. Se um site declara Content-Signal: ai-input=no no seu robots.txt, não lemos as páginas dele para você.

Quando um site recusa uma requisição de outra forma, com um 403, um 429, uma página de desafio ou um formulário de login, você recebe `target_refused` e o status do próprio site em `target_status`. Se um site recusa nossa requisição, devolvemos a resposta dele: nunca resolvemos CAPTCHAs, nunca entramos em contas e nunca tentamos contornar um bloqueio ou um paywall. Tentar a mesma URL de novo um minuto depois normalmente dará a mesma resposta, então trate esses códigos como um resultado, não como uma falha: registre-os e siga em frente.

## Como verifico por que uma página foi recusada?

Abra o `/robots.txt` do site e procure um grupo que nomeie `WebReaderAPI`, ou o grupo `*` que se aplica a todos os robôs. Uma linha `Disallow` que corresponde ao caminho explica um `blocked_by_robots`. Uma linha como `Content-Signal: ai-input=no` explica um `blocked_by_site_policy`.

Mantemos uma cópia de cada robots.txt por no máximo 24 horas. Se um site acabou de abrir uma seção para robôs, a mudança pode levar até um dia para valer. Os donos de sites podem ler como nosso crawler se comporta, e como permiti-lo ou bloqueá-lo, na [nossa página do crawler](https://webreaderapi.com/pt/bot/).

## Como devo tentar novamente falhas temporárias?

Tente novamente apenas `concurrency_limit`, `fetch_timeout` e, uma ou duas vezes, `fetch_failed`. Espere antes de cada nova tentativa e dobre a espera a cada vez, com um pouco de jitter aleatório para que workers paralelos não tentem juntos. Pare após 4 ou 5 tentativas.

Para `concurrency_limit`, a melhor solução é enviar menos requisições ao mesmo tempo: Cada conta tem um limite de quantas páginas lê ao mesmo tempo; seu dashboard mostra esse limite, e o suporte pode aumentá-lo. Para `fetch_timeout`, aumente `timeout` até 60 segundos antes de tentar novamente um site lento. Tentativas não custam nada, porque uma tentativa que falha nunca é cobrada.

*Python: tente novamente apenas os erros que podem mudar*

```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: a mesma regra com backoff exponencial*

```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);
  }
}

```

## E os erros dentro de um lote ou crawl?

Um job não para porque uma página falha. Essa página aparece em `results` como `{url, error}` com os mesmos códigos acima, e não custa nada. Um rastreamento (crawl) que encontra `blocked_by_robots` em uma seção simplesmente lê o restante do site.

Ao final de um job, conte os códigos de erro. Muitos `target_refused` em um site significam que o site não aceita leitores automatizados; muitos `fetch_timeout` significam que ele é lento, e um `timeout` maior nas opções do job pode ajudar.

## Erros: dúvidas frequentes

### Sou cobrado por uma página que o robots.txt bloqueia?

Não. 1 crédito = 1 página lida sem navegador; 2 créditos quando a página precisa de um navegador para renderizar JavaScript. Uma requisição que falha, ou que o robots.txt de um site recusa, não é cobrada. O mesmo vale para todos os outros erros desta página.

### Você consegue ler uma página que bloqueia seu crawler se eu pedir?

Não. Seguimos a escolha do dono do site, e a política é a mesma para todos os clientes. Se o site for seu, adicione uma regra para `WebReaderAPI` no seu robots.txt; a [página do crawler](https://webreaderapi.com/pt/bot/) mostra como.

### Por que recebo fetch_failed para uma página que existe?

O site pode ter ficado fora do ar, ter demorado a aceitar uma conexão TLS ou estar retornando 404 para clientes automatizados. Tente de novo uma ou duas vezes com backoff. Se continuar falhando, verifique o endereço em um navegador.

### O que meu agente deve dizer ao usuário após uma recusa?

Algo simples, como: o site não permite a leitura automatizada dessa página. Depois, deixe o modelo usar outras fontes. Retornar o código de erro como resultado da ferramenta é o suficiente para a maioria dos modelos.

---

WebReaderAPI: O WebReaderAPI é uma API de leitor web para agentes de IA que converte qualquer página web pública em Markdown limpo, pago com cripto e sem envio de documentos. Os créditos pré-pagos a partir de US$ 0,07/1K páginas nunca expiram, e o robots.txt é respeitado. Sem KYC — sem documento, sem selfie, sem envio de arquivos. Pague com cripto.
