Skip to content
Lucent ImagingLucent Imaging icon
Core concepts

Rate limits

Limits are counted per key rather than per address, so your throughput is yours alone and is not affected by anyone else integrating from the same network.

The limits

Each endpoint has its own budget, measured over a rolling minute. Reads are generous; the writes that spend credit are deliberately tighter.

EndpointLimit

GET /catalogue

120/min

GET /orders

120/min

POST /orders

30/min

GET /orders/{id}

120/min

POST /orders/{id}/cancel

30/min

POST /quotes

60/min

GET /quotes/{id}

120/min

POST /uploads

60/min

Failed authentication

20/min

Failed authentication is counted by source address rather than by key - a request that did not authenticate has no key to count against.

Handling a 429

Over the limit you get 429 with error.code = "RATE_LIMITED" and a Retry-After header in seconds. Honour it: it is computed from the actual window, so it will always beat a guess.

async function callWithRetry(path: string, init: RequestInit, attempt = 0): Promise<unknown> {
  const res = await fetch(API + path, init);

  if (res.status === 429 && attempt < 5) {
    // Retry-After is in seconds and is authoritative - prefer it to your own
    // backoff curve, which cannot know how full the window is.
    const wait = Number(res.headers.get("Retry-After") ?? 5);
    await new Promise((r) => setTimeout(r, wait * 1000));
    return callWithRetry(path, init, attempt + 1);
  }

  return res.json();
}

There are currently no X-RateLimit-* headers, so you cannot see how much budget is left before you spend it. If you are running a bulk job, pace it against the table above rather than sprinting into a 429.

One retry rule worth repeating: if a POST /orders is throttled, retry with the same Idempotency-Key. A fresh key on a retry is how an order gets placed twice.