Rate Limits
Every rate limit the Starship API enforces — the rules, what each is keyed on, and how to read the headers
Rate Limits
This page lists the default rate limits the Starship API enforces on client-API and authentication endpoints.
How rules are matched
Rules are evaluated in the order listed below, matching the request path by prefix. The first matching rule wins — a request to POST /api/v1/orders is governed by the orders rule and never also by the general rule.
Each rule is counted against a key, which determines whose budget the request spends:
| Key | Meaning |
|---|---|
| API key | The X-API-Key on the request. All requests using the same key share one budget, regardless of source IP. |
| Client | Your client account. |
| IP | The source IP of the request. |
On the client API, limits apply per API client: no other client's traffic spends your budget. The numbers below are the defaults. Configurable per-client limits are available on request — see Need a higher limit?.
Client API (/api/v1/*)
| Rule | Applies to | Default limit | Window | Keyed on |
|---|---|---|---|---|
| Order creation | POST /api/v1/orders | 100 requests | 1 minute | API key |
| Product images | GET /api/v1/products/imagesGET /api/v1/products/{id}/image | 100 requests | 1 minute | Client |
| General | Every other /api/v1/* endpoint | 1,000 requests | 1 hour | API key |
The general rule is the one that will bite you. Product listing, product details, charges, order lookups, wallet reads, transaction history, payouts, beneficiaries and webhook management all draw on the same 1,000-per-hour budget. There is no separate per-endpoint allowance for any of them.
How the budget refills (bursts are allowed)
Limits are enforced with GCRA (the generic cell rate algorithm), a smooth token-bucket style limiter, not a fixed window that resets on the hour:
- Bursts are allowed. A full budget lets you send up to the rule's whole limit back to back, e.g. 1,000 general requests in quick succession.
- The budget refills continuously, at
limit ÷ window: one request every 3.6 seconds for the general rule (1,000 per hour), one every 0.6 seconds for order creation (100 per minute). - Once the budget is empty, you wait for the next request's worth of refill, not for a whole window. That is why
Retry-Afteron these rules is a few seconds, not tens of minutes.
Sustained throughput can never exceed the limit per window; bursting only spends refill you have not used yet.
Designing around the general limit
The general limit rewards bulk reads over chatty ones:
- Import the catalog in pages, not per product.
GET /api/v1/productsacceptslimitup to 10,000 and returns each product's denominations and your resolved discount in the same payload. A full catalog import costs a handful of requests, not one per product. - Do not call the Charges API per product at import time. It is a quote endpoint, not a prerequisite for ordering — see Pricing and Charges.
POST /api/v1/ordersre-prices server-side at creation and debits the authoritative amount. - Back off when polling order status. 10 seconds is the floor, 60 seconds is preferred for
DELAYEDorders. See Delivery Semantics. - Cache reference data. Countries and currencies are served with a 1-day
Cache-Controlmax-age, categories and subcategories with 5 minutes.
Authentication endpoints
These are keyed on IP, not on your API key. The login rule applies a block period after the limit is hit — during a block, every login request from that IP is rejected until the block expires, and Retry-After reports the block duration.
| Rule | Applies to | Limit | Window | Block after breach |
|---|---|---|---|---|
| Login | POST /auth/login | 5 requests | 1 minute | 15 minutes |
| Token refresh | POST /auth/refresh | 20 requests | 1 minute | — |
Automated test suites trip the login limit constantly. Five failed or successful logins from one IP inside a minute earns that IP a 15-minute block. Authenticate once and reuse the token across your suite rather than logging in per test case.
Additional limits apply to portal-only flows (password reset, 2FA enrolment, credential management). They are keyed on the signed-in user and are not reachable from the client API.
Reading the headers
Responses on a rate-limited path carry your current standing against the matched rule:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1640995200| Header | Meaning |
|---|---|
X-RateLimit-Limit | The matched rule's limit |
X-RateLimit-Remaining | Requests you can still send right now as a burst |
X-RateLimit-Reset | Unix timestamp (seconds) at which your budget is fully refilled if you send nothing more. It is not a hard window boundary: capacity comes back gradually before then. |
When you are throttled
A breached limit returns 429 Too Many Requests:
HTTP/1.1 429 Too Many Requests
Retry-After: 4
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640998800
Content-Type: application/json{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 1 minutes or contact support to increase your limit.",
"retry_after": 4
}This is a typical response from the general rule once its budget is empty: the wait is the time until the next request's worth of refill (up to 3.6 seconds, rounded up). On order creation it is usually 1 second. On the login rule, which blocks, the first rejection reports the full 15-minute block (900) and later ones the time remaining on it.
Branch on the error field, which is the literal string rate_limit_exceeded. The message text is human-readable, varies by rule, and is stated in whole minutes with a minimum of 1, so it overstates short waits — do not parse it. Use retry_after, which mirrors the Retry-After header and is expressed in seconds.
Honour Retry-After. It is expressed in seconds and tells you when the next request will be accepted; a request sent sooner is rejected again. Wrap retries in exponential backoff with jitter so a fleet of workers doesn't resynchronise onto the same reset boundary.
Need a higher limit?
If your traffic profile genuinely needs more headroom, contact your Starship representative with your expected request volume, its shape (steady versus bursty), and which endpoints dominate it. Configurable per-client limits are available on request.