Errors
Standard HTTP status codes and one error envelope on every endpoint. Branch on the code; the message is for people.
Error envelope
Every non-2xx response is an object with a single top-level key:
{
"error": {
"code": "invalid_api_key",
"message": "API key is invalid, revoked, or expired",
"details": { }
}
}
Always branch on error.code rather than the English message. Messages may be refined over time; codes are stable. The AI pass-through is the one exception: it returns errors in the provider's own shape, so the provider SDK raises its own typed errors.
Status codes
| Status | When |
|---|---|
| 200 OK | Successful response. A rejected order leg is still a 200 — read results[i].rejected_code. |
| 201 Created | Something was made: a model, a practice deposit, a link session, an app token. |
| 202 Accepted | Queued, not done: a notification send, an AI inference still running past wait_seconds. |
| 400 Bad Request | Invalid parameters, bad JSON, a malformed cursor, an unknown field on a strict body. |
| 401 Unauthorized | Missing, invalid, revoked or expired credential. |
| 402 Payment Required | Out of reasoning credits on a managed AI call. |
| 403 Forbidden | Authenticated, but the plan, key scopes or the user's consent do not cover this — or the account is not yours. |
| 404 Not Found | The resource does not exist, or is not yours (the two are indistinguishable by design). |
| 409 Conflict | State disagrees: an archived model, an already-listed app, a linked AI key the provider rejected. |
| 413 Payload Too Large | An AI request body over 32 MB. |
| 429 Too Many Requests | The per-minute or per-day budget, or a user's monthly AI token cap. |
| 500 Internal Server Error | Unexpected server error. Safe to retry with backoff. |
| 503 Service Unavailable | An upstream dependency is down. Retry with backoff. |
Codes you will meet everywhere
| Code | Meaning |
|---|---|
missing_credentials | Neither X-API-Key nor Authorization: Bearer was sent. |
invalid_api_key | The key is unknown, revoked or past its expiry. |
invalid_jwt | The session token failed verification or expired. |
entitlement_required | Your plan or key scopes lack a code; details.missing lists which. |
scope_not_granted | An install key whose user declined the scope, or a Connect user who withheld a venue permission. |
account_forbidden | X-OpenMarkets-Account names a user your organization does not own. |
rate_limit_exceeded | Per-minute or per-day quota hit. details.window says which; Retry-After says how long. |
unknown_parameter | A field outside a strict contract (orders, models) was sent. |
not_found | The addressed league, contest, market or position does not exist. |
invalid_position_hash | The position_hash is not a 6-part colon-separated string. |
internal_error | Unexpected server-side failure. Safe to retry. |
Each page lists the codes specific to its surface — orders, models, AI Connect, Connect and notifications all have their own.
Retrying
Safe to retry: 429 (after Retry-After), 500, 503. Use exponential backoff with jitter. On a write, send the same Idempotency-Key so a retry cannot double-place.
Not safe to retry without changes: 400, 404, 409 — fix the input or the state first.
Never retry: 401 and 403. A second attempt with the same credential fails the same way.