Límites de peticiones
Los presupuestos por clase de endpoint (read, write, quote), los encabezados X-RateLimit-* y cómo manejar un 429 con reintentos correctos.
Cada petición se clasifica en una de tres clases de endpoint y se cuenta contra el presupuesto por minuto de esa clase. Las clases son independientes: cotizar mucho nunca te deja sin capacidad para comprar guías, ni al revés.
| Clase | Qué entra |
|---|---|
read |
GET, HEAD y OPTIONS |
write |
POST, PUT, PATCH y DELETE que no sean cotización |
quote |
Cotización: POST /v1/rates, POST /v1/rates/carrier/:carrierCode y GET /v1/shipments/:id/rates |
Cómo se agrupan tus peticiones
La ventana es de 60 segundos y se comparte entre todos los servidores de la API.
| Tráfico | Se mide por | Presupuesto por minuto |
|---|---|---|
Llave de producción (sk_live_) |
Organización, por clase | El de tu plan (ver abajo) |
Llave de prueba (sk_test_) |
Organización, por clase, en un cubo aislado | 25% del plan, con piso de 10/min |
| Sesión del dashboard | Sesión, por clase | read 300 · write 100 · quote 30 |
| Sin autenticar | IP, por clase | read 60 · write 30 · quote 10 |
Todas las llaves de una organización comparten el mismo presupuesto. El tráfico de prueba tiene sus propios cubos: lo que consumas en el sandbox nunca le quita capacidad a producción, ni al revés.
Presupuestos por plan
Para llaves de producción, peticiones por minuto:
| Plan | read |
write |
quote |
|---|---|---|---|
| Free | 60 | 30 | 20 |
| Growth | 300 | 150 | 60 |
| Scale | 1,000 | 500 | 200 |
| Enterprise | 5,000 | 2,500 | 1,000 |
Las llaves sk_test_ reciben el 25% de cada número, con un piso de 10/min. Por ejemplo: quote de prueba en Free es 10, y read de prueba en Scale es 250.
Lee los encabezados
Toda respuesta limitada incluye estos encabezados, sin sufijo:
| Encabezado | Descripción |
|---|---|
X-RateLimit-Limit |
Presupuesto de la clase en la que cayó esta petición |
X-RateLimit-Remaining |
Peticiones restantes en ese cubo este minuto |
X-RateLimit-Reset |
Momento en que el cubo se reinicia, en segundos epoch Unix |
Retry-After |
Segundos para poder reintentar (solo en 429) |
Los encabezados describen la clase de la petición que los devolvió: un POST y un GET enviados en el mismo segundo pueden reportar límites distintos, y eso es correcto.
Maneja un 429
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"details": { "retryAfter": 30 }
}
}
Al pasarte, las peticiones se rechazan hasta que la ventana de 60 segundos cierre; no hay castigo adicional ni bloqueo extendido. Respeta Retry-After y aplica backoff exponencial:
async function withRetry(fn, maxAttempts = 5) {
for (let attempt = 1; ; attempt++) {
const res = await fn();
if (res.status !== 429 || attempt === maxAttempts) return res;
const retryAfter = Number(res.headers.get("Retry-After") ?? 1);
const backoff = retryAfter * 1000 * 2 ** (attempt - 1);
await new Promise((r) => setTimeout(r, backoff));
}
}