Rate limits and abuse protection
Limits
Section titled “Limits”| Scope | Limit |
|---|---|
| All requests with one key | 120 per minute |
POST /shipments/quote and POST /addresses/verify |
30 per minute per key, and 2,000 per day per organization |
POST /shipments/{id}/buy |
30 per minute per key |
| Request body size | 256 KB |
When you go over a limit the response is 429 with the code rate_limited, and a Retry-After header with the number of seconds to wait.
Every response carries three headers so you can slow down before you hit a limit:
| Header | Meaning |
|---|---|
RateLimit-Limit |
The limit that applies to this request. |
RateLimit-Remaining |
How many requests are left in the current window. |
RateLimit-Reset |
Seconds until the window resets. |
Handle a 429
Section titled “Handle a 429”async function withRetry(makeRequest, attempts = 5) { for (let i = 0; i < attempts; i++) { const res = await makeRequest(); if (res.status !== 429 && res.status < 500) return res; const wait = Number(res.headers.get("Retry-After")) || 2 ** i; await new Promise((r) => setTimeout(r, (wait + Math.random()) * 1000)); } throw new Error("Giving up after repeated rate limits or server errors");}import randomimport time
def with_retry(make_request, attempts=5): for i in range(attempts): res = make_request() if res.status_code != 429 and res.status_code < 500: return res wait = int(res.headers.get("Retry-After", 2 ** i)) time.sleep(wait + random.random()) raise RuntimeError("Giving up after repeated rate limits or server errors")Retrying a purchase is safe only with the same Idempotency-Key on every attempt.
Abuse protection
Section titled “Abuse protection”- Bad keys are blocked. Repeated requests with invalid keys from one IP address are blocked for 15 minutes.
- Keys are stored hashed. Only a hash of the secret is kept, and the key is shown once when you create it.
- Keys can expire. Set an expiry date when you create a key. An expired key stops working.
- Keys can be limited to addresses. An IP allowlist, with exact addresses or IPv4 ranges, rejects requests from anywhere else.
- Spending is capped. A per label and a per day limit on the key, plus your account's own limit, bound what a leaked key can spend.
- Permissions are narrow. A key only does what you ticked, and the MCP server only lists the tools the key may use.
- No browser access. The API sends no CORS headers. Call it from a server and never put a key in front end code.
- HTTPS only. In production the API is served over HTTPS only. Never send a key over plain HTTP.
© 2026 Ship Jay. All rights reserved. Created & Developed by WCWS Internet
