Sin KYC: ni documento de identidad, ni selfie, ni documentos. Paga con criptomonedas: BTC, USDT, USDC, ETH, SOL, LTC
Idioma: Español
Obtén una clave API

Docs · errores

Errores, rechazos por robots.txt y reintentos

Cada error tiene un estado HTTP, un code estable y un message legible. Ninguno usa un crédito. Algunos merecen un reintento; los rechazos que vienen de un sitio son definitivos y nunca los eludimos.

Última actualización: · 6 min de lectura

Respuesta rápida

¿Qué significan los códigos de error de WebReaderAPI y cuáles debo reintentar?

Los errores WebReaderAPI vienen en tres grupos, y ninguno se cobra nunca. Los problemas de la solicitud (invalid_url, invalid_api_key, insufficient_credits) requieren una corrección de tu parte. Las decisiones del sitio (blocked_by_robots, blocked_by_site_policy, target_refused) son definitivas. Los fallos temporales (fetch_failed, fetch_timeout, concurrency_limit) se pueden reintentar con backoff. Los créditos empiezan en 0,07 US$/1K páginas, se pagan con criptomonedas y sin KYC.

¿Qué códigos de error puede devolver la API?

El cuerpo siempre es {"error": {"code", "message"}}, más target_status cuando el rechazo vino del sitio.

HTTPcodeCausa¿Reintentar?
400invalid_urlNo es http o https, es demasiado larga, o es una dirección privada, local o de metadatos en la nubeNo: corrige la URL
400invalid_parameterUn parámetro tiene un valor fuera de su lista o rangoNo: corrige la solicitud
401invalid_api_keyClave ausente, incorrecta o revocadaNo: revisa la clave
402insufficient_creditsTu saldo no tiene créditos disponiblesDespués de comprar un pack
403blocked_by_robotsEl robots.txt del sitio no permite la página para WebReaderAPI ni para ningún robotNo: es definitivo
403blocked_by_site_policyEl sitio declara Content-Signal: ai-input=noNo: es definitivo
422unsupported_contentUn tipo que no leemos (vídeo, archivo) o un archivo demasiado grandeNo
429concurrency_limitHay demasiadas solicitudes tuyas ejecutándose a la vezSí, con backoff
502target_refusedEl sitio rechazó la petición: 403, 429, una página de desafío o un muro de inicio de sesión; su estado está en target_statusNo: es definitivo
502fetch_failedError de DNS, TLS o conexión, o el sitio respondió 404 o 410Una o dos veces, con backoff
504fetch_timeoutEl sitio no respondió en timeoutSí, con backoff o un timeout más largo

¿Por qué son definitivos el robots.txt y los rechazos del sitio?

Porque son la decisión del propietario del sitio, y WebReaderAPI está diseñado para respetarla. Respetamos robots.txt: nuestro rastreador se identifica como WebReaderAPI, sigue las reglas escritas para él o para todos los robots y no lee una página que un sitio prohíbe. Las páginas rechazadas no se cobran. Si un sitio declara Content-Signal: ai-input=no en su robots.txt, no leemos sus páginas para ti.

Cuando un sitio rechaza una solicitud de otra forma, con un 403, un 429, una página de desafío o un formulario de inicio de sesión, recibes target_refused y el estado propio del sitio en target_status. Si un sitio rechaza nuestra solicitud, devolvemos su respuesta: nunca resolvemos CAPTCHAs, iniciamos sesión en cuentas ni intentamos saltar un bloqueo o un paywall. Reintentar la misma URL un minuto después normalmente dará la misma respuesta, así que trata estos códigos como un resultado, no como un fallo: regístralos y continúa.

¿Cómo compruebo por qué se rechazó una página?

Abre el /robots.txt del sitio y busca un grupo que nombre WebReaderAPI, o el grupo * que se aplica a todos los robots. Una línea Disallow que coincida con la ruta explica un blocked_by_robots. Una línea como Content-Signal: ai-input=no explica un blocked_by_site_policy.

Conservamos una copia de cada robots.txt durante un máximo de 24 horas. Si un sitio acaba de abrir una sección a los robots, el cambio puede tardar hasta un día en aplicarse. Los propietarios de sitios pueden leer cómo se comporta nuestro rastreador, y cómo permitirlo o bloquearlo, en nuestra página del rastreador.

¿Cómo debo reintentar los fallos temporales?

Reintenta solo concurrency_limit, fetch_timeout y, una o dos veces, fetch_failed. Espera antes de cada nuevo intento y duplica la espera cada vez, con un poco de jitter aleatorio para que los workers en paralelo no reintenten a la vez. Detente tras 4 o 5 intentos.

Para concurrency_limit, la mejor solución es enviar menos solicitudes a la vez: Cada cuenta tiene un límite de cuántas páginas lee a la vez; tu panel lo muestra y el soporte puede ampliarlo. Para fetch_timeout, sube timeout hasta 60 segundos antes de reintentar un sitio lento. Los reintentos no cuestan nada, porque un intento fallido nunca se factura.

Python: reintenta solo los errores que pueden cambiar
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: la misma regla con 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);
  }
}

¿Y los errores dentro de un lote o de un rastreo?

Un trabajo no se detiene porque falle una página. Esa página aparece en results como {url, error} con los mismos códigos que arriba, y no cuesta nada. Un rastreo que se encuentra con blocked_by_robots en una sección simplemente lee el resto del sitio.

Al final de un trabajo, cuenta los códigos de error. Muchos target_refused en un sitio significan que el sitio no acepta lectores automatizados; muchos fetch_timeout significan que es lento, y un timeout más largo en las opciones del trabajo puede ayudar.

Errores: preguntas frecuentes

¿Me cobran por una página que robots.txt bloquea?

No. 1 crédito = 1 página leída sin navegador; 2 créditos cuando la página necesita un navegador para renderizar JavaScript. Una solicitud que falla, o que el robots.txt de un sitio rechaza, no se cobra. Lo mismo ocurre con cualquier otro error de esta página.

¿Pueden leer una página que bloquea su rastreador si se lo pido?

No. Respetamos la decisión del propietario del sitio, y la política es la misma para todos los clientes. Si eres el propietario del sitio, añade una regla para WebReaderAPI en tu robots.txt; la página del rastreador explica cómo.

¿Por qué recibo fetch_failed para una página que existe?

Puede que el sitio estuviera caído, tardara en aceptar una conexión TLS o devolviera 404 a clientes automatizados. Reintenta una o dos veces con backoff. Si sigue fallando, comprueba la dirección en un navegador.

¿Qué debería decir mi agente al usuario tras un rechazo?

Algo simple, como que el sitio no permite la lectura automatizada de esa página. Luego deja que el modelo use otras fuentes. Devolver el código de error como resultado de la herramienta basta para la mayoría de los modelos.

Desde 0,07 US$/1K páginas · sin KYC

Obtén una clave API