Rate Limits
Cada request a la API de FourA pasa por tres comprobaciones antes de llegar a un motor: los límites de tu propio plan, luego la asignación compartida de la plataforma para el endpoint al que llamaste, y después la asignación compartida de la plataforma para todo el tráfico. Cada comprobación puede rechazar un request por sí sola, y cada una responde con un cuerpo diferente.
Las tres comprobaciones, en orden
- Límites del plan. Lo que permite tu propio plan: qué endpoints y parámetros incluye, cuántos requests pueden ejecutarse a la vez por endpoint, cuántos por minuto, cuántos requests de navegador por día, y los créditos y ancho de banda disponibles en el período de facturación.
- Límite global de la plataforma. Todo lo que el host de la API al que llamaste está procesando en ese momento, independientemente del endpoint al que se dirigió el tráfico. Un rechazo aquí reporta
"service": "api". - Límite de la plataforma por endpoint. Tráfico en el servicio single, proxy o browser al que llamaste.
Tu propio plan se evalúa primero, y ese orden es el contrato en lugar de un detalle de implementación. Las asignaciones compartidas son propiedad común, por lo que un request que la plataforma siempre iba a rechazar no debe consumirlas en el camino hacia su rechazo. Una cuenta que envía mucho más de lo que permite su plan se corta antes de tocar cualquier recurso que otros utilicen.
Las comprobaciones 2 y 3 cuentan el tráfico total de FourA, no el tuyo. Interpreta un rechazo de cualquiera de las dos como "FourA está ocupado", no como "enviaste demasiado". La comprobación 1 concierne únicamente a tu cuenta, y nada más en la plataforma la altera.
Un rechazo de cualquiera de las comprobaciones compartidas le devuelve a tu cuenta todo lo que su admisión contabilizó, tanto el cupo por minuto como el espacio diario de navegador, porque el request nunca llegó a un backend. Tampoco cuenta para la pausa de reintento descrita en Requests por minuto: fue la capacidad de FourA la que lo rechazó, no tu plan.
POST /api/auto/ no retiene ningún cupo propio. Las subllamadas Single, Proxy y Browser que realiza por ti pasan las tres comprobaciones como cualquier otro request, por lo que un lote paralelo de llamadas auto cuenta contra tu plan a través de sus subllamadas. (Tus conteos de requests y tasa de éxito cuentan la llamada auto en sí, una sola vez; las subllamadas se muestran como sus intentos).
Límites del plan
Un límite del plan responde con un header X-FourA-Limit que indica qué límite rechazó la llamada. El mismo código está en el cuerpo bajo reason, para que puedas ramificar la lógica según corresponda sin leer los headers. Cada cuerpo de límite de plan incluye error, reason y documentation; el resto de los campos depende del límite.
X-FourA-Limit |
Estado | Qué se agotó |
|---|---|---|
plan_limit_feature |
403 | El endpoint que llamaste, o el parámetro exitCountries, no está en tu plan |
plan_limit_premium |
403 | exitClass: premium no está en tu plan |
plan_limit_concurrency |
429 | Requests simultáneas en ese endpoint |
plan_limit_rate |
429 | Requests por minuto en ese endpoint |
plan_limit_browser_daily |
429 | Requests de navegador para el día |
plan_limit_credits |
429 | Créditos facturados para el periodo de facturación |
plan_limit_bandwidth |
429 | Ancho de banda para el periodo de facturación |
Los números detrás de cada límite corresponden a tu plan, y la pestaña Límites y funciones de Uso y límites los muestra junto a tu uso en tiempo real. No los escribas en el código: cada rechazo incluye el límite máximo que lo rechazó.
Una request rechazada no consume nada. El resultado es rate_limit, y solo se factura success.
Endpoint o parámetro no incluido en el plan
Un 403 con plan_limit_feature significa que la llamada solicitó algo que el plan no incluye. La verificación se ejecuta antes de contabilizar nada, por lo que la llamada rechazada no afecta tu rate limit ni tus contadores diarios.
{
"error": "The browser endpoint is not included in your plan (Free). Upgrade to use it.",
"reason": "plan_limit_feature",
"documentation": "https://foura.ai/prices"
}
El mismo código y estado responden a una llamada de POST /api/proxy/ que establece exitCountries en un plan sin segmentación geográfica. La cadena error indica el parámetro:
{
"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"
}
plan_limit_premium tiene la misma forma para exitClass: premium en un plan sin salidas premium. FourA puede, en su lugar, atender dicha solicitud desde el pool estándar e informar exitClass: standard en la respuesta, así que gestiona ambas respuestas. Ninguna de las dos consume una salida premium. Consulta exitClass.
Ningún 403 establece Retry-After. Esperar no cambia la respuesta.
Solicitudes simultáneas
La concurrencia se cuenta por endpoint: tu plan incluye un límite para Single, uno para Proxy y uno para Browser. La solicitud que supera el límite regresa como 429 con Retry-After: 1:
{
"error": "Concurrency limit reached: your plan allows 50 simultaneous proxy request(s). Retry when an in-flight request finishes.",
"reason": "plan_limit_concurrency",
"documentation": "https://foura.ai/prices",
"limit": 50,
"in_flight": 51,
"retry_after_seconds": 1
}
in_flight también cuenta el request rechazado, por lo que muestra al menos uno más que limit.
La solución es limitar tu propio paralelismo en lugar de reintentar con más insistencia. Responder a un 429 reenviando el mismo lote de inmediato genera otro 429 para cada llamada dentro de él. Consulta Ejecutar requests en paralelo para ver un patrón práctico.
Requests por minuto
Single y Proxy tienen un límite por minuto, medido en un minuto deslizante. Solo los requests admitidos cuentan para este límite: un request rechazado se descuenta, por lo que una cuenta que solicita de forma constante un poco más de su límite recibe su cupo asignado en lugar de ser rechazada casi por completo.
{
"error": "Rate limit reached: your plan allows 600 single requests per minute. Cool down and retry.",
"reason": "plan_limit_rate",
"documentation": "https://foura.ai/prices",
"limit_per_minute": 600,
"current_rate": 613,
"retry_after_seconds": 17
}
retry_after_seconds indica cuánto tiempo falta para que se admita una request más, si no envías nada más entretanto: al menos 1 segundo y como máximo 120. El header Retry-After incluye el mismo valor.
Reintentar requests rechazadas más rápido que eso tiene su propia regla. Cuando las requests rechazadas por este límite en el minuto móvil superan el doble del límite, la llamada se rechaza con una pausa de 30 segundos en su lugar:
{
"error": "Rate limit cooldown: 1250 requests over your plan's 600 single requests per minute were refused in the last minute and kept arriving. Pause for 30 seconds.",
"reason": "plan_limit_rate",
"documentation": "https://foura.ai/prices",
"limit_per_minute": 600,
"current_rate": 540,
"refused_last_minute": 1250,
"cooldown": true,
"retry_after_seconds": 30
}
Los rechazos durante la pausa no se contabilizan, por lo que la pausa termina por sí sola a medida que avanza el minuto, incluso para un cliente que sigue reintentando. Para distinguir la pausa del límite ordinario, lee cooldown en lugar del texto error.
Browser requests por día
Browser no tiene un límite por minuto. El límite de su plan es una cantidad de browser requests por día, contados desde la medianoche UTC, y el contador registra cada browser request admitida, no solo las exitosas.
{
"error": "Daily browser limit reached: your plan allows 300 browser requests per day (resets at midnight UTC).",
"reason": "plan_limit_browser_daily",
"documentation": "https://foura.ai/prices",
"limit_per_day": 300,
"used_today": 301
}
Este rechazo no incluye ningún retry_after_seconds ni encabezado Retry-After, ya que la espera es de horas en lugar de segundos. Trátalo como una detención y programa la siguiente ejecución para la medianoche UTC.
Créditos para el periodo de facturación
Solo cuentan los créditos facturados, lo que significa únicamente las solicitudes exitosas. Cuando el total facturado alcanza los créditos disponibles para ti en este periodo, se rechazan las solicitudes posteriores hasta que se restablezca el periodo o compres más.
{
"error": "Credit limit reached: 75,000 of 75,000 credits used this billing period. Upgrade your plan or wait for the reset.",
"reason": "plan_limit_credits",
"documentation": "https://foura.ai/prices",
"used": 75000,
"hard_stop": 75000,
"retry_after_seconds": 86400,
"resets_at": "2026-10-01T00:00:00.000Z"
}
hard_stop es el recuento de créditos facturados en el que se detienen las requests para este período. Léelo desde el body en lugar de calcularlo: ya incluye los créditos que hayas comprado además del plan.
Ancho de banda para el período de facturación
Los planes que incluyen un límite de ancho de banda rechazan requests una vez que el tráfico estándar de este período lo alcanza. El tráfico premium tiene su propia asignación y no cuenta para este límite. El ancho de banda comprado cuenta igual que el ancho de banda incluido, y el string error indica lo que tienes disponible, no solo lo que incluye el plan.
{
"error": "Bandwidth limit reached: 50 GB is available to you this billing period.",
"reason": "plan_limit_bandwidth",
"documentation": "https://foura.ai/prices",
"used_bytes": 53687091200,
"limit_bytes": 53687091200,
"retry_after_seconds": 86400,
"resets_at": "2026-10-01T00:00:00.000Z"
}
En ambos límites de período, retry_after_seconds está limitado a 24 horas; resets_at es el instante exacto en que se reinicia el período.
Campos de límites del plan
| Campo | Tipo | Presente en | Descripción |
|---|---|---|---|
error |
string | todos | Mensaje legible para humanos, incluido el número al que estás sujeto |
reason |
string | todos | plan_limit_ más el nombre del límite. Mismo valor que el header X-FourA-Limit. |
documentation |
string | todos | Enlace a la página de planes |
retry_after_seconds |
number | concurrencia, rate, créditos, ancho de banda | Cuánto tiempo esperar. Mismo valor que el header Retry-After. |
limit |
number | concurrencia | Solicitudes simultáneas que el plan permite en ese endpoint |
in_flight |
number | concurrencia | Solicitudes en ejecución en ese endpoint para tu cuenta, incluida la rechazada |
limit_per_minute |
number | rate | Solicitudes por minuto que el plan permite en ese endpoint |
current_rate |
number | rate | Solicitudes contabilizadas en el minuto móvil, incluida la rechazada |
refused_last_minute |
number | pausa de rate | Solicitudes que el límite por minuto rechazó en el minuto móvil. Solo en la pausa de 30 segundos. |
cooldown |
boolean | pausa de rate | true en la pausa de 30 segundos por reintentar demasiado rápido. Ausente en un rechazo ordinario por minuto. |
limit_per_day |
number | browser diario | Solicitudes de browser que el plan permite por día |
used_today |
number | browser diario | Solicitudes de browser contabilizadas hoy, incluida la rechazada |
used |
number | créditos | Créditos facturados hasta ahora en este período |
hard_stop |
number | créditos | Créditos facturados en los que las solicitudes se detienen en este período |
used_bytes |
number | ancho de banda | Tráfico estándar hasta ahora en este período, en bytes. El tráfico premium no está incluido. |
limit_bytes |
number | ancho de banda | Bytes disponibles en este período |
resets_at |
string | créditos, ancho de banda | Marca de tiempo ISO 8601 del final del período |
Los límites del plan usan retry_after_seconds. Los límites de la plataforma a continuación usan retryAfter. Un helper de reintentos debe leer ambos, o leer el header Retry-After, que solo establecen los límites del plan.
Límites de la plataforma
Las comprobaciones de la plataforma rastrean dos cosas por servicio y una más en todos ellos:
- Concurrencia: cuántas solicitudes está ejecutando FourA al mismo tiempo.
- RPM: cuántas solicitudes ha recibido FourA en los últimos 60 segundos.
Ambos contadores son compartidos por todos los que usan ese servicio. current y limits en las respuestas a continuación describen la plataforma, no tu cuenta. Si deseas tu propio número, lee in_flight desde una respuesta de límite del plan, o abre Usage & Limits en el dashboard.
429: RPM excedido
{
"error": "Rate limit exceeded",
"status": 429,
"service": "single",
"retryAfter": 5,
"current": {
"concurrency": 12,
"rpm": 3000
},
"limits": {
"maxConcurrency": 500,
"maxRpm": 3000
}
}
El servicio alcanzó el límite de requests permitidos en el último minuto. Espera retryAfter segundos.
503: Concurrency Exceeded
{
"error": "Service at capacity",
"status": 503,
"service": "proxy",
"retryAfter": 2,
"current": {
"concurrency": 500,
"rpm": 1200
},
"limits": {
"maxConcurrency": 500,
"maxRpm": 3000
}
}
El servicio está ejecutando tantas solicitudes como tiene permitido ejecutar a la vez. Esto se resuelve en segundos.
Servicio deshabilitado
Cuando un servicio se desconecta temporalmente por mantenimiento, la API devuelve 503 con un mensaje de error diferente:
{
"error": "Service disabled",
"status": 503,
"service": "single",
"retryAfter": 60,
"current": { "concurrency": 0, "rpm": 0 },
"limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}
Esto no es un rate limit. El servicio no está disponible temporalmente. Revisa el valor de retryAfter y vuelve a intentarlo tras esa cantidad de segundos. Esto suele resolverse en cuestión de minutos.
Ambas variantes de 503 contienen las mismas claves, así que bifurca según la cadena error y nunca según los campos presentes. Service disabled es mantenimiento, Service at capacity es concurrencia.
En la variante de mantenimiento, current.concurrency y current.rpm siempre son 0: la request se rechazó antes de realizar cualquier medición.
Campos de Platform Limit
| Campo | Tipo | Descripción |
|---|---|---|
error |
string | Mensaje de error legible para humanos |
status |
number | Código de estado HTTP (429 o 503) |
service |
string | Qué servicio rechazó la llamada: single, proxy, browser o api |
retryAfter |
number | Tiempo de espera recomendado en segundos antes de reintentar |
current.concurrency |
number | Requests que el servicio estaba ejecutando a nivel de plataforma al rechazar |
current.rpm |
number | Requests recibidas por el servicio a nivel de plataforma en los últimos 60 segundos |
limits.maxConcurrency |
number | Límite de concurrencia a nivel de plataforma del servicio |
limits.maxRpm |
number | Límite por minuto a nivel de plataforma del servicio |
Cómo gestionar cada rechazo con un solo helper
Retry-After se incluye en los límites del plan por los que vale la pena esperar, retry_after_seconds está en sus bodies y retryAfter está en los bodies de la plataforma. Lee los tres en ese orden y deténte en los límites del plan que ninguna espera resolverá:
import time
import requests
# Plan limits that a short wait never clears.
STOP_ON = {
"plan_limit_feature",
"plan_limit_premium",
"plan_limit_browser_daily",
"plan_limit_credits",
"plan_limit_bandwidth",
}
def wait_seconds(resp, attempt):
header = resp.headers.get("Retry-After")
if header and header.isdigit():
return int(header)
try:
body = resp.json()
except ValueError:
return 2 ** attempt
return body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt
def fetch(url, api_key, max_retries=5):
for attempt in range(max_retries):
resp = requests.post(
"https://eu.api.foura.ai/api/single/",
headers={"X-API-Key": api_key, "Content-Type": "application/json"},
json={"method": "GET", "url": url},
)
limit = resp.headers.get("X-FourA-Limit")
if limit in STOP_ON:
raise RuntimeError(f"stopped by plan limit {limit}: {resp.json().get('error')}")
if resp.status_code in (429, 503):
time.sleep(wait_seconds(resp, attempt))
continue
return resp
raise RuntimeError("Max retries exceeded")
Un límite diario no se restablece hasta pasadas varias horas y un límite de período tarda días, así que trátalos como una detención en lugar de una espera. Lee resets_at del cuerpo si quieres programar la siguiente ejecución.
Consejos
- Limita el número de requests en curso en lugar de reintentar un lote rechazado. Una tormenta de reintentos convierte un 429 en muchos.
- Lee
X-FourA-Limitprimero. Te indica en una sola cadena si el límite es tuyo o de la plataforma, y ningún rechazo de la plataforma lo establece. - No fijes los números en el código. Cada response por límite de plan incluye el tope que la rechazó, y Usage & Limits los muestra todos.
- El valor de
retryAfteren los límites de la plataforma es fijo según el tipo: 2 segundos para concurrencia, 5 para RPM, 60 para mantenimiento. - Compara contra
errorpara diferenciar los dos errores 503. Ambas estructuras incluyencurrentylimits, por lo que comprobar simplemente si esos campos existen interpretará el mantenimiento como un problema de concurrencia. - Un 403 con
X-FourA-Limitcorresponde a tu plan, no al sitio de destino. El destino nunca llegó a responder.
El puerto proxy tiene sus propios valores
Todo lo anterior corresponde a la API JSON. El tráfico que envías a través de proxy.foura.ai está sujeto a un conjunto independiente de límites de plan, en una unidad diferente: túneles abiertos simultáneamente, aperturas de túnel por minuto y el tráfico estándar del período de facturación. Esos rechazos se reciben como un estado HTTP con un header X-Foura-Error en lugar de un cuerpo JSON, ya que un CONNECT no tiene cuerpo donde incluirlo. Consulta Proxy Port para ver la tabla de estados y How Your Plan Is Metered para saber de qué saldo se descuentan los gigabytes del puerto.
Relacionado
- Run Requests in Parallel: Un patrón práctico de concurrencia limitada
- Usage & Limits: Todos los límites del plan junto a tu uso en tiempo real
- API Endpoints: Referencia completa de parámetros
- Error Handling: Todos los tipos de error y responses
- Response Headers:
X-FourA-Limit,Retry-Aftery el resto - Troubleshooting: Problemas comunes y soluciones