Límites de uso (rate limits)¶
Algunos endpoints aplican un límite de uso para proteger la estabilidad de la plataforma. Los límites se cuentan por organización (salvo el login, que se cuenta por IP).
Límites por endpoint¶
| Método | Endpoint | Límite |
|---|---|---|
POST |
/api/auth/login |
5 requests / 5 min por IP |
POST |
/api/campaign/start |
5 requests / min por organización |
POST |
/api/campaign/start-upload |
5 requests / min por organización |
POST |
/api/campaign/schedule |
10 requests / 5 min por organización |
GET |
/api/campaigns |
100 requests / min por organización |
GET |
/api/analytics/stats |
30 requests / min por organización |
Los endpoints que no aparecen en esta tabla no tienen un límite específico documentado. Aun así, haz un uso razonable: evita ráfagas innecesarias y agrupa tus consultas.
No hay headers X-RateLimit-*¶
La API no devuelve headers de tipo X-RateLimit-Limit, X-RateLimit-Remaining ni
X-RateLimit-Reset. No intentes leer el estado del límite desde headers: no existen.
En su lugar, el cliente infiere el límite a partir de la respuesta 429:
- Cuando superas un límite, la API responde
429. - Algunas respuestas
429incluyen el headerRetry-Aftercon la cantidad de segundos que debes esperar antes de reintentar. No todas lo incluyen — trata el header como opcional.
Hoy Retry-After viene en el 429 de estos endpoints:
| Método | Endpoint |
|---|---|
GET |
/api/campaigns |
GET |
/api/analytics/stats |
POST |
/api/campaign/schedule |
En el resto de los endpoints — incluidos POST /api/auth/login,
POST /api/campaign/start y POST /api/campaign/start-upload — el 429 no trae
Retry-After. Para POST /api/campaign/schedule el header aparece sólo cuando se supera el
límite general del endpoint; si lo que se supera es la cuota por organización, el 429 llega
sin header.
Diseña tu cliente para que funcione sin el header (ver backoff exponencial abajo) y úsalo como refinamiento cuando esté presente.
El cuerpo del 429 tiene dos formas¶
La misma división de arriba se ve en el cuerpo: el 429 no tiene un shape
único. Escribe tu manejo de errores para tolerar las dos.
Forma A — el 429 que trae Retry-After (los tres endpoints de la tabla
anterior, cuando se supera el límite general del endpoint):
{
"error": "Too many requests. Please slow down.",
"error_code": "RATE_LIMITED",
"retry_after_seconds": 30
}
retry_after_seconds repite en el body el valor del header Retry-After.
Forma B — el 429 que no trae Retry-After (POST /api/auth/login,
POST /api/campaign/start, POST /api/campaign/start-upload, y
POST /api/campaign/schedule cuando lo que se supera es la cuota por
organización). El cuerpo trae sólo error, con un texto descriptivo del
límite que tocaste:
Con una excepción: el 429 de POST /api/auth/login agrega además
"success": false.
Lo único que puedes dar por garantizado en cualquier 429 es el campo error.
error_code, retry_after_seconds y success aparecen sólo en algunas formas.
Los textos de error son descriptivos y pueden cambiar: no los uses para
ramificar lógica — ramifica por el código HTTP 429.
El 429 no incluye error_id ni request_id. Esos campos aparecen en las
respuestas 500, no aquí; no los busques en el cuerpo de un rate limit.
Si el 429 trae Retry-After, respétalo. Reintentar antes de ese tiempo volverá a fallar y
puede prolongar el bloqueo. Si no lo trae, aplica backoff exponencial (ver abajo).
Recomendación: backoff exponencial¶
Para integraciones robustas, maneja el 429 con backoff exponencial. El backoff es la base
—funciona con o sin header—; usa Retry-After como espera mínima cuando venga:
import time
import requests
API_BASE_URL = "https://api.neuronstudio.ai"
HEADERS = {"X-API-Key": "nrn_live_abcdef1234567890abcdef1234567890"}
def get_with_backoff(path, params=None, max_intentos=5):
espera = 1
for intento in range(max_intentos):
resp = requests.get(f"{API_BASE_URL}{path}", headers=HEADERS, params=params)
if resp.status_code != 429:
return resp
# Respetar Retry-After si viene; si no, usar backoff exponencial.
retry_after = int(resp.headers.get("Retry-After", espera))
time.sleep(max(retry_after, espera))
espera = min(espera * 2, 60) # tope de 60s
return resp # último intento (probablemente sigue siendo 429)
Pautas:
- Empieza con una espera corta y duplícala en cada reintento, con un tope (ej. 60s).
- Cuando el
429traigaRetry-After, úsalo como mínimo: nunca esperes menos que ese valor. Cuando no venga, no asumas un default del servidor — usa tu propio backoff. - Agrega un poco de jitter (aleatoriedad) si tienes muchas integraciones concurrentes, para evitar reintentos sincronizados.
Buenas prácticas para no llegar al límite¶
- Pagina con cursor en
GET /api/analytics/callsen vez de repetir consultas amplias. - Espacia el polling: consulta
GET /api/analytics/statsocallscada N minutos, no en bucle cerrado. - Reutiliza una sola campaña activa (recuerda: máximo 1 campaña corriendo por organización).
- Una API Key por integración ayuda a que el consumo de cada sistema sea independiente y más fácil de monitorear.
Ver también: Errores y troubleshooting para el detalle del
429 dentro del catálogo de códigos.