Errors¶
The API uses standard HTTP status codes plus a JSON body:
| Field | Meaning |
|---|---|
detail | Human-readable message (safe to show or log) |
code | Stable machine-readable code (may be absent on generic errors) |
Status codes¶
| Status | Meaning | What to do |
|---|---|---|
400 | Bad request (invalid/missing fields) | Fix the request body |
401 | Unauthenticated | Missing/invalid/revoked API key — check Authorization |
403 | Forbidden | Read key on a mutating route, or quota exceeded — use a full key / free capacity |
404 | Not found | Resource doesn't exist or isn't yours |
409 | Conflict | e.g. exec on a machine that isn't running; duplicate resource |
429 | Rate limited | Back off and retry (see below) |
5xx | Server error | Transient — retry with backoff; if persistent, contact support |
Common code values¶
code | Typical status | Meaning |
|---|---|---|
quota_exceeded | 403 | An account limit was reached. The message names which one (machines, vCPU, memory, storage, volumes) and how much is in use. Retry succeeds once that resource is freed — unlike forbidden, so branch on code, not the status. See Usage & billing. |
invalid_api_key | 401 | Key missing, malformed, or revoked |
forbidden_scope | 403 | A read key was used on a mutating route |
not_found | 404 | Unknown id (or not owned by you) |
machine_not_running | 409 | Exec/terminal requires a running machine |
rate_limited | 429 | Per-account limit hit |
Handling rate limits¶
On 429, wait and retry with exponential backoff. Mutating routes and exec have separate limits, so a heavy exec loop won't block creates and vice-versa.
How the SDKs surface these¶
Both SDKs map statuses to typed exceptions — AuthError (401/403), NotFoundError (404), RateLimitError (429), and APIError for the rest, each carrying status, detail, and code. See Python errors / JavaScript errors.