Skip to main content

client_order_id is the contract

You supply client_order_id on every order. It must be unique per order and is the idempotency key for the whole placement path. The rule that matters: on any failure where you are unsure, retry with the same client_order_id. At most one order ever results. Retrying with a new id after a timeout is how duplicate orders happen.

Order states

Cancellations arrive as success

A 201 does not mean your order is on the book.Post-only orders that would have crossed, unfillable fok orders, ioc remainders, self-trade prevention, and market buys that would breach max_cost all return a successful response with status: "canceled" and a reason.They are execution outcomes, not errors. Always read status on a success response instead of assuming placement succeeded.

Placement responses are real outcomes

The API holds your request until the order is sequenced, so the response describes what actually happened — filled_qty, resting_qty, and a fills array in stream order. There is no separate “pending” state to poll through. seq on the response is the sequencer stamp, which is your audit join back to canonical history.

Balances are not in the response

Reservation and fee accounting are deliberately absent from placement responses — they arrive on a different lane. Get them from GET /v1/portfolio/balance or, better, the user WebSocket channel.

Reading your orders

status takes a comma-separated list. Querying by client_order_id is how you resolve a timeout.

Cancelling

decrease shrinks resting quantity without losing queue priority — use it instead of cancel-and-replace when you only want to reduce size. Cancels charge the write bucket, same as placements.

Batching

POST /v1/portfolio/orders/batch places several orders in one request. It charges one write token per order, so batching saves round trips, not rate limit budget.