Rate limits
Each secret key may make a set number of requests a minute. Every response says how many are left and when the count resets.
Updated
Default limits
| Limit | Default | Counted |
|---|---|---|
| Requests per secret key | 600 a minute | Per key, in fixed one-minute windows |
| Scenes added through the API | 500 a day | Per workspace, across all its keys, per UTC day |
| Renders in one request | 100 | Per request |
| Renders running at once | 3 | Per workspace; the rest wait their turn |
- Every request counts once against the per-key limit, whether it reads one render or creates 100, so batching is the cheapest way to send work.
- Rate limits bound how often you call. What you can spend is bound separately, by your credits.
- The website visualizer has its own limits, per shopper, which the API does not share.
Rate limit headers
| Header | Means |
|---|---|
X-RateLimit-Limit | Requests this key may make in each window. |
X-RateLimit-Remaining | Requests left in the current window. |
X-RateLimit-Reset | Seconds until the current window ends and the count starts again. |
Retry-After | On a 429 only: seconds to wait before trying again. |
Successful responses carry the first three; a 429 carries all four.
Over the limit
A request over the limit is refused with 429 Too Many Requests and is not carried out, so nothing is charged.
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 17{
"error": {
"type": "rate_limited",
"message": "Too many requests. Try again in 17 seconds."
}
}Back off and retry
Wait for Retry-After, then send the same request again. For a render request, keep the same Idempotency-Key.
async function withBackoff(send, attempts = 5) {
for (let attempt = 1; ; attempt++) {
const response = await send();
if (response.status !== 429 || attempt === attempts) {
return response;
}
const seconds = Number(response.headers.get("Retry-After") ?? 1);
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
}Stay under the limit
- Send many renders as one batch of up to 100, not as 100 requests.
- Poll renders every few seconds, not in a tight loop. Poll for results has a schedule that works well.
- Read
X-RateLimit-Remainingand slow down as it nears 0, rather than waiting for a 429. - Cache what changes rarely, such as your product list, instead of fetching it before every render.
- Reuse scenes rather than adding the same room again.
Higher limits
Something these pages do not answer? Email admin@skipshoot.com and we’ll help.
