Rate limits
Per-key, per-minute. How to back off when you hit them.
Sellvik enforces per-key rate limits in a Postgres-backed sliding window. Limits are calibrated for typical merchant traffic; sustained loads above default can be raised on request — email developers@sellvik.com with a key prefix and a use case.
Defaults
| Key class | Limit |
|---|---|
Admin (sk_live_…) | 1000 requests/minute |
Publishable (pk_live_…) | 600 requests/minute |
The limit applies per key, not per shop. If you mint multiple admin keys for the same shop (e.g. one per integration), each gets its own bucket.
Detecting a hit
When the limit is exceeded, every subsequent request that minute returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
Content-Type: application/json
{
"error": {
"code": "rate_limited",
"retryAfter": 23,
"limit": 1000
}
}Retry-Afterheader: seconds until the bucket resets. Always present.error.retryAfterbody field: same value. Use the header.error.limit: your per-minute ceiling, for telemetry.
Backoff
async function callWithBackoff(fn: () => Promise<Response>) {
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fn()
if (res.status !== 429) return res
const retryAfter = Number(res.headers.get("Retry-After") ?? 1)
await new Promise((r) => setTimeout(r, retryAfter * 1000))
}
throw new Error("rate limit not clearing")
}Do not retry tighter than Retry-After. Every retry inside the window adds
to the bucket and pushes the reset further out. Honouring the header
guarantees the bucket has rolled by your next attempt.
Avoiding the limit
- Cache reads. Catalog and category endpoints are safe to cache for
minutes; their response headers include
Cache-Control: public, max-age=...where applicable. - Page large reads. A daily catalog sync of 10k products at 50/page is 200 calls — well under the cap. Spread it; don't fire all 200 simultaneously from N workers.
- Use webhooks. Polling for order updates burns the bucket. Subscribe to
order.created,order.fulfilled, etc. — see Webhooks.
Limit raises
We grant raises for legitimate use cases. Mail us with:
- The key prefix (the visible
sk_live_a1b2c3part, not the full key). - The endpoint(s) you're hitting.
- The target sustained rate (per minute or per second).
- The shape of the workload (steady, bursty, scheduled).
Typical raises ship within a working day.