Límites de uso de la API
Por clave, por organización y por endpoint, en ventana deslizante de un minuto. Nunca solo por IP: una integración viene de pocas IPs y muchas peticiones.
| Cubo | Peticiones / minuto | Para qué |
|---|---|---|
| Por clave | 600 | El tope de una integración. |
| Por organización | 1200 | Todas las claves de la empresa juntas. |
| Por clave y endpoint | 300 | Una lista pesada no puede vaciar el cubo de la clave. |
| Fallos de autenticación por IP | 60 | Antes de autenticar. Adivinar claves cuesta. |
| Pruebas por destino | 5 | POST /webhooks/{id}/test: cada prueba es un POST firmado a un servidor de verdad. Cuenta por host, no por webhook: dos webhooks al mismo servidor comparten el cubo. |
| Pruebas por empresa y hora | 60 | El tope de todas tus pruebas de webhook juntas. Este cubo es POR HORA, no por minuto. |
Cabeceras
| Cabecera | Cuándo | Qué es |
|---|---|---|
X-RateLimit-Limit | Toda respuesta con clave válida, incluido un 403 por scope | El tope por clave (600). |
X-RateLimit-Remaining | Toda respuesta con clave válida, incluido un 403 por scope | Las que quedan en este minuto: la menor de los tres (clave, organización y endpoint). Nunca promete más de lo que hay en el cubo más apretado. |
Retry-After | Solo en 429 (que no lleva las dos de arriba) | Segundos a esperar. También va en error.details.retry_after. |
json
{
"error": {
"code": "rate_limited",
"message": "Demasiadas peticiones. Espera y vuelve a intentarlo.",
"request_id": "req_9f8e7d6c5b4a39281706",
"details": {
"retry_after": 12
}
}
}Qué hacer con un 429
javascript
async function conReintentos(url, init, intentos = 5) {
for (let i = 0; i < intentos; i++) {
const res = await fetch(url, init);
if (res.status !== 429 && res.status < 500) return res;
const retry = Number(res.headers.get('retry-after') ?? 0);
const espera = Math.max(retry * 1000, 500 * 2 ** i) + Math.random() * 250;
await new Promise((r) => setTimeout(r, espera));
}
throw new Error('agotados los reintentos');
}- Respeta
Retry-After: es el tiempo real que falta para que el cubo se libere. - Para sincronizaciones grandes usa
limit=100y los filtros de fecha; para el día a día, webhooks en vez de sondeo. - Un 429 por autenticación fallida no depende de la clave sino de tu IP: revisa la clave antes de reintentar.
Tamaño del cuerpo
Los cuerpos JSON tienen un tope de 256 KB medidos en bytes UTF-8, no en caracteres (413 payload_too_large), y deben ir con Content-Type: application/json (415 si no, también si la cabecera falta). Una petición rechazada por scope (403) cuenta contra el cubo igual que una atendida.