Saltar al contenido
SendIt está en desarrollo y todavía no opera comercialmente · SendIt is in development and not yet commercially available.
SendItdocs
English
Esc
navigateopen⌘Jpreview
En esta página

Rate limits

Per-endpoint-class budgets (read, write, quote), the X-RateLimit-* headers, and how to handle a 429 with correct retries.

Every request is classified into one of three endpoint classes and counted against that class’s per-minute budget. The classes are independent: heavy quoting never starves your label purchases, and vice versa.

Class What falls in it
read GET, HEAD, and OPTIONS
write POST, PUT, PATCH, and DELETE that aren’t quotes
quote Rate quoting: POST /v1/rates, POST /v1/rates/carrier/:carrierCode, and GET /v1/shipments/:id/rates

How your requests are bucketed

The window is 60 seconds and is shared across every API server.

Traffic Metered by Per-minute budget
Live key (sk_live_) Organization, per class Your plan’s budgets (below)
Test key (sk_test_) Organization, per class, in an isolated bucket 25% of the plan, floor of 10/min
Dashboard session Session, per class read 300 · write 100 · quote 30
Unauthenticated IP, per class read 60 · write 30 · quote 10

All of an organization’s keys share the same budget. Test traffic gets its own buckets: what you spend in the sandbox never eats into production headroom, and vice versa.

Per-plan budgets

For live keys, requests per minute:

Plan read write quote
Free 60 30 20
Growth 300 150 60
Scale 1,000 500 200
Enterprise 5,000 2,500 1,000

sk_test_ keys get 25% of each number, with a floor of 10/min. For example: Free test quote is 10, and Scale test read is 250.

Read the headers

Every throttled response carries these headers, unsuffixed:

Header Description
X-RateLimit-Limit The budget for the class this request landed in
X-RateLimit-Remaining Requests remaining in that bucket this minute
X-RateLimit-Reset When the bucket resets, in Unix epoch seconds
Retry-After Seconds until you can retry (on 429 only)

The headers describe the class of the request that returned them: a POST and a GET sent in the same second can legitimately report different limits.

Handle a 429

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "details": { "retryAfter": 30 }
  }
}

Once you’re over, requests are rejected until the 60-second window rolls; there is no extra penalty or extended block. Honor Retry-After and apply exponential backoff:

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));
  }
}

¿Te ha resultado útil esta página?