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/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded — retry in 12s."
}
}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.
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.