Límites de uso y 429 Too Many Requests
Dos capas protegen la API: una protección por IP de origen que va al frente de cada petición, y un cupo por organización y por endpoint. Ambas exponen su estado en headers estándar: léelos y tu cliente se autorregula.
6 min de lecturaRevisado el
El modelo de dos capas
| Capa | Alcance | Límite | Código al exceder |
|---|---|---|---|
| Protección por IP | Por IP de origen (IPv6: por bloque /64) | 35 000 solicitudes por minuto | RATE_DDOS_EXCEEDED |
| Cupo por endpoint | Por organización y por endpoint | Específico de cada endpoint (ping admite 20 por minuto) | RATE_TPS_EXCEEDED |
La protección por IP corre primero: es un piso de seguridad, no un compromiso comercial. Llegar ahí suele indicar una IP compartida mal configurada o un patrón de abuso claro; el umbral es generoso y el tráfico legítimo no se acerca. El cupo por endpoint es el que verás en operación normal, y cada endpoint declara el suyo en la referencia.
Las dos capas cuentan distinto: la capa de IP suma todas las peticiones que salen de una misma IP, sin importar el endpoint; la capa de cupo suma las de un solo endpoint. Rotar endpoints no multiplica el presupuesto por IP, y rotar IPs no multiplica el cupo por endpoint.
IPv6: se cuenta el bloque /64
Con IPv6, la protección por IP no cuenta la dirección exacta sino su bloque /64. A cada conexión IPv6 se le asigna un /64 entero, así que quien la usa puede estrenar dirección en cada petición sin cambiar de red: contar la dirección exacta no pondría ningún tope. Todas las direcciones de un mismo /64 comparten los 35 000 por minuto. Las IPv4 se cuentan una por una, como siempre.
El corte por claves que no existen
La protección por IP tiene una segunda regla, para quien prueba claves al azar. Si desde una misma IP (o un mismo /64) llegan 60 API keys distintas que no autentican en un minuto (que no existen, o que fueron revocadas o vencieron), esa IP queda cortada 15 minutos: toda petición recibe 429 RATE_DDOS_EXCEEDED con un Retry-After que dice cuánto le queda al corte, aunque la clave que envíes sea válida.
- Cuentan las claves distintas, no los intentos: una integración que sigue mandando la misma clave eliminada recibe
401 AUTH_INVALID_KEYcada vez, pero no llega al corte. - Solo cuentan las claves que no autentican. Una clave válida sin permiso para el servicio, de una organización suspendida o usada desde una red que no permite no suma: existe, y no se fabrica al azar.
- Si tu integración reparte el tráfico entre varias claves, revisa que ninguna esté mal copiada o eliminada: detrás de una misma salida a internet, todas cuentan juntas.
Ventana deslizante, no bloques fijos
Las dos capas usan una ventana deslizante de dos bloques con peso, la misma técnica que usan Cloudflare y Stripe. La intuición:
- El bloque actual de 60 segundos cuenta a peso completo.
- El bloque anterior cuenta con un peso que decrece de 1.0 (inicio del bloque actual) a 0.0 (final del bloque actual).
- El total efectivo es
actual + anterior × peso.
Con bloques fijos, un cliente podría mandar el límite completo en el segundo 59 y otro tanto en el segundo 0 del bloque siguiente: el doble del límite en dos segundos. La ventana deslizante atrapa eso.
Headers de respuesta
Las cabeceras RateLimit-* siguen la propuesta pública del IETF sobre cabeceras de límite de uso. Cada respuesta de un endpoint con límite propio lleva:
RateLimit-Limit: 20
RateLimit-Remaining: 18
RateLimit-Reset: 31
RateLimit-Policy: 20;w=60;name="endpoint"RateLimit-Limit- Cupo de la ventana activa.
RateLimit-Remaining- Solicitudes restantes en la ventana activa.
RateLimit-Reset- Segundos hasta que la ventana activa se reinicia.
RateLimit-Policy- Cupo declarativo, en formato interpretable.
20;w=significa 20 solicitudes en una ventana deslizante de 60 segundos.60
En un 429 además recibes Retry-After en segundos: espera al menos ese tiempo antes de reintentar.
Estrategia recomendada
- 01Regula de forma preventiva
Lee
RateLimit-Remainingen cada respuesta. Cuando baje del 20 % deRateLimit-Limit, reduce tu ritmo para llegar al próximo reinicio con margen. - 02Respeta Retry-After
Cuando recibes un 429, espera al menos los segundos que indica antes del próximo intento. Reintentar de inmediato solo vuelve a disparar el contador.
- 03Retroceso exponencial con variación
Si los reintentos siguen fallando, suma un retraso exponencial (por ejemplo
2^n × 100 ms) con una variación aleatoria de ±50 %. La variación evita que miles de clientes se sincronicen en el mismo segundo.
import time, random
def call_with_backoff(client, request, max_retries=5):
for attempt in range(max_retries):
r = client.send(request)
if r.status_code != 429:
return r
wait = int(r.headers.get("Retry-After", "1"))
# retroceso exponencial con techo en Retry-After * 4, mas variacion
delay = min(wait * (2 ** attempt), wait * 4)
delay += random.uniform(0, delay * 0.5)
time.sleep(delay)
return r # quien llama decide que hacer al agotar los intentosasync function callWithBackoff(request, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(request)
if (res.status !== 429) return res
const wait = Number(res.headers.get('Retry-After') ?? 1)
let delay = Math.min(wait * 2 ** attempt, wait * 4)
delay += Math.random() * delay * 0.5
await new Promise((r) => setTimeout(r, delay * 1000))
}
throw new Error('rate limit: retries exhausted')
}Si escribes tu propio cliente, interpretar la política es directo:
// RateLimit-Policy: 20;w=60;name="endpoint"
// -> limite 20, ventana 60 s, nombre "endpoint"
const [limit, ...params] = policy.split(';')
const window = Number(params.find((p) => p.startsWith('w='))?.slice(2))Lo que ves cuando se dispara
Las dos capas devuelven HTTP 429 con la envoltura estándar. El error. te dice cuál se disparó:
RateLimit-Limit: 20
RateLimit-Remaining: 0
RateLimit-Reset: 42
Retry-After: 42
{
"success": false,
"error": {
"code": "RATE_TPS_EXCEEDED",
"type": "https://developers.hablame.co/docs/v6/errors/rate-tps-exceeded",
"message": "You have exceeded the allowed request rate for this endpoint. Please wait before retrying.",
"details": []
},
"meta": { "requestId": "c7d41a09e2b8f356" }
}Si necesitas más
Si tu patrón de tráfico supera de forma legítima el límite de un endpoint (cargas masivas, campañas, altas por lotes), cuéntanos tu caso en comercial@hablame.co. Ten a mano:
- El endpoint y el pico esperado, junto con el volumen diario total.
- Si el pico es puntual (un lanzamiento) o sostenido.
- Cómo reintentas cuando te limitan, para saber que más cupo no solo desplaza el problema.