Every failure has the same shape:
{
"error": {
"code": "invalid_request",
"message": "'plan' must be a string.",
"details": {"field": "plan"}
}
}
code is the stable, machine-readable half and is part of the published
contract. message is prose for humans and may be reworded. Branch on
code. details is present when there is something specific to point at,
usually the offending field.
The codes
| Code | Status | Meaning | What to do |
|---|---|---|---|
unauthorized |
401 | Missing, unknown, revoked, or expired key | Check the header. Do not retry with the same key |
payment_required |
402 | API locked, or this deploy cannot be funded | Add credit or a payment method. See the message |
forbidden |
403 | Account suspended or closed, no billing profile yet, or a read-only key writing | Fix the account or use a full-scope key |
not_found |
404 | No such resource, or not yours | Both cases answer the same. Check the id |
method_not_allowed |
405 | Wrong verb | Read Allow on the response |
invalid_request |
400 | Malformed body or a bad field | Read details.field |
invalid_request |
413 | Body over 16 KB | Nothing here needs a body that big |
conflict |
409 | Right request, wrong state | See below |
rate_limited |
429 | Over a rate limit | Back off. Honour Retry-After when it is present |
server_error |
500 | Our bug. Already reported | Retry once, then open a ticket |
conflict in particular
409 means the request was valid but the server is not in a state to accept it:
- Starting a server that is already running
- An action while another action is still queued
- Any power action on a billing-suspended server
- Destroying something already destroyed
Read the message, fix the state, retry. Retrying immediately without changing anything will conflict again.
A 404 you did not expect
Asking for a server id that belongs to another account returns 404, not 403.
A 403 there would confirm the id exists.
Retrying safely
429and500: retry with exponential backoff.401,402,403,404,400: do not retry. Nothing about waiting changes the answer.409: retry after the state changes, not on a timer.
POST /servers is not idempotent
There is no request key to deduplicate on. A retry after a timeout can deploy a second server that you will be billed for. If a create times out, list your servers and check before retrying.