Rate limiting
Dos capas protegen la API: un guard DDoS per-IP que va al frente de cada request, y un guard TPS per-organización/per-endpoint que aplica el contrato que estás pagando. Ambos exponen su estado en headers estándar: léelos y tu cliente se auto-regula.
El modelo de dos capas
| Capa | Alcance | Límite | Código al exceder |
|---|---|---|---|
| Guard DDoS | Per IP de origen | 35 000 req/min | RATE_DDOS_EXCEEDED |
| Guard TPS | Per organización × per endpoint | Específico al endpoint (ej. ping es 20 req/min) | RATE_TPS_EXCEEDED |
El guard DDoS corre primero: es un piso de infraestructura, no un SLA del cliente. Llegar ahí suele indicar una IP de agregador mal configurada o un patrón claro de abuso; el threshold es generoso, el tráfico legítimo no se acerca. El guard TPS es el que verás en operación normal: cada endpoint declara su cupo en la referencia.
Ambos guards cuentan los requests a través de todos los endpoints de la misma organización para la capa IP, y a través de un solo endpoint para la capa TPS. Rotar endpoints no multiplica el presupuesto IP; rotar IPs no multiplica el presupuesto TPS.
Ventana deslizante, no buckets fijos
Ambos guards usan una ventana deslizante de 2 buckets con peso (la técnica que usan Cloudflare y Stripe). La intuición:
- El bucket actual de 60 segundos organización a peso completo.
- El bucket previo de 60 segundos organización con un peso que decrece linealmente de 1.0 (inicio del bucket actual) a 0.0 (fin del bucket actual).
- Organización efectiva =
current + previous × weight.
Con un bucket fijo, un cliente podría mandar limit requests en el segundo 59 y limit más en el segundo 0 del próximo bucket, 2× el límite en una ventana de 2 segundos. La ventana deslizante atrapa eso.
Headers de respuesta
v6 sigue RFC 9598 (RateLimit Header Fields for HTTP). Cada respuesta protegida por TPS carga:
RateLimit-Limit: 20
RateLimit-Remaining: 18
RateLimit-Reset: 31
RateLimit-Policy: 20;w=60;name="endpoint"
| Header | Significado |
|---|---|
RateLimit-Limit | Cupo para la ventana activa. |
RateLimit-Remaining | Requests restantes en la ventana activa. |
RateLimit-Reset | Segundos hasta que la ventana activa se reinicia. |
RateLimit-Policy | Cupo declarativo en formato parseable. 20;w=60 = 20 requests en una ventana deslizante de 60 segundos. |
En un 429 también obtienes Retry-After en segundos: espera al menos ese tiempo antes de reintentar.
Estrategia recomendada para el cliente
Tres reglas cubren la mayoría de casos:
-
Throttle preventivo.
Lee
RateLimit-Remainingen cada respuesta. Cuando baje del 20% deRateLimit-Limit, reduce tu rate para llegar al próximo reset con margen. -
Respeta
Retry-After. Cuando recibes un 429, duerme al menosRetry-Aftersegundos antes del próximo intento. No reintentes de inmediato: solo vas a re-disparar el contador. -
Backoff exponencial con jitter.
Si los reintentos siguen fallando, suma un delay exponencial (ej.
2^n × 100ms) con jitter aleatorio (ej. ±50%). El jitter evita que miles de clientes se sincronicen en el mismo segundo de reintento.
Implementación de referencia (Python)
import time, random, httpx
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"))
# backoff exponencial cap a Retry-After * 4, más jitter
delay = min(wait * (2 ** attempt), wait * 4)
delay += random.uniform(0, delay * 0.5)
time.sleep(delay)
return r # quien llama decide qué hacer al agotar
SDKs auto-throttlean
Los SDKs oficiales leen RateLimit-Policy en runtime y pacean los requests internamente para que el código de aplicación no tenga que hacerlo. Si estás escribiendo tu propio cliente, parsear la policy es directo:
// RateLimit-Policy: 20;w=60;name="endpoint"
// → límite 20, ventana 60s, nombre "endpoint"
Lo que ves cuando dispara un guard
Ambos guards devuelven HTTP 429 con la envoltura estándar. El error.code te dice cuál:
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 TPS per-endpoint (imports masivos, campañas de marketing, onboarding por lotes), habla con tu account manager. Podemos subir el límite per-organización para un endpoint específico sin tocar el piso DDoS. Trae:
- QPS de pico esperado y volumen diario total.
- Si el pico es one-off (un lanzamiento) o sostenido.
- Cómo haces backoff cuando te limitan (así sabemos que el aumento no solo desplaza el problema).