Skip to content
apikey.tools
SIMTesting

API Rate Limit Header Simulator

See exactly what your client receives at the limit, in three header conventions.

SIMRuns in this tabInput not transmitted

Input

sec

Output

computing

Rate limiting is not a security control on its own, but it is what makes short credentials and one-time codes viable, and it is the difference between a noisy client and an outage.

Three conventions

X-RateLimit-* — the de facto standard. Limit, Remaining, Reset as a Unix timestamp. Most widely understood.

IETF RateLimit — the draft standard, using a compact structured field: "default";r=42;t=3600. Fewer headers, and it separates the policy from the current state.

GitHub styleX-RateLimit-* plus used and resource, since GitHub applies different budgets to different resource classes.

Pick one and be consistent. Sending two conventions with disagreeing values, which happens when a proxy adds its own, is worse than sending neither.

Retry-After is the important one

When you return 429, send Retry-After. Without it, a client has to guess, and its guess will be wrong in one of two directions: too short, and it keeps hammering you; too long, and it stalls unnecessarily.

Clients must honour it. Exponential backoff alone is not enough — a client that backs off from 1s to 2s to 4s while you asked for 60 has made 5 pointless requests before it gets there.

javascript
const retryAfter = Number(response.headers.get("Retry-After"));
const backoff = Number.isFinite(retryAfter) && retryAfter > 0
  ? retryAfter * 1000
  : Math.min(2 ** attempt * 1000, 30_000);

// Jitter matters: without it, every client retries in the same instant.
await new Promise((r) => setTimeout(r, backoff + Math.random() * 1000));

Jitter prevents the thundering herd

When a shared limit resets, every waiting client fires simultaneously and the limit is immediately exhausted again. Adding a random component to the backoff spreads them out. It is one line and it is the difference between recovery and a sustained outage.

Limit per key, not per IP

Per-IP limiting punishes everyone behind a corporate NAT or a mobile carrier gateway, and does nothing against a distributed attacker holding a valid key.

Rate limit per credential. Then a misbehaving client affects only itself, you can raise limits for a specific customer, and the numbers correlate with the account rather than with network topology.

Return 429, not 403

429 means "slow down, try again". 403 means "no, and do not retry". A client that receives 403 for rate limiting will treat it as permanent and stop, which is usually not what you want.

Frequently asked questions

What is a reasonable default limit?

It depends entirely on your workload, but a common shape is 1,000 requests per hour per key for a general API, with higher limits available per plan. Start conservative — raising a limit is easy, lowering one breaks clients.

Should the limit apply to failed requests?

Yes, especially to authentication failures. If failed attempts do not count, an attacker gets unlimited guesses against your credentials. Failed auth should count more heavily, not less.

How do I tell a client their limit is about to run out?

Remaining does this on every response, which lets a well-written client throttle itself before hitting the wall. That is the main argument for sending the headers on successful responses rather than only on 429.

Should Reset be a timestamp or a duration?

X-RateLimit-Reset conventionally uses a Unix timestamp; the IETF draft uses seconds remaining. A duration is more robust because it does not depend on the client's clock being correct. Follow whichever convention you have chosen, consistently.

More testing tools

Rehearse authentication before you ship it