Límites de peticiones y reintentos: buenas prácticas al llamar a una API de moderación
Backoff exponencial, jitter, circuit breakers y qué hacer cuando la API no está disponible un momento. Para que tu aplicación aguante.
Llamar a una API de moderación en producción es como llamar a cualquier otro servicio de terceros en producción: a veces falla. La cuestión es si tu aplicación sabe degradarse sin romperse, machaca al proveedor hasta empeorar la caída o descarta peticiones de usuarios en silencio. Este artículo es una lista de comprobación para escribir un cliente que se comporte como debe.
1. Respeta el límite antes de chocar con él
Toda API de moderación limita las peticiones. La mayoría indica en las cabeceras de cada respuesta cuánto margen te queda:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1716480000
Un buen cliente las lee en cada respuesta y aplica throttling en el lado del cliente cuando Remaining baja de un umbral de seguridad. La alternativa ingenua, lanzar peticiones hasta recibir un 429, es una forma estupenda de hacerte un DoS a ti mismo.
2. Reintenta solo los errores que toca
Una clasificación rápida de las respuestas HTTP:
| Código | ¿Reintentar? | Por qué |
|---|---|---|
| 2xx | No | Éxito |
| 400 | No | Enviaste una petición mal formada. Reintentar no ayuda. |
| 401, 403 | No | Problema de autenticación. Avisa a quien esté de guardia, no reintentes. |
| 402 | No | Sin créditos. La solución es un plan mayor o el mes que viene, no dentro de un minuto. |
| 404 | No | Normalmente un error de URL o de configuración. |
| 408, 425, 429 | Sí, con backoff | Transitorio. |
| 500, 502, 503, 504 | Sí, con backoff | Del lado del servidor, probablemente transitorio. |
Lee la cabecera Retry-After si viene: te indica cuándo cree el servidor que debes volver.
3. Backoff exponencial con jitter
Los reintentos lineales provocan estampidas después de una caída: todo el mundo reintenta a los mismos intervalos y vuelve a tumbar el servicio en cuanto se recupera. Usa backoff exponencial con jitter aleatorio:
function backoffMs(attempt: number): number {
const base = 200; // 200ms
const cap = 10_000; // cap at 10s
const exp = Math.min(cap, base * 2 ** attempt);
return Math.random() * exp; // "full jitter"
}
// attempt 0 -> 0 to 200ms
// attempt 1 -> 0 to 400ms
// attempt 2 -> 0 to 800ms
// attempt 3 -> 0 to 1.6s
// attempt 4 -> 0 to 3.2s
El "full jitter" (aleatorio entre 0 y el exponencial con tope) reparte mejor los reintentos que el "equal jitter" o que no usar jitter. Limita los intentos a entre tres y cinco salvo que tengas una buena razón para seguir.
4. Claves de idempotencia, para ir sobre seguro
Que un reintento envíe dos veces la misma comprobación no es problema, porque el resultado es el mismo. Pero si la moderación forma parte de una operación de escritura más amplia (guardar un comentario, banear a un usuario), no te interesa que los efectos secundarios se ejecuten dos veces. Usa claves de idempotencia:
POST /api/v1/text
Idempotency-Key: 7f3c9e2a-1b8d-4e6f-9a1b-2c8d7e3f4a5b
El servidor devuelve la misma respuesta para la misma clave durante un periodo (normalmente 24 horas). Genera la clave una vez por acción del usuario, no por intento HTTP.
5. Circuit breakers para caídas prolongadas
Si la API está caída, reintentar cada llamada añade carga sin aportar nada. Un circuit breaker detecta un fallo sostenido (por ejemplo, >50% de errores en 30 segundos) y se salta la API por completo durante un periodo de enfriamiento. Mientras el breaker está abierto:
- Pasas a un comportamiento por defecto más seguro, normalmente "dejar pasar el contenido pero marcarlo como no comprobado".
- Después, revisas a mano una parte mayor de ese tráfico.
- Avisas a quien esté de guardia.
Bibliotecas como resilience4j (JVM) u opossum (Node) lo hacen por ti; en Laravel son unas pocas líneas sobre la caché.
6. Timeouts, más ajustados de lo que crees
Una API de moderación que normalmente responde en 50 ms no debería tener un timeout de 30 segundos. Pon el timeout del cliente en dos o tres veces tu p99, no en "lo que traiga por defecto la biblioteca HTTP". Un timeout mal configurado es la razón más común por la que una dependencia lenta acaba en una caída en cascada.
import ToxicFilter from 'toxicfilter-sdk';
const tf = new ToxicFilter(process.env.TOXICFILTER_KEY, {
timeout: 3_000, // milliseconds, not 30_000
retries: 2, // 429 and 5xx only, with a growing wait; never a 402
});
Ajústalo a la llamada más lenta que hagas. Una comprobación que resuelven los detectores gratuitos vuelve en torno a un milisegundo más la red; una que lee el modelo tarda más, así que un timeout pensado para la primera cortará la segunda.
7. Decide el plan B antes del incidente
Cuando la API de moderación no responde y tu circuit breaker se abre, ¿qué hace tu aplicación?
- Fail-open: dejar pasar el contenido sin moderar. Tu aplicación sigue en pie. Riesgo: los atacantes aprenden a atacar durante las caídas.
- Fail-closed: rechazar todo el contenido. Tu aplicación está a salvo. Riesgo: rompes tu propio producto con cualquier incidente del proveedor.
- Modo degradado: recurrir a un filtro local de palabras clave o a un clasificador ligero autoalojado. Término medio; más trabajo de ingeniería.
No hay una respuesta que valga para todos. Lo que importa es que la decisión se tome de antemano, se documente en un runbook y el comportamiento se pruebe con inyección de fallos. El peor momento para decidir tu política de respaldo es a las 3 de la mañana durante la caída de verdad.
Sigue leyendo
Moderar comentarios en Express con el SDK de JavaScript
Construye en Express un muro de comentarios que publica, retiene o rechaza cada comentario con su motivo, mant...
Moderar comentarios en Flask con el SDK de Python
Monta un muro de comentarios en Flask que publica, retiene o rechaza cada comentario con su motivo, verifica e...
Moderar comentarios en Laravel con Laratox
Una regla de validación, una facade y un fake: construye en Laravel un muro de comentarios que publica, retien...