Rate Limits
To keep the Shipwell API fast and reliable for every customer, the API limits how many requests a company can send per minute to each area of the API. Most integrations never come close to these limits. This page explains how they work, what your integration sees when it reaches one, and how to stay well within them.
How rate limits work
- Limits apply per API area. Each area of the API, such as Shipments or Rating, has its own allowance. Using one area never reduces what is available in another.
- Counted per minute. Each allowance starts fresh at the top of every clock minute (:00 seconds). If you reach a per-minute limit, you wait at most until the next minute starts. Companies on a plan also have an hourly limit, which starts fresh at the top of every hour.
- Shared across your company. The allowance belongs to your company, not to a user or token. All users, tokens and integrations of your company share it.
-
Sandbox and production are separate.
Traffic in
sandboxnever counts against yourproductionallowance. -
Authentication isn't counted.
Requests to authenticate, such as
/v2/auth/token/, do not count against any limit.
Plans
Every company starts on the standard limits, which differ by API area. Until your company is on a plan or has a raised limit, the X-RateLimit-Limit header on any response from an area shows its standard limit (see What your integration sees). A plan raises the limits on every API area at once, as a multiple of the standard limits:
| Plan | Per-minute limits | Hourly limits | Included API calls per month |
|---|---|---|---|
| Bronze | 2 × the standard limits | 30 × the per-minute limit | 2 million |
| Silver | 4 × the standard limits | 30 × the per-minute limit | 6 million |
| Gold | 6 × the standard limits | 30 × the per-minute limit | 20 million |
| Enterprise | Set per API area for your company | Set for your company | Agreed with your company |
- The monthly allowance never blocks a request. If your company goes past it, your Shipwell representative will talk with you about the plan that fits.
-
Your limits are always in the headers.
X-RateLimit-Limitshows your company's current limit for each API area, whatever your plan. - One busy area doesn't need a bigger plan. Shipwell can raise a single API area on any plan. See Need a higher limit?
To change plans, contact your Shipwell representative.
What your integration sees
Every response from a rate-limited area tells you where you stand:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Your company's limit for this API area |
X-RateLimit-Remaining |
Requests you have left before that limit |
X-RateLimit-Reset |
Seconds until the allowance starts fresh |
With both a per-minute and an hourly limit, the headers describe whichever one you're closer to.
When an area's limit is reached, further requests to that area receive 429 Too Many Requests until the allowance starts fresh. A 429 request was not processed, so it is always safe to send the same request again.
Example responses
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 842
X-RateLimit-Reset: 23HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 23
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 23
{"error": "rate_limit_exceeded"}What your team should do
-
Handle
429responses. Wait the number of seconds in theRetry-Afterheader, then send the same request again. Don't retry immediately in a loop: every retry before the minute resets gets another429. - Add a little randomness to retries. If several workers run in parallel, add a short random delay (for example 0–2 seconds) so they don't all retry at the same instant.
-
Pace bulk jobs.
For backfills, syncs and scheduled jobs, watch
X-RateLimit-Remainingand slow down as it approaches 0.
Example: retry after a 429
import random
import time
import requests
def send(method, url, **kwargs):
while True:
response = requests.request(method, url, **kwargs)
if response.status_code != 429:
return response
wait = int(response.headers.get("Retry-After", "1"))
time.sleep(wait + random.uniform(0, 2))async function send(url, options = {}) {
while (true) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const wait = Number(response.headers.get("Retry-After") ?? 1);
await new Promise((resolve) => setTimeout(resolve, (wait + Math.random() * 2) * 1000));
}
}File uploads
These examples send the same request again. For JSON bodies that just works. When uploading a file, open the file again (or rewind it) before each retry, otherwise the retry can send an empty file.
Need a higher limit?
If your integration legitimately needs more than its current limit, contact your Shipwell representative or support@shipwell.com. Tell us which API area, what volume you expect per minute, and what the traffic is for. Shipwell reviews the request and can raise the limit for your company. A raised limit applies to that API area only.