Guides

Rate limits

Enough headroom for any reasonable integration, with a clear path when you need more.

Default limit

Every token gets 60 requests per minute by default, applied as a token-bucket with a burst capacity of 60. Steady-state throughput is one request per second; short bursts up to the bucket size are absorbed without penalty.

The limit is per-token, not per-merchant. If you split one integration into multiple tokens for different services, each gets its own bucket.

Per-token overrides

Need more headroom — a heavy back-fill, an ERP pull every five minutes, a real-time inventory sync? Bump the limit on a specific token in Developers → API tokens → Edit token → Rate limit. There's no fixed ceiling; we'll work with you on anything above a few hundred req/min so we can size the backend right.

When you hit the limit

Exceeded requests return 429 Too Many Requests with a Retry-After header (seconds) and the standard error envelope:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded — retry in 12s."
  }
}
Honour Retry-After
The hint isn't advisory — it's the bucket's actual recovery time. Retrying earlier just burns more rate-limit budget on rejections.

Recommended retry strategy

For any retry-able failure (429, 500, 503) use exponential backoff with full jitter, capped at around 60 seconds. When the server provides a Retry-After, prefer it.

javascript
async function callWithBackoff(fn, maxAttempts = 5) {
  for (let i = 0; i < maxAttempts; i++) {
    const res = await fn();
    if (res.status !== 429) return res;

    // Prefer the server's hint, fall back to exponential backoff.
    const hint = Number(res.headers.get("Retry-After"));
    const wait = Number.isFinite(hint)
      ? hint * 1000
      : Math.min(60_000, 500 * 2 ** i + Math.random() * 250);
    await new Promise((r) => setTimeout(r, wait));
  }
  throw new Error("rate-limited after retries");
}

Cap your total retries — a runaway loop hitting a sustained 429 wall doesn't become a successful one, it just wastes CPU. Five attempts is a sensible default for synchronous code paths; background jobs can go higher.