Rate limits
Cubicle HTTP API rate limits per user or service account (600 requests and 120 non-GET requests a minute), device sign-in limits, 429 rate_limited with Retry-After and retryAfterSeconds, strikes and the automatic 60-minute address block (403 blocked), and how to poll and retry within budget.
Cubicle limits how fast each account may send requests, and blocks client addresses that keep
sending after they are refused. These are the limits on api.cubicle.sh. They may change.
Request budgets
Every authenticated request spends from the budget of the user or service account that sends it:
| Budget | Limit | Counts |
|---|---|---|
| All requests | 600 per minute | Every authenticated request |
Non-GET requests | 120 per minute | Every request whose method is not GET |
- A
POST,PUTorDELETEspends from both budgets. - A budget belongs to the account, not to a token. All API keys of one service account share it, as do all sessions of one person. A second key for the same service account adds no budget; a separate service account has its own.
- A budget refills continuously, not once a minute. A full budget allows a burst of up to 600
requests (120 non-
GET); after that, one more request fits every 100 ms, and one more non-GETrequest every 500 ms. - A refused request spends nothing.
- A request that fails authentication is answered before any budget is checked, and spends nothing.
The device sign-in routes, which need no credentials, are limited per client address instead:
- Start a device sign-in shares the sign-in limit with the Dashboard's other sign-in requests: 20 a minute per client address, and 300 a minute across all clients.
- Exchange a device code may be called 120 times a minute per client address, and 3000 times a minute across all clients.
When you exceed a budget
A refused request gets 429 rate_limited. The Retry-After header and error.retryAfterSeconds
both give the whole number of seconds, at least 1, until the request would fit:
{
"error": {
"code": "rate_limited",
"message": "Too many requests; retry later",
"retryAfterSeconds": 3
}
}Responses carry no X-RateLimit-* headers, so you cannot read how much budget is left. Pace your
requests instead.
Repeated refusals block your address
- Every
429 rate_limitedis a strike against the client address that sent the request. - An address may collect 30 strikes an hour. The allowance refills by one strike every 2 minutes.
- The next refusal blocks the address for 60 minutes.
- A block covers everyone who sends from that address, whatever their account: for example, every CI job behind the same NAT gateway.
While the address is blocked, every authenticated request and every device sign-in request gets
403 blocked. Retry-After and retryAfterSeconds give the seconds until the block ends:
{
"error": {
"code": "blocked",
"message": "this address is blocked",
"retryAfterSeconds": 3517
}
}Cubicle operators can also block an address. Such a block may have no end: it then has no
Retry-After, and lasts until it is lifted.
Stay within budget
- Poll about once a second. One poll a second for each Box or operation you wait on is enough. See Requests and waiting.
- Poll one listing for many Boxes.
List an organization's Boxes returns every Box in
one request, each with the same
state,desired,observedandsshfields as Get a Box. - Stop on 429. Wait
Retry-Afterseconds before you send anything else from that client, not only before repeating the refused request: each further refusal is another strike. - Back off when there is no
Retry-After. After a lost connection or a502,503or504, wait longer before each retry, for example 1 s, then 2 s, doubling up to 30 s. - Share the budget across workers. Parallel jobs that use the same service account, through one key or several, share 600 requests a minute. Spread their polling, or give independent workloads their own service accounts.
The SDK paces its polls and
retries a 429 for you.
Errors
The Cubicle HTTP API error envelope with code and message, every error code the public routes return (invalid_request, unauthenticated, forbidden, not_found, conflict, idempotency_conflict, rate_limited, capacity and more) with its HTTP status and what to do, conflict messages, and which errors are safe to retry.
All endpoints
Every public endpoint of the Cubicle HTTP API, grouped by what it manages, generated from the OpenAPI document.

