Guides

Rate Limits

Per-tier request limits, the rate limit response headers, and how to handle 429 responses with exponential backoff.

Last updated 2026-07-16View as Markdown

Coinbax APIs enforce tier-based rate limits per credential. Limits exist to keep the platform predictable for every integration; well-behaved clients that respect the response headers never hit them.

Tiers

Tier Limit Applies to
Public 20 req/min Unauthenticated endpoints
Standard 100 req/min Regular authenticated API calls
High-frequency 1000 req/min Session polling and other frequent-access endpoints
Admin 50 req/min Administrative operations (deliberately strict)

Endpoints are assigned to tiers server-side; you do not select a tier. Most of your traffic falls under Standard.

Response headers

Every response tells you where you stand:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1735073400
Header Meaning
X-RateLimit-Limit Requests allowed in the current window
X-RateLimit-Remaining Requests left in the window
X-RateLimit-Reset Unix timestamp when the window resets
Retry-After Seconds to wait; sent on 429 responses only

When you exceed a limit

The API returns 429 with the standard envelope (error.code: "RATE_LIMIT_EXCEEDED") and a Retry-After header:

HTTP/1.1 429 Too Many Requests
Retry-After: 42

Two rules:

  1. Honor Retry-After when present. It is the authoritative wait time.
  2. Fall back to exponential backoff with jitter when it is absent or when retrying network-level failures.

Backoff implementation

async function coinbaxWithRetry(path, init, maxRetries = 5) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(`${BASE}${path}`, init);

    if (res.status !== 429) return res;
    if (attempt >= maxRetries) {
      throw new Error(`Rate limited after ${maxRetries} retries`);
    }

    const retryAfter = res.headers.get("Retry-After");
    const waitMs = retryAfter
      ? Number(retryAfter) * 1000
      // Exponential backoff with full jitter: 1s, 2s, 4s, 8s, 16s (capped)
      : Math.random() * Math.min(1000 * 2 ** attempt, 30_000);

    await new Promise((r) => setTimeout(r, waitMs));
  }
}

The jitter matters: if a burst of workers all hit the limit together, fixed backoff makes them retry together and hit it again. Randomized waits spread the retries out.

Staying under the limits

  • Prefer webhooks to polling. One webhook subscription replaces a polling loop entirely; you receive every state transition as it happens instead of spending your budget asking.
  • Watch X-RateLimit-Remaining. Treat a low value as a signal to slow down before you hit 429, not after.
  • Batch where the API supports it. POST /transactions/batch creates multi-recipient payments in one request.
  • Cache stable reads. Template configuration and platform settings change rarely; do not re-fetch them per transaction.
  • Use one credential per integration. Limits apply per credential, so separate integrations do not consume each other’s budget, and a noisy deploy cannot starve an unrelated system.

Next steps