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:
- Honor
Retry-Afterwhen present. It is the authoritative wait time. - 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/batchcreates 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
- Errors and the response envelope: the 429 response body in context
- Webhooks: event delivery instead of polling