Skip to content
Skip the menu

Concepts

Rate limits

How many requests each key, player and address may send per period, what a 429 looks like, and how to stay under the limits.

View as Markdown

On this page

The limits

Each limit counts requests in a fixed window. The window starts at the top of the period (the top of the minute or of the hour) and the count restarts when it ends.

What is countedLimitWindow
Server API, per secret key600 requests1 minute
Client API with a player token, per player120 requests1 minute
Client API without a token (catalog, token exchange), per IP address120 requests1 minute
Creating an anonymous player, per IP address30 requests1 hour
Gift code attempts (right or wrong), per player10 attempts15 minutes
Gift code attempts, client API, per IP address30 attempts15 minutes
Creator codes typed at checkout, per player30 attempts15 minutes
Creator codes typed at checkout, client API, per IP address60 attempts15 minutes

Two keys do not share a counter, and neither do two players. A busy player cannot slow down the others.

The OpenAPI document (/api/v1/openapi.json) is public and has no rate limit.

When you go over

The request is refused with 429 RATE_LIMITED and nothing changes. The Retry-After header holds the number of seconds to wait.

A 429 answer
HTTP/1.1 429 Too Many Requests
Retry-After: 33
Cache-Control: no-store
Content-Type: application/json; charset=utf-8

{"error":{"code":"RATE_LIMITED","message":"Too many requests"}}

The order of checks matters: a request is checked for authentication first, then the rate limit, then the idempotency key, then the body. A refused request still counts.

Staying under the limits

  • Do not poll. Read a balance when something may have changed, not on a timer. The browser SDK keeps a cached copy: gc.balance("gems") costs no request, and change events tell you when it moves.
  • Cache the catalog. It changes when you edit it, not on every frame.
  • Use one token per player session, and renew it only when it expires (once an hour).
  • Spread bulk work. A job that grants rewards to thousands of players should pace itself under 600 requests a minute, with a small queue.

Retrying a 429

Wait for Retry-After, then send the same request again. If it is a write, send it with the same idempotency key: the retry cannot count twice.

The SDKs do it for you. The server SDKs retry a 429 after Retry-After (up to 30 seconds), up to two times by default. The browser SDK waits up to 10 seconds; for a longer wait it stops and raises RATE_LIMITED, with the seconds in error.details.retryAfter, so a game never freezes.

LangageLanguage
import { ErrorCode, GameCoinError } from "@apilow/gamecoin";

try {
  await gamecoin.catalog.get();
} catch (error) {
  // The SDK already waited and retried; this is what is left.
  if (error instanceof GameCoinError && error.code === ErrorCode.RATE_LIMITED) {
    console.log(`still limited: try again in ${error.retryAfter} seconds`);
  } else {
    throw error;
  }
}

Where next