Every non-2xx response of the public API has the same envelope:
{
"error": {
"code": "not_found",
"message": "Project not found",
"status": 404
}
}
code — a stable, machine-readable identifier. Branch on it, never on the
message text.message — a human-readable English description. It may change.status — the HTTP status, repeated for convenience.details — optional structured context, such as the fields that failed
validation.| Status | Code | When | Retry? |
|---|---|---|---|
| 400 | bad_request | The request cannot be served as sent. | No — fix the request. |
| 401 | unauthorized | Missing, malformed, unknown, revoked or expired API key. Always the same response. | No — see Authentication. |
| 403 | forbidden | The key is valid but does not hold the endpoint's scope. | No — see Scopes. |
| 404 | not_found | No such resource in your company, including one that belongs to another company. | No. |
| 409 | conflict | The project has no database, so its datasets have no rows to read. | No — until the project gets one. |
| 422 | validation_error | A parameter failed validation; details names each one. | No — fix the request. |
| 422 | query_failed | A dataset's query could not run in ClickHouse. The query is never echoed back. | No — the dataset needs fixing in Muvia. |
| 429 | rate_limit_exceeded | A rate-limit window is exhausted. | After Retry-After — see Rate limits. |
| 500 | internal_error | An unexpected server error. | Yes, with backoff. |
| 503 | service_unavailable | The data store behind dataset rows did not answer, or refused the platform. | Yes, with backoff. |
New codes may appear within v1 (see Versioning). Handle a code you do not know by its HTTP status.
A 422 validation_error carries one entry per offending parameter, named by
field:
{
"error": {
"code": "validation_error",
"message": "The request is not valid",
"status": 422,
"details": [
{ "field": "size", "message": "Input should be less than or equal to 100" }
]
}
}
Asking for a project, dataset or node that belongs to another company answers
404, the same as asking for one that does not exist. The API never confirms
that data outside your company exists.
For 500 and 503, retry with exponential backoff and jitter — for example
1, 2, 4, 8 seconds, then give up and alert. For 429, wait exactly as long as
Retry-After says. Never retry a 400, 401, 403, 404 or 422 unchanged:
the answer will be the same.