Skip to main content
OCX uses standard HTTP status codes with a JSON error body. Always branch on the status code first, then read the body for detail.

Error shapes

The body depends on which layer answered.
  • Options business rejections carry the code in error.
  • Futures business rejections carry the code inside message, after the status text ("Bad Request: <CODE>: <detail>").
  • Gateway errors (authentication, validation, routing) carry only message. A request that fails schema validation returns 400 with the offending field in message.
New codes may be added over time — treat an unrecognized code as a generic failure of its status class.

Status codes

Common business rejections

These come back on trading and wallet endpoints. They are expected outcomes, not bugs — handle each deliberately.
The order would breach your margin. Every order passes a server-side margin check before acceptance. If the shortfall is only collateral sitting elsewhere in your account, the gateway moves it and retries once before you see this. Otherwise reduce size or deposit. Options rejections include details.reason.
Options: opening this position would leave you unable to close it at current book prices without taking your account to the liquidation line.
A postOnly order would have taken liquidity, so it was rejected to keep you on the maker side. Re-price behind the touch and resubmit.
A reduceOnly order would have increased your position (wrong side, or already flat). Check your position before retrying.
A fok order could not fill in full, or a market order found nothing to take. Nothing executes; adjust price or time-in-force.
Futures: a limit price too far through the mark and the opposite side of the book.
The market is halted or past expiry (MARKET_NOT_TRADING on futures; MARKET_HALTED / MARKET_EXPIRED on options).
A price off the tick, a missing limit price, or a quantity that is not a positive whole number of contracts.
You reused a clientOrderId with different order parameters. A retry with identical parameters returns the original result instead.
The engine’s per-account order rate limit. Returned as 400, not 429; options responses include retryAfterMs.
A multi-leg strategy could not fill every leg immediately. Strategies are all-or-none — nothing executes. The body names failedLegIndex.
A transfer or withdrawal exceeds the available balance. Returned as 400 from the wallet endpoints.
A business rejection is not a transport failure. Retrying it unchanged will fail the same way and can burn your rate budget — fix the cause first.

Rate limits

The gateway limits requests per client in one-minute windows: Rate-limited responses carry: When you exceed a limit you receive 429 with a retry-after header and a body { statusCode, error, message, retryAfter } (seconds). Order placement is additionally limited per account by the engines (RATE_LIMITED, above).

Handling 429 in practice

Do not tight-poll. Prefer the streaming endpoints for live books, fills, the option board, and balances — one SSE connection replaces many polling requests.

Idempotency

Send a clientOrderId on orders. If a response is lost and you retry with the same clientOrderId and parameters, you get the original result rather than a duplicate order. Reconcile by reading your orders rather than assuming a lost request failed.