Sem KYC — sem documento, sem selfie, sem envio de arquivos. Pague com cripto: BTC, USDT, USDC, ETH, SOL, LTC
Idioma: Português
Obtenha uma chave de API

Docs · erros

Erros, recusas por robots.txt e retentativas

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.

Última atualização: · 6 min de leitura

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.

HTTPcodeCausaTentar novamente?
400invalid_urlNão é http ou https, muito longo, ou um endereço privado, local ou de metadados de nuvemNão: corrija a URL
400invalid_parameterUm parâmetro tem um valor fora da lista ou do intervaloNão: corrija a requisição
401invalid_api_keyChave ausente, errada ou revogadaNão: verifique a chave
402insufficient_creditsSeu saldo não tem mais créditosDepois de comprar um pacote
403blocked_by_robotsO robots.txt do site não permite a página para o WebReaderAPI ou para todos os robôsNão: definitivo
403blocked_by_site_policyO site declara Content-Signal: ai-input=noNão: definitivo
422unsupported_contentUm tipo que não lemos (vídeo, arquivo) ou um arquivo grande demaisNão
429concurrency_limitMuitas das suas requisições estão rodando ao mesmo tempoSim, com backoff
502target_refusedO site recusou: 403, 429, uma página de desafio ou uma barreira de login; o status está em target_statusNão: definitivo
502fetch_failedErro de DNS, TLS ou conexão, ou o site respondeu 404 ou 410Uma ou duas vezes, com backoff
504fetch_timeoutO site não respondeu dentro de timeoutSim, 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.

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

A partir de US$ 0,07/1K páginas · sem KYC

Obtenha uma chave de API