Problemas comunes

Soluciones a los problemas más comunes al usar la API de FourA.

Contenido vacío o incompleto

Síntoma: La API devuelve un estado 200 pero el campo data está vacío o no incluye el contenido esperado.

Causa: La página de destino utiliza JavaScript para renderizar el contenido tras la carga inicial de la página.

Solución: Cambia del endpoint individual al endpoint de navegador. Utiliza checkText para verificar que el contenido se haya cargado:

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/products",
    "timeout_ms": 15000,
    "checkText": "product-list"
  }'

Nota: el endpoint de browser devuelve el contenido en el campo body (no en data).

403 Forbidden o páginas de verificación

Síntoma: La API devuelve HTML que contiene una página de verificación o una página de acceso denegado.

Causa: El sitio de destino detectó la request como automatizada y la bloqueó.

Solución: Usa el endpoint de proxy para la rotación automática de IP:

curl -X POST https://eu.api.foura.ai/api/proxy/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "maxTries": 5,
    "request": {
      "method": "GET",
      "url": "https://example.com/prices",
      "unblocker": true
    }
  }'

Si el problema persiste, aumenta maxTries para darle más intentos a la rotación de proxy.

Un 403 devuelto por el destino llega como HTTP 200 con status: 403 dentro del body. Un 403 en la llamada misma, con un header X-FourA-Limit, es diferente: consulta 403 Not in Your Plan.

Errores de Timeout

Síntoma: Las requests fallan con un error de timeout.

Causa: La página de destino tarda más en cargar que el timeout configurado.

Solución: Aumenta timeout_ms (el valor predeterminado es 15s para single, 30s para browser y 45s para proxy):

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://slow-site.com",
    "timeout_ms": 60000
  }'

Para browser requests, verifica también que el valor de tu checkText aparezca realmente en la página. Un error tipográfico hace fallar la llamada con checkText:<your text> not found.

403 Not in Your Plan

Síntoma: La API devuelve 403 con un header X-FourA-Limit y un reason de plan_limit_feature o plan_limit_premium.

