Límites de uso
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 lectura
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 API keys que no existen
La protección por IP tiene una segunda regla, para quien prueba keys 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 key que envíes sea válida.
- Cuentan las keys distintas, no los intentos: una integración que sigue mandando la misma key revocada recibe
401 AUTH_INVALID_KEYcada vez, pero no llega al corte. - Solo cuentan las keys que no autentican. Una key 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 keys, revisa que ninguna esté mal copiada o revocada: 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
La API sigue el RFC 9598 (RateLimit Header Fields for HTTP). Cada respuesta protegida por cupo 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=60significa 20 solicitudes en una ventana deslizante de 60 segundos.
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 intentosSi 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.code 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",
"message": "You have exceeded the allowed request rate for this endpoint."
}
}Pedir un límite más alto
Si tu patrón de tráfico excede legítimamente el cupo de un endpoint (cargas masivas, campañas, altas por lotes), habla con tu asesor comercial. Podemos subir el límite de tu organización para un endpoint específico sin tocar el piso de protección por IP. Ten a mano:
- Pico esperado y volumen diario total.
- Si el pico es puntual (un lanzamiento) o sostenido.
- Cómo haces el retroceso cuando te limitan, para saber que el aumento no solo desplaza el problema.