Rate limits and retries
Every authenticated request clears two buckets: the key's own per-minute allowance, and the workspace's plan allowance, which every key in the workspace shares. Exceeding either returns 429 Too Many Requests with the rate_limited error code.
The shared bucket is the one that surprises people: minting more keys does not buy more throughput. The per-key bucket exists to stop one runaway credential starving the others, not to multiply your ceiling. error.details.scope on a 429 tells you which bucket refused the call — "key" or "organization" — so you know whether to slow one worker down or the whole fleet.
The ceilings
The workspace allowance is set by your plan. A key's own allowance is configurable (minimum 10) but is clamped to the plan ceiling at request time, so a downgrade applies immediately to keys minted before it:
| Plan | Requests / minute (whole workspace) |
|---|---|
| Starter | 60 |
| Growth | 120 |
| Business | 300 |
| Enterprise / Platform | 600 / 1 200 |
Read the headers rather than hard-coding a number — the value you see is the live ceiling for the call you just made.
Limit headers
Successful and rate-limited responses both carry the current window state, so you can pace yourself before you ever hit a 429. When both buckets pass, the headers report whichever has less headroom left, so pacing against them always obeys the binding ceiling:
| Header | Meaning |
|---|---|
x-ratelimit-limit | The maximum number of requests allowed in the window. |
x-ratelimit-remaining | Requests left in the current window. |
x-ratelimit-reset | When the window resets, as an ISO-8601 UTC timestamp — not a unix epoch. Parse it as a date. |
A 429 from /api/v1 also carries retry-after: the whole number of seconds to wait before retrying. It names the same moment as x-ratelimit-reset (also repeated as error.details.reset_at), so a client that waits on retry-after by default retries at the right time.
The 429 response
HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 120
x-ratelimit-remaining: 0
x-ratelimit-reset: 2026-08-19T10:31:00.000Z
retry-after: 37
{
"ok": false,
"error": {
"code": "rate_limited",
"message": "API rate limit exceeded for this workspace. Your plan's per-minute API limit is shared by every key in the workspace, so minting another key does not raise it.",
"details": {
"limit": 120,
"reset_at": "2026-08-19T10:31:00.000Z",
"scope": "organization"
}
},
"request_id": "req_abc123"
}The standard next_steps array is elided above for brevity — every error carries one; see Error codes for the full envelope.
A 429 is always safe to retry: you were rejected before any work happened, so there is nothing to duplicate. That is not true of the other retryable statuses — see the SDK's idempotency rules.
The SDK retries for you
The SDK automatically retries 429 and 5xx responses with exponential backoff and jitter (roughly 0.3s, 0.6s, 1.2s). It retries twice by default; tune it with maxRetries (set 0 to disable):
const bt = new Blueticked({
apiKey: process.env.BLUETICKED_API_KEY!,
maxRetries: 4, // default is 2; set 0 to handle retries yourself
});If retries are exhausted, the SDK throws a typed BluetickedError with code: "rate_limited" and status: 429 — see Error codes.
Backoff guidance (raw HTTP)
- On a
429, sleep untilx-ratelimit-reset, or back off exponentially (for example 0.3s, 0.6s, 1.2s) with a little random jitter to avoid thundering-herd retries. - Use
x-ratelimit-remainingto slow down before you are blocked — pause when it approaches zero rather than sending until you are rejected. - Branch on
error.details.scope. A"key"refusal means this worker is too fast; an"organization"refusal means the whole workspace is at its plan ceiling and adding workers or keys will make it worse. - Spread bulk work over time. For large fan-outs, prefer a campaign — it queues and paces sends server-side instead of you looping over
messages.send. - Cap total retries so a persistently throttled caller fails loudly rather than retrying forever.