Skip to main content
The whole API returns responses in one fixed shape. This page shows how to recognise an error, when to retry it and how to stay within the limits.

Response shape

Success:
Some endpoints add context fields — e.g. creating an order returns redirectUrl, and a free order returns free: true. Paginated lists return data, total, page, pages, hasNext, hasPrev. Error:

Status codes

Machine codes

Selected code values worth handling in code: New codes may appear — treat an unknown code according to its statusCode.

Validation

The API rejects invalid data before processing it. Fields outside the schema are ignored and types are converted automatically (e.g. numbers from the query string). Lists accept page and limit; limit is clamped to a safe maximum (usually 100).

Rate limits

The whole REST API has a global limit per real client IP. Production configuration: 200 requests per minute (RATE_LIMIT_MAX=200, RATE_LIMIT_WINDOW=60000); the platform operator can change both values. Sensitive routes have additional, stricter fixed windows, counted per IP and route: Login routes have their own brute-force limits. Payment webhooks are protected per provider and shop while keeping the provider’s retry semantics.
Counters for sensitive routes are shared through Redis across API instances. If Redis is briefly unavailable, each instance keeps a bounded in-memory counter — protection doesn’t disappear, it just becomes per-instance.
The game plugin doesn’t poll REST — its Socket.IO connection has its own authentication, validation, debounce and event limits (see Plugin protocol).

Retries

Retry only 429, 500, 503 and network errors. Don’t retry 400, 401, 403, 404.
  • If a 429 response has a Retry-After header, wait that many seconds. Not every response has it (stricter per-route limits don’t send it) — then use backoff.
  • Exponential backoff with jitter: e.g. 1 s, 2 s, 4 s ± random 0–500 ms, a few attempts at most.
  • Never retry immediately in a loop — it only extends the block.

Idempotency

Creating an order (POST /orders, and from 3.0 also POST /storefront/v1/shops/<shopId>/checkout) requires an Idempotency-Key header:
  • a random value of 32–128 chars (A-Z a-z 0-9 _ -), e.g. a UUID without dashes or base64url;
  • generate it once per purchase attempt and reuse it only when retrying the identical request;
  • a retry with the same key and the same body returns the same order instead of creating a second one;
  • the same key with a different body → 409 IDEMPOTENCY_KEY_REUSED; a closed attempt → 409 CHECKOUT_ATTEMPT_CLOSED.
The server stores only the key’s SHA-256 hash. On the payment side ItemShop is also idempotent towards repeated provider notifications — an order is marked paid exactly once.

Next

API with a key

Keys, scopes and your first request.

Webhooks

Discord notifications and payment-provider webhooks.