Suavizar tus llamadas con el rate limiting
La API pública de CaptainDNS aplica dos capas de rate limiting: una por IP antes de la autenticación y una por clave con token bucket cuya capacidad depende del plan. Esta guía explica ambas capas y cómo escribir un cliente que las respete sin perder peticiones.
Dos capas de protección
1. Límite por IP pre-auth
Antes de leer la cabecera Authorization, un limitador en memoria restringe el tráfico por IP origen. El valor por defecto son 120 peticiones por minuto y por IP, ajustable del lado CaptainDNS. Esta capa protege de ataques volumétricos y credential stuffing.
Si se excede, la respuesta es 429 RATE_LIMITED con Retry-After: 60. No se verifica ninguna clave ni se consume ningún crédito.
2. Token bucket por clave
Una vez autenticada la clave, un token bucket persistente en Postgres gestiona el límite por clave. La capacidad depende del plan:
| Plan | Capacidad (tokens) | Recarga |
|---|---|---|
| Free | 10 | 10 tokens/min |
| Solo | 10 | 10 tokens/min |
| Starter | 60 | 60 tokens/min |
| Pro | 500 | 500 tokens/min |
| Business | 1 000 | 1 000 tokens/min |
| Enterprise | 1 200 | 1 200 tokens/min |
Cada petición autenticada consume un token. El bucket se recarga continuamente, por lo que no hay que esperar un tick completo de minuto para reintentar.
Un bucket vacío devuelve 429 RATE_LIMITED. La cabecera Retry-After contiene los segundos hasta el próximo token.
Cabeceras de respuesta
Cada respuesta pública incluye estas cabeceras:
RateLimit-Policy: "default";q=60;w=60
X-RateLimit-Limit: 60
RateLimit-Policy: formato IETF draft, describe la política.qes la capacidad del bucket,wla ventana en segundos.X-RateLimit-Limit: capacidad del bucket, idéntica aq.
X-RateLimit-Remaining no se emite en cada petición: se fija en 0 solamente ante un rechazo 429 (la función PL/pgSQL que consume un token no devuelve el número de tokens restantes, y una lectura adicional tendría un coste de latencia injustificado). Dimensiona tu cliente basándote en la capacidad, la cabecera Retry-After y tus propias mediciones del lado cliente, no en un contador servidor en tiempo real.
Ante un 429, tendrás además:
Retry-After: 12
X-RateLimit-Remaining: 0
El cliente debe respetar Retry-After y esperar al menos los segundos indicados antes de reintentar.
Estrategia de retry recomendada
El patrón idiomático para un cliente respetuoso es el backoff exponencial acotado:
- Cuenta los tokens del lado cliente a partir de
X-RateLimit-Limit. Mantén tu propio bucket local para anticipar la saturación antes de enviar la petición. - Ante
429 RATE_LIMITED, leeRetry-Aftery espera al menos ese tiempo. - Si acumulas varios 429 seguidos, aplica un multiplicador: 2x, 4x, 8x con un techo de 60 segundos.
- Añade un jitter aleatorio de +/- 20 % para evitar que varios clientes reintenten a la vez.
- Limita el número total de reintentos (por ejemplo 5). Más allá, registra el fallo como error y súbelo a tu observabilidad.
Ejemplo de pseudocódigo:
delay = retryAfter + random(-0.2, 0.2) * retryAfter
for attempt in 1..5:
response = send(request)
if response.status != 429:
return response
retryAfter = response.header["Retry-After"] or delay * attempt
sleep(min(retryAfter, 60))
raise RateLimitExceeded
Estrategias para pipelines por lotes
Las integraciones que procesan grandes volúmenes en batch (por ejemplo, escanear 10 000 dominios cada noche) se benefician de estrategias adicionales:
- Token bucket en el cliente: replica localmente el bucket del servidor y envía solo si hay un token disponible. Así evitas los 429 y el jitter que arrastran.
- Paralelizar según la capacidad: un plan Pro permite 500 tokens/min, unos 8 req/s. Ejecutar 8 workers en paralelo es más eficiente que un único worker que hace 500 iteraciones por minuto con pausas.
- Separación de endpoints caros: si lanzas 500
page-crawl-check(10 créditos) y 500dmarc/lookup(1 crédito), repártelos en dos colas distintas. Suavizas el consumo de créditos y respetas el límite con más holgura. - Ventanas de mantenimiento: algunos batchs pueden correr fuera de horas punta, para no disputarle la envolvente al tráfico en tiempo real.
Qué cuenta como petición
Cada petición HTTP autenticada consume un token, incluso si falla con 400 o 500. El límite protege el backend; la validación de los datos de entrada llega después de obtener el token, no antes.
Excepciones:
429 RATE_LIMITED: no consume token, claro está, porque el bucket ya está vacío.401 INVALID_API_KEY: no toca el bucket por clave, porque todavía no se sabe de qué clave se trata. El límite por IP, en cambio, sí cuenta.403 IP_NOT_ALLOWED: no consume ni token ni crédito, porque la comprobación se hace antes del consumo.
Aumentar tu capacidad
La capacidad del token bucket es propiedad del plan, no un parámetro de la clave. Para aumentarla:
- Pasa al plan superior desde
/account/billing. - El efecto es inmediato: la clave hereda el nuevo techo en la siguiente petición.
Los clientes Enterprise pueden negociar un techo personalizado, hasta 5 000 req/min previo presupuesto, con su account manager de CaptainDNS.
Diagnosticar un 429
Un 429 inesperado puede tener varias causas:
- Bucket pre-auth: superas 120 req/min en una sola IP. Verifica que varios servicios no compartan el mismo egress (NAT, CGNAT).
- Token bucket por clave: tu plan no ofrece la capacidad necesaria. Sube de plan o reduce la concurrencia.
- Pico puntual: un batch sin suavizado envió 200 peticiones en un segundo. Añade jitter y suavizado en el cliente.
- Desfase de reloj: en los raros casos de un servidor desincronizado con NTP, el
Retry-Afterpuede parecer excesivo. Sincroniza el reloj.
Si el problema persiste aun respetando Retry-After al pie de la letra, abre un ticket de soporte con el X-Request-Id de una petición 429 representativa.
Herramientas CaptainDNS relacionadas
- El DNS lookup permite verificar manualmente un registro sin consumir créditos.
- El DMARC monitoring agrega los informes DMARC sin pasar por la API pública.
Siguiente paso: la idempotencia para ahorrar créditos en reintentos, o los códigos de error.