{
  "error": "Geo targeting (the exitCountries parameter) is not included in your plan (Free). Remove the parameter or upgrade.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

Causa: Tu plan no incluye el endpoint al que llamaste o el parámetro que enviaste. plan_limit_feature cubre un endpoint excluido y exitCountries sin geolocalización; plan_limit_premium cubre exitClass: premium sin salidas premium. El destino nunca fue contactado y no se gastó nada.

Solución: Elimina el parámetro, llama a un endpoint incluido en tu plan o actualiza tu suscripción. La pestaña Límites y funciones de Uso y límites detalla lo que incluye tu plan. No reintentes sin cambios: no se establece ningún Retry-After porque esperar no cambiará la respuesta.

429 Too Many Requests

Síntoma: La API devuelve 429.

Causa: Una de dos comprobaciones rechazó la llamada y la respuesta te indica cuál fue. Si incluye un header X-FourA-Limit, se alcanzó uno de los límites de tu plan: requests simultáneos o requests por minuto en ese endpoint, requests de Browser para el día, o los créditos o ancho de banda para el período de facturación. Si no incluye dicho header, la cuota compartida por minuto de la plataforma para ese servicio estaba llena, lo cual depende del tráfico de FourA y no del tuyo.

Solución: Lee primero X-FourA-Limit. Espera cuando el límite esté a unos segundos y detén las solicitudes cuando no sea así. Los límites del plan que se resuelven esperando incluyen los segundos en el header Retry-After y en retry_after_seconds; el límite compartido los incluye en retryAfter:

import time
import requests

# Plan limits that come back in hours or days, not seconds.
STOP_ON = {"plan_limit_browser_daily", "plan_limit_credits", "plan_limit_bandwidth"}

def make_request(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code == 429:
            limit = resp.headers.get("X-FourA-Limit")
            if limit in STOP_ON:
                raise Exception(f"stopped by {limit}: {resp.json().get('error')}")
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Rate limit not resolved after retries")

# Example: single request
make_request(
    "https://eu.api.foura.ai/api/single/",
    {"method": "GET", "url": "https://example.com"}
)

Si el header indicaba plan_limit_concurrency o plan_limit_rate, la solución es limitar cuántas llamadas mantienes abiertas y cuántas inicias por minuto en lugar de reintentar con más agresividad. Reenviar un lote rechazado de inmediato hace que se rechace todo el lote de nuevo. Las llamadas rechazadas no cuentan para tu límite por minuto, pero si continúan llegando a más del doble de ese límite, los rechazos se convierten en un cooldown: el cuerpo del 429 incluye cooldown: true y te pide pausar durante 30 segundos (retry_after_seconds: 30). Run Requests in Parallel contiene el patrón, y Usage & Limits en el Dashboard muestra tus contadores en vivo junto a tus límites.

503 Service Unavailable

Síntoma: la API devuelve un estado 503.

Causa: esto ocurre en dos casos:

  1. El servicio está al límite de su capacidad. FourA ya está ejecutando tantas solicitudes en ese motor como se le permite ejecutar al mismo tiempo, contabilizadas en todo el tráfico global y no solo en el tuyo. Service at capacity en el campo error. Generalmente se resuelve en segundos.
  2. Servicio deshabilitado temporalmente. Hay una ventana de mantenimiento en curso. Service disabled en el campo error.

Ambos casos incluyen un campo retryAfter en la respuesta. Ninguno de los dos es un límite del plan: los límites de tu propio plan siempre responden con un header X-FourA-Limit en un 403 o un 429, nunca con un 503.

Solución: espera durante retryAfter segundos y luego reintenta:

import time
import requests

def make_request_with_retry(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code in (429, 503):
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Request not resolved after retries")

Un 503 por capacidad significa que FourA está ocupado, por lo que aplicar backoff y reintentar es la solución completa. Si recibes un 429 con X-FourA-Limit en su lugar, ese error corresponde a tu lado: reduce la cantidad de requests paralelos en tu pipeline.

504 Upstream Timeout

Síntoma: La API devuelve 504 con {"error": "Upstream timeout"}.

Causa: El trabajo no terminó dentro del límite de tiempo que definiste para el request. Un destino lento, la resolución en frío de un challenge o una página muy pesada pueden provocarlo. No es un problema con tu clave, tus parámetros ni tu proxy.

Solución: Dale más tiempo a la llamada o reintenta. FourA espera tu timeout_ms más un pequeño margen, por lo que aumentarlo extiende la espera de forma efectiva:

{
  "url": "https://slow-site.com/report",
  "timeout_ms": 90000
}

Para /api/auto/ en un objetivo protegido, una primera llamada en frío puede tardar decenas de segundos. Su timeout_ms cubre toda la escala y acepta hasta 180000.

Cuando el propio /api/auto/ agota ese presupuesto, la llamada sigue respondiendo HTTP 200. El cuerpo incluye un error que comienza con time budget exhausted, y status suele ser 504 (un intento fallido anterior puede dejar su propio estado allí en su lugar). Aumenta timeout_ms o reintenta.

502 Upstream Unavailable

Síntoma: La API devuelve 502 con {"error": "Upstream unavailable"}, o 503 con {"error": "Backend service unavailable"}.

Causa: FourA llegó a su propio motor pero no pudo usar la respuesta, normalmente porque una instancia se estaba reiniciando.

Solución: Reintenta con un backoff corto. Ambos se clasifican como service_error, y solo se factura success, por lo que un reintento no te cuesta nada extra. Si dura más de uno o dos minutos, consulta la página de estado.

401 Authentication Errors

Síntoma: Cada request devuelve 401 Unauthorized.

Lista de verificación:

  1. Verifica que el header sea X-API-Key: YOUR_API_KEY (no Authorization: Bearer ni Api-Key)
  2. Comprueba si hay espacios en blanco adicionales o saltos de línea en tu API key
  3. Genera una nueva key desde el Dashboard si la actual podría estar comprometida

400 Target Resolves to a Private or Reserved IP

Síntoma: La API devuelve 400 con Refusing to fetch <target>: target resolves to a private or reserved IP range antes de que la request salga de FourA.

Causa: Tu url resuelve a un rango de IP privada, loopback o reservada (RFC 5735, RFC 6598 o bloques reservados de IPv6). FourA rechaza estos objetivos para que su red no pueda usarse para alcanzar hosts internos.

Solución: Haz fetch de una URL pública. Si estás haciendo pruebas, usa un objetivo público como https://example.com o https://httpbin.org/get. Si tu objetivo previsto es un servicio que administras, exponlo primero en un hostname público.

{ "error": "Refusing to fetch <target>: target resolves to a private or reserved IP range. The FourA scraping API only forwards requests to public internet hosts." }

Un nombre de host que no se puede resolver no es rechazado. La llamada devuelve HTTP 200 con status: 0 y el motivo (could not resolve <host>: <reason>), como cualquier destino al que FourA no puede acceder, y no se factura.

no_eligible_proxy al usar exitCountries

Síntoma: Una llamada /api/proxy/ con exitCountries devuelve HTTP 200 con un JSON envelope de error:

{
  "error": "No eligible proxy found for exit countries: CZ, GB",
  "code": "no_eligible_proxy",
  "details": { "exitCountries": ["CZ", "GB"] },
  "total": 0.084
}

Causa: El pool de proxy actual no tiene ninguna salida funcional cuyo país visible para el destino coincida con tu allowlist. FourA nunca recurre a un país no solicitado cuando configuras exitCountries.

Solución: Conserva el alcance solicitado y vuelve a intentarlo más tarde. El pool se actualiza aproximadamente cada diez minutos, por lo que un país que no tiene coincidencias ahora a menudo obtiene una en menos de una hora.

import time, requests

def fetch_scoped(url, countries, max_attempts=6, wait_sec=600):
    for _ in range(max_attempts):
        r = requests.post("https://eu.api.foura.ai/api/proxy/",
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json={"maxTries": 5, "exitCountries": countries,
                  "request": {"method": "GET", "url": url}}).json()
        if r.get("code") == "no_eligible_proxy":
            time.sleep(wait_sec)
            continue
        return r
    raise RuntimeError(f"no eligible exit in {countries} after {max_attempts} attempts")

Solo amplía la lista de países si el requisito de país de tu flujo de trabajo cambió genuinamente. Los fallbacks silenciosos a otros países pueden romper la lógica posterior que depende de la ubicación geográfica.

El cuerpo de la respuesta devuelve texto ilegible

Síntoma: El data (o body) de la respuesta contiene mojibake o caracteres ilegibles cuando el destino utiliza un charset que no es UTF-8.

Causa: De forma predeterminada, FourA decodifica automáticamente los cuerpos de las respuestas a UTF-8 según el header Content-Type del destino o una etiqueta HTML <meta charset>. Si el destino miente sobre su charset, obtendrás texto ilegible.

Solución: Para cargas binarias (imágenes, protobuf, audio sin procesar), establece returnBuffer: true en la request. Single y Proxy devolverán entonces data como un objeto que contiene los bytes sin procesar, {"type": "Buffer", "data": [<byte values>]}, sin aplicar transcodificación de charset.

{
  "method": "GET",
  "url": "https://example.com/image.png",
  "returnBuffer": true
}

Para objetivos de texto que declaran incorrectamente su charset, decodifica los bytes sin procesar tú mismo: realiza la petición con returnBuffer: true, lee los valores de bytes en data.data y luego decodifícalos con el charset correcto.

HTML inesperado en lugar de JSON

Síntoma: Esperabas JSON del sitio objetivo, pero recibiste HTML.

Causa: La página de destino puede entregar contenido diferente según los headers.

Solución: Agrega un header Accept y habilita unblocker para obtener headers de navegador realistas:

curl -X POST https://eu.api.foura.ai/api/single/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "GET",
    "url": "https://api.example.com/data",
    "headers": [["Accept", "application/json"]],
    "unblocker": true
  }'

También puedes configurar tryJsonData en true para que FourA parsee automáticamente las respuestas JSON.

El cuerpo es una página de challenge, no el contenido

Síntoma: La llamada tuvo éxito, status es 200, pero data (o body) es una verificación de bots en lugar de la página que querías.

Causa: El objetivo ejecutó una verificación de bots que FourA encontró pero no pudo superar. La respuesta lo indica: Single y Proxy devuelven defense con solved: false, y Browser devuelve defenseSolved: false con el proveedor en defenses.present.

Solución: Revisa defense.vendor primero y luego escala. Prueba con un perfil de navegador diferente en Single, pasa a Proxy para obtener una salida diferente o usa Browser para que se ejecute JavaScript. Referencia completa de campos y lista de proveedores: Site checks.

Agrega una subcadena validate.data.accept que solo contenga la página real. Una página de verificación que FourA reconoce nunca se considera un éxito: se devuelve con un encabezado X-FourA-Check-Page y no se factura. Sin validate, una página de verificación que FourA no reconoce, devuelta con HTTP 200, cuenta como un éxito, y te enterarás más adelante en lugar de en la propia llamada.

¿Sigues con problemas?

Si ninguna de las soluciones anteriores funciona:

  1. Consulta la página de estado para ver si hay incidentes en curso
  2. Revisa las métricas de tus requests en el Dashboard
  3. Contacta a soporte en support@foura.ai con los detalles de tu request (incluye el X-FourA-Request-Id de la respuesta fallida)

Próximos pasos

Actualizado: 30 de septiembre de 2026