Skip to main content
Every error the API returns carries three things:
  • an HTTP status that tells you the class of failure,
  • a stable error code (error_code on task endpoints, code on LLM endpoints) that tells you the exact reason, so your code can branch on it without parsing the message,
  • a request_id you can quote to support so we can find the exact request in our logs.
Unifically uses two error shapes:
  • Task endpoints (/v1/tasks, /v1/account, /v1/resources) wrap every response in a { success, code, data } envelope. See Task Endpoints.
  • LLM endpoints (/v1/chat/completions, /v1/responses, /v1/messages, /v1/models) return the provider-native error shape. See LLM Endpoints.
The error codes themselves are the same on both. See Error Codes for the full list.

Task Endpoints

Response Structure

Success Response

Error Response

Empty arrays and objects are omitted from responses when not needed.

Response Fields

boolean
Indicates whether the request was successful (true) or failed (false)
integer
HTTP status code of the response
object
Response data on success, error details on failure
string
Human-readable description of the error. Safe to show to your users
string
Stable machine-readable reason. Present when the failure has a defined code (see Error Codes). Branch on this, not on message, since message text can change
string
Unique id of this request. Include it when contacting support. On 5xx responses the same id is also appended to message

HTTP Status Codes

Example Error Responses

Solution: Send your key in the Authorization header:
Solution: Top up in the dashboard. The request was not billed
Solution: Edit the key’s allowed models in the dashboard, or use a key without a model restriction
Solution: Implement exponential backoff and respect rate limits
Solution: Check the model’s documentation for required parameters and valid values. The message names the parameter
Solution: Do not retry. Pick a replacement at unifically.com/models
Solution: Retry the request after a brief delay. Contact support with the request id if the issue persists. Failed requests are not billed

LLM Endpoints

The LLM endpoints do not use the { success, code, data } envelope. They return the error shape of the API they are compatible with, so drop-in OpenAI and Anthropic clients parse it natively. The code and request_id fields are the same values as error_code and request_id on task endpoints. /v1/chat/completions, /v1/responses, and /v1/models return OpenAI-style errors:
/v1/messages returns Anthropic-style errors:
The type field follows the status: authentication_error (401), permission_error (403), not_found_error (404), rate_limit_error (429), invalid_request_error (other 4xx), and service_unavailable (OpenAI-style) or api_error (Anthropic-style) for 5xx. Billing failures on /v1/chat/completions and /v1/responses use type insufficient_balance or billing_error. Use code for the exact reason.

HTTP Status Codes

On /v1/chat/completions, a 400 is also returned when messages is empty or not an array, or when a message has a role other than system, developer, user, assistant, tool, or function.

Errors During Streaming

Once a stream has started, the HTTP status is already 200. A later error arrives inside the stream, in the same shape as a non-stream error and with the same code and request_id:
  • /v1/chat/completions: a data: chunk with an error object.
  • /v1/responses: an error event, with no [DONE] after it.
  • /v1/messages: an error event.
On chat completions, check each chunk for error before reading choices. On the other two, handle the error event. Treat it as a failed request. The most common in-stream error is insufficient_balance: your balance ran out mid-generation. You are billed only for the tokens received before the stop.

Error Codes

The code is data.error_code on task endpoints and error.code on LLM endpoints. Codes are stable: a code’s meaning never changes, and new codes may be added. The messages next to them are examples and may be reworded. An error without a code is an internal failure; the HTTP status says all there is to say about it.

Authentication

Models and access

Request validation

Billing

Rate limits

Idempotency

These apply only when you send an Idempotency-Key header. The header makes a repeated request with the same key, body, and route return the original response instead of running again. Keys are scoped to the API key that sent them.

Upstream providers

Best Practices

Branch on the code

Use error_code / code to decide what to do. Fall back to the HTTP status when there is no code

Implement Retry Logic

Retry with exponential backoff on 429, 500, and 503. Never retry 540 or 402

Log the request id

Log every error response with its request_id. Support can trace a request by that id alone

Check streams too

An error can arrive inside a 200 stream. Handle the error chunk or event like a failed request
Show the error message to your users: data.message on task endpoints, error.message on LLM endpoints
Never expose your API key in client-side code or public repositories