Skip to main content

Matching has already happened when the call returns

POST /v1/orders is not an acknowledgement. By the time you have a response, your order has been matched against the book and the result is final. The status on that response is what actually happened:
An ioc order that finds no liquidity comes back canceled, not accepted. This is the most common surprise for a taker strategy, and the response is telling you the truth: nothing traded and nothing is resting, so there is nothing to poll for.

The six statuses

accepted needs the caveat: it is a transient state you will only ever see on a read, while the durable record catches up. A create or amend response never returns it. Without knowing that, a client that sees filled from a create and accepted from a read a moment later concludes the two endpoints disagree — they do not; one is ahead of the other. Treat accepted and open identically in your own bookkeeping.

Filtering by status

GET /v1/orders?status= accepts group names as well as concrete statuses, and combines them comma-separated (open,filled): resting is the one a quoter wants each cycle: it is exactly the set of orders that still have working quantity, and it includes accepted so an order does not vanish from your view while the read side catches up. Group names and status values deliberately share no ambiguous token. filled means the same thing in both. resting is a group name only — no order’s status is resting.

Budget orders: spend cash instead of counting shares

A create sized in cash instead of shares: send budget_cents instead of qty — “spend $50.00 on yes” — with side: buy and time_in_force ioc or fok. The engine sizes the order against the live book inside the match step, fees included, so the budget is consumed exactly; there is no client-side dollars-to-shares conversion to get wrong.
Three things change relative to a qty order:
  • price is a slippage bound, not a target. Fills execute at the resting prices, which may be better; nothing executes above your bound.
  • The response’s qty and filled_qty are the engine-derived executed size, and spent_cents is the exact realized cost — fill flow plus taker fees, cent for cent what left your balance. spent_cents / qty is your realized average cost per share.
  • A budget order never rests. fok fills the full affordable size or rejects 422 budget_not_fillable when the book cannot absorb the budget within your bound; ioc executes whatever is eligible and stops. The budget is also capped at your available cash — spending is bounded by min(budget_cents, available_cents).

Idempotency, and why it matters more here

POST /v1/orders takes a client_order_id: 1–64 printable ASCII characters, yours to choose, unique per user. Not globally unique — you never need to coordinate with anyone else’s ids. It does one job, and it is the important one: it makes a retry safe. Re-sending with the same client_order_id returns the existing order instead of creating a second.
It is echoed on the create response and then not stored. client_order_id does not come back from GET /v1/orders or GET /v1/orders/{order_id}, so keep your own client_order_id → order_id mapping. If you lose one, re-sending the identical create returns the same order — which is also how you recover its order_id.
Amends carry no idempotency key at all; see below. That safe-retry property is what you reach for when a write returns 503 result_unknown — the command reached the matching engine but its outcome did not reach you. Re-send with the same id, or poll the order. Never re-send with a fresh id. See result_unknown; it is the paragraph most worth reading twice before your bot goes live.

Amending and canceling

An amend body takes price, qty, or both; an omitted field keeps its current value and a body with neither is 400. Any other field you send is ignored. It takes no idempotency key — the order_id in the path already names one order. That also means an amend has no dedup to fall back on, so if one returns 503 result_unknown, read the order back before deciding what to do rather than re-amending blind.
Queue position survives only if price is unchanged and qty does not increase. Sizing down keeps your place. Any price change, or any increase in size, makes this a new order with a new timestamp and sends it to the back of the queue at that price.The size half of that rule is the one that surprises people: adding 50 shares to a resting order is not “topping up”, it is requoting.
Canceling an order that is already filled, canceled, or expired is 422 order_not_cancelable, not something to retry.
Watch your rate limit around cancels specifically. A throttled cancel leaves an order resting that you believe you pulled, and every decision after that is made against inventory you do not have.

Your own orders will not trade with each other

If your incoming order would match your own resting order, neither trades. Both are reduced by the overlapping quantity, no fill is generated, and no trade prints. An order entirely consumed this way ends up canceled. This matters if you quote both sides: crossing your own quotes destroys them instead of filling them. See Matching.

After the fill

A fill moves shares and cash immediately. Then:
  • GET /v1/portfolio/fills — your fill history. Each fill carries outcome_price (your price) and trade_price (the tape’s). Reconcile cash against outcome_price; see Binary markets.
  • GET /v1/portfolio/positions — what you hold, per outcome.
  • GET /v1/portfolio/balance — cash, and how much of it is reserved against resting orders.
Positions persist until the market resolves, at which point they pay out. See Settlement.