Errors

{ "error": { "code", "message", "details?" } }

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

StatusWhen
200 OKSuccessful response. A rejected order leg is still a 200 — read results[i].rejected_code.
201 CreatedSomething was made: a model, a practice deposit, a link session, an app token.
202 AcceptedQueued, not done: a notification send, an AI inference still running past wait_seconds.
400 Bad RequestInvalid parameters, bad JSON, a malformed cursor, an unknown field on a strict body.
401 UnauthorizedMissing, invalid, revoked or expired credential.
402 Payment RequiredOut of reasoning credits on a managed AI call.
403 ForbiddenAuthenticated, but the plan, key scopes or the user's consent do not cover this — or the account is not yours.
404 Not FoundThe resource does not exist, or is not yours (the two are indistinguishable by design).
409 ConflictState disagrees: an archived model, an already-listed app, a linked AI key the provider rejected.
413 Payload Too LargeAn AI request body over 32 MB.
429 Too Many RequestsThe per-minute or per-day budget, or a user's monthly AI token cap.
500 Internal Server ErrorUnexpected server error. Safe to retry with backoff.
503 Service UnavailableAn upstream dependency is down. Retry with backoff.

Codes you will meet everywhere

CodeMeaning
missing_credentialsNeither X-API-Key nor Authorization: Bearer was sent.
invalid_api_keyThe key is unknown, revoked or past its expiry.
invalid_jwtThe session token failed verification or expired.
entitlement_requiredYour plan or key scopes lack a code; details.missing lists which.
scope_not_grantedAn install key whose user declined the scope, or a Connect user who withheld a venue permission.
account_forbiddenX-OpenMarkets-Account names a user your organization does not own.
rate_limit_exceededPer-minute or per-day quota hit. details.window says which; Retry-After says how long.
unknown_parameterA field outside a strict contract (orders, models) was sent.
not_foundThe addressed league, contest, market or position does not exist.
invalid_position_hashThe position_hash is not a 6-part colon-separated string.
internal_errorUnexpected 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.