Sellvik / developers
Concepts

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 classLimit
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-After header: seconds until the bucket resets. Always present.
  • error.retryAfter body 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_a1b2c3 part, 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.

On this page