Errores de la API
Cómo manejar los errores de la API de FourA.
Formato de respuesta de error
La API devuelve objetos JSON planos para todos los errores. No hay objetos error anidados ni códigos de error.
{
"error": "Invalid API key"
}
Algunos errores incluyen campos adicionales como status, service, retryAfter, current o limits en el nivel superior:
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Seguimiento de una solicitud
Cada respuesta de la API (éxito o error) incluye un header X-FourA-Request-Id con un UUID para esa llamada. Regístralo de tu lado. Si necesitas preguntar al soporte qué pasó con una solicitud específica, ese ID nos permite encontrarla.
curl -i -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://example.com"}'
# HTTP/1.1 200 OK
# X-FourA-Request-Id: 9f1c4e6c-7b2a-4d3e-8a1f-2c9d8e4a3b15
# Content-Type: application/json
# ...
Tipos de errores
400: Bad Request
Al cuerpo de la request le faltan campos requeridos, contiene valores inválidos o nombra un destino que la API rechaza obtener.
{
"error": "Invalid request body format"
}
El mismo 400 también cubre la protección SSRF. Si tu url se resuelve a un rango de IP privado, de loopback o reservado (RFC 5735, RFC 6598, bloques reservados de IPv6), la request se rechaza antes de salir de la red de FourA:
{
"error": "Target <ip> resolves to a private/reserved IP"
}
Un JSON malformado en el cuerpo se rechaza de la misma manera, antes de leer cualquier campo:
{
"error": "Invalid JSON in request body"
}
Los campos proxy y ignoreProxies tienen sus propios errores 400. Ambos toman los ID de proxy opacos que devolvieron las respuestas anteriores, por lo que cualquier otra cosa no se podrá decodificar:
| Mensaje | Qué ocurrió |
|---|---|
Invalid proxy format |
El valor proxy no es un ID de proxy emitido por FourA. Una dirección de proxy sin procesar termina aquí. |
Invalid ignoreProxies format |
Una de las entradas en ignoreProxies no es un ID de proxy. |
Proxy not found |
El ID se decodificó correctamente pero ya no se resuelve en una salida activa. Elige uno nuevo. |
Solución: Comprueba que tu request incluya todos los campos obligatorios, que las URL utilicen http:// o https://, que el host se resuelva en una dirección pública y que cualquier valor de proxy sea un ID copiado textualmente de una respuesta anterior.
401: No autorizado
Tu API key falta o no es válida.
Falta la API key:
{
"error": "Missing API key. Include X-API-Key header."
}
Clave no válida:
{
"error": "Invalid API key"
}
Solución: Verifica que tu header X-API-Key contenga una clave válida. Genera una nueva clave desde el Dashboard si es necesario.
429: Rate Limited
Has enviado demasiadas requests en un periodo corto.
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": { "concurrency": 12, "rpm": 3000 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Solución: Espera el número de segundos en retryAfter antes de enviar más requests. Consulta Rate Limits para más detalles.
500: Error del servidor
Algo salió mal de nuestro lado.
Solución: Reintenta el request después de una breve pausa. Si el error persiste, comprueba la página de estado o contacta a soporte con el X-FourA-Request-Id del response fallido.
502: Upstream no disponible
FourA llegó a su propio motor pero no pudo usar la respuesta.
{
"error": "Upstream unavailable",
"details": "..."
}
Solución: Reintenta con una breve pausa. Esto está de nuestro lado, por lo que no te cuesta nada: el resultado es service_error y solo se factura success.
504: Timeout en el upstream
El motor no terminó dentro del límite de tiempo asignado para esta request.
{
"error": "Upstream timeout",
"details": "the backend did not finish inside the time budget for this request"
}
Un 504 trata sobre cuánto tiempo tomó el trabajo, no sobre tu clave, tus parámetros o tu proxy. Los objetivos lentos, las resoluciones en frío de desafíos y las páginas grandes son las causas habituales.
Solución: Aumenta timeout_ms en la request (Single acepta hasta 120000, Browser hasta 120000, Auto hasta 180000) o reintenta. FourA espera el presupuesto que declaraste más un pequeño margen, por lo que pedir más tiempo realmente te da más tiempo.
503: Servicio deshabilitado o al límite de capacidad
Un 503 significa que el servicio no está disponible temporalmente por mantenimiento o que has alcanzado el límite de concurrencia. Ambas respuestas incluyen un campo retryAfter. La forma de concurrencia también incluye current y limits.
{
"error": "Service disabled",
"status": 503,
"retryAfter": 60
}
Solución: Espera retryAfter segundos y luego vuelve a intentarlo. La página de estado enumera las ventanas de mantenimiento activas.
Una tercera forma de 503 no tiene retryAfter. Significa que el motor detrás de tu endpoint se estaba reiniciando cuando llegó tu llamada:
{
"error": "Backend service unavailable",
"backend_status": 503
}
Vuelve a intentarlo después de uno o dos segundos.
Leer fallos de /api/auto/
POST /api/auto/ responde con HTTP 200 siempre que se ejecute la escalera, incluso cuando falle cada peldaño. El resultado real se encuentra en el body:
{
"status": 0,
"error": "all attempts failed",
"attempts": 7,
"meta": { "rung": "fail", "solved": false, "attempts": 7, "credits": 47 }
}
Así que no derives en el estado de transporte para Auto. Lee status y error desde el cuerpo en su lugar. Un estado distinto de 200 genuino de /api/auto/ significa que FourA rechazó la llamada antes de que comenzara la cadena: 401, 400, 429 o 503, todos documentados anteriormente.
Fallos del lado del objetivo dentro de un 200 OK
No todos los fallos aparecen como un estado HTTP distinto de 2xx. Cuando el sitio objetivo devuelve HTTP 200 con un payload de error, FourA todavía te entrega el cuerpo, pero clasifica la request como application_error. Cuando el objetivo devuelve un estado distinto de 2xx que tus reglas validate no aceptan, el resultado es application_fail y el cuerpo se entrega sin cambios.
Ambos casos son facturables como si la request hubiera funcionado a nivel de red. La referencia Outcomes cubre la taxonomía completa.
Codificación de la respuesta
FourA decodifica automáticamente los cuerpos de las respuestas a UTF-8. Si el objetivo sirve windows-1251, gbk, shift_jis, iso-8859-* o cualquier otro charset declarado en la cabecera Content-Type o en una etiqueta HTML <meta charset>, recibes una cadena UTF-8 limpia en el campo data (single, proxy) o body (browser).
Para payloads binarios (imágenes, protobuf, audio crudo), configura returnBuffer: true en la request. El cuerpo se devuelve como un búfer base64 sin aplicar ninguna transcodificación de charset.
Estrategia de reintentos
Una política práctica de reintentos:
import time
import requests
def make_request(url, payload, api_key, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
url,
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json=payload,
)
if resp.status_code == 200:
return resp.json()
body = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
retry_after = body.get("retryAfter", 2 ** attempt)
request_id = resp.headers.get("X-FourA-Request-Id", "?")
if resp.status_code in (429, 503):
time.sleep(retry_after)
continue
if resp.status_code >= 500: # 500, 502, 503, 504 are all ours to fix
time.sleep(2 ** attempt)
continue
# 400/401/404 won't fix themselves
raise RuntimeError(f"{resp.status_code} (request {request_id}): {body.get('error')}")
raise RuntimeError(f"Exhausted {max_retries} retries")
Relacionado
- Límites de tasa: Detalles de concurrencia y RPM
- Resultados de request: Explicación de los siete valores de resultado
- Problemas comunes: Síntomas, causas y soluciones
- Defensas anti-bot: Cuando el cuerpo de la respuesta es una página de desafío en lugar de un error