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

LimitDefaultCounted
Requests per secret key600 a minutePer key, in fixed one-minute windows
Scenes added through the API500 a dayPer workspace, across all its keys, per UTC day
Renders in one request100Per request
Renders running at once3Per 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

HeaderMeans
X-RateLimit-LimitRequests this key may make in each window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetSeconds until the current window ends and the count starts again.
Retry-AfterOn 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.

Headers
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 17
Body
{
  "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-Remaining and 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.