EkireDocs

Rate limits

The default limit is 100 requests per minute, counted per IP address.

How limits work

Every request is counted against a bucket. When a bucket fills up within its window, further requests are rejected until the window rolls over.

  • The window. Counting resets on a rolling window — the default is 100 requests per minute. Once the window passes, your allowance refills.
  • The bucket key. The global default is counted per IP address: every request in that window — including session-token traffic — shares one bucket tied to the sender's IP, because the default limit has no custom key. Per-account bucketing, where a session token maps to your account, applies only to the tightened per-route limits below that opt into it — never to the 100/min default. A forged or expired token can never spend a real user's allowance — it falls back to the sender's IP, so the cost lands on whoever sent it.
  • Sensitive endpoints are tighter. Account-security and money routes set their own, stricter limits. For example, requesting an SMS code is capped at 5 per 10 minutes and payment/checkout calls at 15 per minute. Destructive or expensive actions (creating servers, snapshots, keys) sit below the global ceiling too.

Exact numbers can change

The global ceiling and per-route caps are tuned per deployment and adjusted as the platform grows. Treat the values above as current defaults, not a contract — always read the response headers (below) to see the live limit for the endpoint you're calling.

When you hit a limit

Go over the limit and the request is rejected with 429 Too Many Requests. Every response — not just the rejections — carries headers describing your current standing:

  • x-ratelimit-limit — the maximum requests allowed in the window.
  • x-ratelimit-remaining — how many you have left (0 when you're throttled).
  • x-ratelimit-reset — seconds until the window resets and your allowance refills.

A rejected request adds one more:

  • retry-after — seconds to wait before trying again.
HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
x-ratelimit-reset: 42
retry-after: 42
content-type: application/json
 
{
  "error": {
    "code": "internal_error",
    "message": "Rate limit exceeded, retry in 1 minute"
  }
}

The body follows the standard error envelope, but a 429 carries no rate-limit-specific code — it reports the generic internal_error, so don't branch on error.code here. The reliable signals to code against are the 429 status and the retry-after header — not the code or the message text.

Best practices

  • Respect retry-after. When you get a 429, wait the number of seconds it tells you before retrying. Don't hammer the endpoint in a tight loop — that just keeps the bucket empty.
  • Back off with jitter. For repeated retries, grow the delay (exponential backoff) and add a small random offset so many clients don't all retry on the same tick.
  • Watch x-ratelimit-remaining. Slow down as it approaches 0 instead of waiting for the 429.
  • Cache reads. If you poll for status, cache results and widen the interval. A dashboard refreshing every few seconds rarely needs to.
  • Spread work over time. Batch or queue bulk operations rather than firing them all at once.

Let the response set your pace

Read the retry-after value the server sends instead of guessing a backoff constant — it already accounts for the live window on that endpoint.