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

CapaAlcanceLímiteCó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.

i

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"
HeaderSignificado
RateLimit-LimitCupo para la ventana activa.
RateLimit-RemainingRequests restantes en la ventana activa.
RateLimit-ResetSegundos hasta que la ventana activa se reinicia.
RateLimit-PolicyCupo 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:

  1. Throttle preventivo. Lee RateLimit-Remaining en cada respuesta. Cuando baje del 20% de RateLimit-Limit, reduce tu rate para llegar al próximo reset con margen.
  2. Respeta Retry-After. Cuando recibes un 429, duerme al menos Retry-After segundos antes del próximo intento. No reintentes de inmediato: solo vas a re-disparar el contador.
  3. 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:

429 Too Many Requests
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).