Errors and retries
The error envelope, HTTP status codes, and which requests are safe to retry.
Every error response uses the same envelope. type is a stable, machine-readable identifier
you can branch on; message is human-readable and may change between releases. Some error
types carry extra fields alongside these two.
{
"error": {
"type": "authentication_error",
"message": "No API key provided. Pass Authorization: Bearer <key>."
}
}Status codes
| Status | Meaning |
|---|---|
200 OK | Success. The body contains the resource or list you asked for. |
201 Created | A new resource was created. The body contains it, including its ID. |
202 Accepted | The work was accepted and runs asynchronously. The body carries an operation ID; poll Poll an operation or register a webhook rather than holding the connection open. |
204 No Content | Success with an empty body. Deletes and revocations return this. |
401 Unauthorized | Missing, malformed, revoked or unknown key. See below. |
403 Forbidden | Valid key, wrong scope or wrong project. The error carries required_scope. |
429 Too Many Requests | You hit the per-key rate limit. Back off on the Retry-After header. See Rate limits. |
Other 4xx | The request is at fault. Fix it before retrying; the same input will fail the same way. |
5xx | Something failed on our side. Retry with backoff, following the rules below. |
Authentication and permission errors
A 401 means the key itself is the problem: missing, malformed, revoked or unknown.
{
"error": {
"type": "authentication_error",
"message": "No API key provided. Pass Authorization: Bearer <key>."
}
}A 403 means the key is valid but lacks the scope the route requires, or is bound to a
different project. The response names the missing scope in required_scope.
{
"error": {
"type": "permission_error",
"required_scope": "train",
"message": "Key key_2Fd has scopes [read, infer]."
}
}When a deployment returns 403 and nobody is sure which key it is actually using, call
Verify the current key. It returns the key
ID, its scopes and its project binding without side effects.
Retry rules
Every GET and DELETE route is safe to retry. If a read times out or returns a 5xx,
retry it with exponential backoff. On 429, wait for the Retry-After value instead of a
fixed interval; see Rate limits.
POST routes need more care, because a timeout does not tell you whether the server acted
before the connection dropped. That is what idempotency keys are for.
Idempotency
Five POST routes allocate real resource, GPUs, storage or a queue slot, and are therefore
not naturally safe to retry:
- Upload a dataset,
POST /v1/files - Create a fine-tuning job,
POST /v1/fine-tuning/jobs - Create a training run,
POST /v1/training-runs - Create a checkpoint
- Export weights
Retrying one of these after a timeout can create a duplicate: a second file, a second queued
job, a second export. If your client retries automatically, send an Idempotency-Key header
on these routes. Use a unique value per logical request, generated when you first build the
request and reused on every retry of it. A UUID works well. If the original attempt did get
through, the retry returns the existing resource instead of allocating another one.
curl https://api.nucleus-ai.io/v1/files \
-H "Authorization: Bearer $NUCLEUS_API_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-4f7a-9a5d-3c4b2f1e0d9b" \
-F purpose=fine-tune \
-F [email protected]Async routes that return 202 Accepted are a separate case: the request itself completed,
so there is nothing to retry. Poll
Poll an operation with the operation ID from the
response until the work finishes.