Skip to content

Errors

View as Markdown

The API reports errors with standard HTTP status codes and a JSON body. Decide what to do from the status code first, then from the machine-readable code in the body. Never parse the human-readable message: its wording can change.

Status codes

Status Meaning What to do
400 The request was understood but its content is not acceptable: a missing or invalid field, a field you may not change. Fix the request. Retrying it unchanged will fail again.
401 or 403 The key is missing, malformed, unknown or revoked; or the key is valid but not allowed here; or your plan does not include the action. Read the code (below) to tell these apart.
404 The object does not exist, or it belongs to another account. Also returned for a page number past the end of a list. Check the ID. Horizon does not tell you whether another account’s object exists.
409 The request conflicts with the object’s current state. Read the error_code.
429 Too many requests (throttled). Wait for Retry-After seconds and retry.
5xx Something failed on Horizon’s side. Retry with backoff.

A rejected key can come back as 401 or 403 depending on the endpoint. Do not branch on the status alone for authentication problems; use the code.

Body shapes

There is not one envelope for every error. Expect one of these.

A general refusal carries a message in detail, and often a code:

{
"detail": "Invalid or inactive API key.",
"code": "api_key_invalid"
}

A validation error names each field that failed, with a list of messages, and uses non_field_errors for a problem that is not about one field. For example:

{
"quantity": ["A valid integer is required."]
}

An order refusal carries an error_code in capitals, a detail message, and extra keys that depend on the code. Validation-style messages for the fields involved sit under each field’s name, as above:

{
"error_code": "READ_ONLY_FIELDS",
"detail": "These fields are kept by Horizon and cannot be changed through the API: status.",
"read_only_fields": ["status"],
"status": ["…"]
}

A robust client reads code, then error_code, and falls back to the status code when neither is present.

Codes worth handling

Authentication

Code Meaning
api_key_header_invalid The Authorization header starts with Api-Key but is not Api-Key <key>.
api_key_invalid The key is unknown or has been revoked. Do not retry; check which key is deployed.
api_key_owner_inactive The account the key belongs to is no longer active.
api_key_forbidden The key is valid, but it was used outside the documented API, for example to buy a label or change an order. Keys work only on the endpoints in this section. See Authentication and keys.

Plan and billing

Status Code Meaning
403 subscription_required The account has no active subscription that covers this action.
403 dimension_unavailable Your plan does not include this at all.
403 grace_restricted A payment on the account failed. Some actions are paused until the payment details are updated.
403 entitlement_lookup_failed Horizon could not work out your plan just then. Nothing was done. This is on Horizon’s side; try again later.

When a higher plan would lift the limit, the body also carries upgrade_to with the plan’s plan_key and plan_name. Your allowances and current usage are in the app; see Usage and limits.

Orders

Status Code Meaning
400 READ_ONLY_FIELDS A new order set fields Horizon keeps. read_only_fields lists them. Nothing was saved.
400 ADDRESS_INVALID A delivery address field of a new order fails the destination country’s format. fields gives a code per field.

API keys lists the fields Horizon keeps and the address codes.

Rate limiting

A 429 body carries "code": "throttled" and retry_after, the number of seconds to wait, which is also sent in the Retry-After header. retry_after can be null when no wait is known; back off before retrying anyway.