Skip to content

Rate limits

The API is rate-limited on two levels: a token bucket per API key for day-to-day fairness, and a ceiling per team so no single team can monopolize the platform. Every response tells you where you stand, and a 429 tells you exactly how long to wait.

Two levels: per-key bucket and per-team ceiling

Section titled “Two levels: per-key bucket and per-team ceiling”
  • Per-key token bucket. Each API key gets its own bucket that refills continuously. This is the limit you’ll meet in normal use, and it keeps one noisy script from starving your other integrations.
  • Per-team ceiling. A separate aggregate limit applies across all keys in a team. It exists for fairness and abuse resistance — minting more keys doesn’t buy more total throughput, because every key in the team draws against the same team ceiling.

Buckets are sized by action class, because a read costs far less than provisioning an environment. The class is determined by the HTTP method:

Action class Example limit Covers
read 1000/min GET requests — lists, gets, observability reads.
write 100/min PATCH, PUT, and DELETE — updates and removals.
create 5/min POST — creating resources and starting jobs.

Every response — not just 429s — carries your current standing, so you can throttle proactively instead of waiting to be rejected:

Header Meaning
X-RateLimit-Limit The bucket’s ceiling for the matched action class.
X-RateLimit-Remaining Tokens left in the current window.
X-RateLimit-Reset Unix timestamp when the bucket next refills to full.
Retry-After On a 429 only — seconds to wait before retrying.
Headers on a normal 200 response
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1782192000

Exceed a limit and you get 429 Too Many Requests with a Retry-After header and the standard error envelope:

A rate-limited response
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1782192012
Content-Type: application/json
{
"error": {
"type": "rate_limit",
"code": "rate_limit.exceeded",
"message": "rate limit exceeded for write actions; retry after 12s",
"doc_url": "https://docs.managed.dev/errors/rate_limit/rate_limit.exceeded"
},
"request_id": "req_01J9…"
}

To see your current standing, read GET /v1/rate-limits. It reports every bucket — limit, remaining tokens, and reset time — plus the team ceiling, and the peek itself doesn’t consume a token:

GET /v1/rate-limits
curl https://api.managed.dev/v1/rate-limits \
-H "Authorization: Bearer mfk_live_…" \
-H "Forge-Version: 2026-06-23"
Buckets for the calling key
{
"data": {
"buckets": [
{ "class": "read", "limit": 1000, "remaining": 1000, "reset": 1782192060 },
{ "class": "write", "limit": 100, "remaining": 87, "reset": 1782192000 },
{ "class": "create", "limit": 5, "remaining": 5, "reset": 1782192000 }
],
"team_ceiling": { "limit": 5000, "remaining": 4870, "reset": 1782192000 }
},
"request_id": "req_01J9…"
}

The example figures above are illustrative — read your real ceilings off this endpoint.

When you hit a 429, retry — but politely:

  1. Honor Retry-After first. It’s the authoritative wait. Sleep at least that long before retrying.
  2. Use exponential backoff with jitter for repeated failures: wait base * 2^attempt, plus a random fraction, so a fleet of clients doesn’t retry in lockstep and re-spike the limit (the thundering-herd problem).
  3. Cap your retries, then surface the error. Don’t loop forever.
  4. Throttle proactively. Watch X-RateLimit-Remaining and slow down as it approaches zero, rather than sprinting into a wall of 429s.

The Go SDK implements this for you by default: it retries 429s and 5xxs with exponential backoff and jitter, honoring Retry-After, so you get polite retries without writing the loop.