Orders API

Place, preview, and cancel orders, including multi-leg.

Orders open and close positions in a trading session. The endpoints have the same shape on both surfaces — an account-less practice session (/practice/sessions/{sid}/orders) and a live session under an account (/accounts/{id}/sessions/{sid}/orders) — so the request/response bodies below apply to both; only the path prefix differs (live sessions additionally offer dry-run and PUT replace, which practice does not). This page covers placing, previewing, inspecting, replacing, and canceling them. For the concepts — order types, fill rules, SPX ticks — see Orders & order types.

Endpoints

Method Path Purpose
GET /accounts/{id}/sessions/{sid}/orders List orders
POST /accounts/{id}/sessions/{sid}/orders Place an order
POST /accounts/{id}/sessions/{sid}/orders/dry-run Validate + preview without persisting
GET /accounts/{id}/sessions/{sid}/orders/{orderId} Get one order
PUT /accounts/{id}/sessions/{sid}/orders/{orderId} Replace a resting limit order's price
DELETE /accounts/{id}/sessions/{sid}/orders/{orderId} Cancel/delete an order

Instrument strings

Legs name instruments by string:

Type Format Example
Equity <SYMBOL> SPX
Equity option <ROOT><YYMMDD><C|P><strike×1000, 8-digit> (21-char OSI) SPXW 250115C05950000

The equity-option string is canonical OPRA/OSI: a 6-char root left-justified and space-padded, the YYMMDD expiry date, a C or P side letter, and the strike × 1000 zero-padded to 8 digits. SPX 0DTE options use the SPXW weekly root (so SPXW followed by two spaces). An option is identified by root + date + side + strike; the session-close time is implied, not encoded.

Place an order

A limit debit call vertical (buy the 5950 call, sell the 5970 call) — multi-leg orders must be limit:

curl -s https://api.0dtespx.com/accounts/$ACCT/sessions/$SID/orders \
  -X POST -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "type": "limit",
    "price": "10.50",
    "price_effect": "debit",
    "legs": [
      {"instrument":"SPXW  250115C05950000","quantity":"1","action":"buy to open"},
      {"instrument":"SPXW  250115C05970000","quantity":"1","action":"sell to open"}
    ]
  }'

The response includes the computed fill_price, fill_datetime, status, per-leg transactions, fees, and a buying-power-effect block projecting the impact on margin and available buying power:

{
  "id": "a7e2c9d1-…",
  "type": "limit",
  "price": "10.50",
  "price_effect": "debit",
  "status": "filled",
  "fill_price": "10.45",
  "fill_price_effect": "debit",
  "execution_price": "10.50",
  "fees": "3.44",
  "legs": [ … ],
  "transactions": [ … ],
  "buying-power-effect": {
    "change-in-buying-power": "108.44",
    "change-in-buying-power-effect": "Debit",
    "new-buying-power": "99891.56"
  }
}

A single-leg market order fills immediately at the natural price (ask for a buy, bid for a sell); market and stop orders must be single-leg.

Limit and stop orders

Add price for a limit and stop_trigger for a stop, plus a price_effect of debit or credit. Slippage is not a request field — it is a single per-account setting (in Settings → Simulator Costs, defaulting to $0.05 on new accounts) that the server applies to limit/stop fills, and is echoed back on the order's slippage/execution_price — see Slippage:

{"type":"limit","price":"1.50","price_effect":"credit","legs":[ … ]}
{"type":"stop","stop_trigger":"12.00","price_effect":"debit","legs":[ /* single leg */ ]}

On a practice session, a non-marketable limit comes back pending; advance the session clock past its fill_datetime and it shows filled. On a live session it stays pending until a matching tick arrives. A stop that would trigger immediately is rejected with 400.

Validation order

Checks run in this order, each with its own 400:

  1. Order type and leg shape parse; market and stop orders must be single-leg. Every leg must be a 0DTE SPX index option (only 0DTE SPX index options can be traded).
  2. price required for limit, stop_trigger for stop, price_effect for both. (Slippage is taken from your account setting, not the request body.)
  3. SPX option tick rules — single-leg < $3 → $0.05, ≥ $3 → $0.10, multi-leg → $0.05.
  4. A limit price can't exceed the structural maximum profit of the legs.
  5. Market data must exist for every leg at the current time.
  6. A stop can't already be triggered.
  7. Available-to-close: a to close leg can't exceed the quantity you hold (insufficient quantity to close).
  8. Defined-risk only: the resulting position can't hold an uncovered short option (naked short positions are not allowed) — checked before buying power, so a naked-adding order returns this, not a buying-power error, regardless of capital.
  9. Buying-power check, including all existing pending orders.

Rejections are JSON {"message": …} on practice sessions. The naked-short and available-to-close checks read your resulting book (current filled + pending positions plus the new order's legs), so they also block stripping the long leg out of a spread you already hold.

Dry-run

POST /accounts/{id}/sessions/{sid}/orders/dry-run runs every validation and returns the same response (minus transactions) without persisting anything — no order, no trades, no history change. It returns 200 instead of 201. Use it for pre-trade confirmation; a successful dry-run strongly implies the real POST will succeed too. (On a live session the result is advisory — the market may move between the dry-run and the real place.)

Order status and fees

On a historical session, order status is computed from fill_datetime versus the session's current clock time — filled if the fill is at or before now, otherwise pending; rewind the clock and a filled order can revert to pending. On a live session, status is the persisted lifecycle state (pending, filled, canceled, expired, rejected) and never time-travels. Fees follow suit: a filled order reports the actual summed fees; a pending order shows a schedule-based estimate. See Fees.

Replace

curl -s -X PUT https://api.0dtespx.com/accounts/$ACCT/sessions/$SID/orders/$ORDER \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"price":"10.40","price_effect":"debit"}'

Only price and price_effect may change — type, legs, and underlying are fixed. On a live session this is an atomic cancel + replace. Replacing a non-limit order returns 400; replacing a non-pending order returns 409.

Cancel

curl -s -X DELETE https://api.0dtespx.com/accounts/$ACCT/sessions/$SID/orders/$ORDER -H "Authorization: $TOKEN"

Returns 204. On a practice session, deletion removes the order and its trades (including any settlement trades it affected) and recomputes history. On a live session it cancels the pending order; cancelling a non-pending order returns 409.

For today's session, see the live trading API — the same paths, with idempotency keys scoped per session.