Skip to main content
Every non-2xx answer from the v2 API is an RFC 9457 problem document with Content-Type: application/problem+json:
string
The stable, machine-readable name of what went wrong. Branch on this, not on detail and not on type.
string
https://api.splox.com/problems/<code> — the same code as a URI.
string
The status’s name.
integer
The HTTP status, repeated in the body.
string
A sentence for a person. Written to be shown; not to be parsed.
string
Whatever you sent as X-Request-Id, echoed back — send one and the server’s logs and your logs name the same request.
array
On a validation failure, one entry per bad field. See below.

Validation failures

A 422 names every field it rejected, with a JSON Pointer at the exact place:
name is a JSON Pointer for body fields and the plain name for a parameter; location is body, path, query or header; reason is for a person and code is for your code. A field the API does not know is a 400, not a shrug:

The codes

400 — the request is wrong before anything is looked up 401 / 402 / 403 / 404 — who you are, what you may spend, what you may see 406 / 409 / 410 — the request is fine, the state is not 422 — semantically wrong 429 / 5xx

Rate limits

A rate-limited request answers 429 rate_limited with a Retry-After header — seconds, or an HTTP date. Wait that long; retrying sooner earns another 429. The SDKs surface the wait as a number: e.retry_after in Python, err.retryAfter in Node, APIError.RetryAfter in Go. All three retry GETs automatically with backoff, and honor Retry-After when it is there.

Running out of allowance

There is a second kind of 429, and it is not about how fast you are calling. A plan holds two rolling windows — five hours and a week — and when one fills, the refusal carries the whole quota so you can show somebody exactly what happened:
status is allowed, limited or unknownunknown means the read failed and carries no numbers, because rendering it as 0% would claim room nobody verified. mode says what the next call would be funded by: plan, extra (the wallet pays past a full window) or blocked. Retry-After on the response carries the same wait as retry_after_seconds. A window that fills while a run is already going does not produce an HTTP error at all: the run stops and comes back failed, with the reason on it. This one is real, from a run on the account these pages were written against:
usage_window_exceeded and out_of_funds are deliberately different codes. One is fixed by waiting and the other by paying, and telling somebody with money in their account to go to a checkout is both wrong and expensive.

What is safe to retry

POST /v2/runs/{id}/cancel carries no key and needs none: it is idempotent on the server, and a run that is already terminal comes back unchanged.

In the SDKs

Problems arrive as typed errors carrying status, code, detail, trace_id and the parsed body: