Core concepts

Order lifecycle & statuses

Every order status, which ones are final, and how to track a trade to a final state

Trading calls return pending right away and finish in the background. You find out how a trade ended by polling, or by listening for webhooks and WebSocket events. Always keep going until you reach a terminal state.

Two levels of status

LevelWhereValues
TradeoverallState on GET /execution/statusOne state for the whole quote, across all its orders
Orderstatus on each orderOne status per venue order

A fill split across two venues is one trade with two orders.

Order statuses

StatusTerminalMeaning
pendingNoAccepted. Funds are being checked.
signingNoThe order is being signed.
pending_bridgeNoFunds are moving to the venue's chain.
submittingNoBeing sent to the venue.
submittedNoSent to the venue, waiting for confirmation.
openNoA limit order resting on the venue's book.
partially_filled_openNoA resting limit order with some fills and size left.
cancel_pendingNoCancel requested, waiting for the venue to confirm.
filledYesFully filled.
partial_fillYesPart filled. The rest will not fill. See partialFillReason.
failedYesFailed. GET /execution/status uses errorReason. GET /execution/orders uses errorMessage.
expiredYesTimed out, or a limit order reached its expiry.
cancelledYesCancelled by the user or the system.

A market order never shows open, partially_filled_open, or cancel_pending.

partialFillReason is an open set. Known values: venue_capacity, price_slipped, venue_cancelled, venue_expired, venue_closed, user_cancelled, timeout, clearinghouse_capacity, native_remainder_voided, bridge_shortfall.

bridge_shortfall means a bridge delivered less than quoted, so the order was downsized before submit. It replaces venue_capacity on a downsized market order that ends partial_fill. A downsized order the venue filled in full still ends filled, with no reason.

On a market order, remainingSizeRaw is set only on partial_fill (requested shares minus filled shares) and stays null on filled. Limit orders always carry it, 0 once complete.

stateDiagram-v2
    [*] --> pending
    pending --> signing
    pending --> pending_bridge
    pending --> submitting
    signing --> submitting
    pending_bridge --> submitting
    submitting --> submitted
    submitted --> filled
    submitted --> partial_fill
    submitted --> open: limit order
    open --> partially_filled_open
    open --> cancel_pending
    partially_filled_open --> cancel_pending
    open --> filled
    partially_filled_open --> filled
    partially_filled_open --> partial_fill
    cancel_pending --> cancelled
    cancel_pending --> filled
    pending --> failed
    submitting --> failed
    submitted --> failed
    pending_bridge --> expired
    open --> expired
    pending --> cancelled
    filled --> [*]
    partial_fill --> [*]
    failed --> [*]
    expired --> [*]
    cancelled --> [*]

The diagram shows the usual paths. Not every order visits every state, and a failure can happen from any in-flight state. Branch on the status you read, not on the path you expect.

Trade states

GET /execution/status rolls the orders up into overallState:

overallStateTerminalMeaning
createdNoAccepted, execution has not started.
routing, quoting, placing, confirmingNoIn progress.
filledYesEvery order filled.
partially_filledYesAt least one order filled or part filled, and at least one did not fill in full.
cancelledYesEvery order was cancelled.
expiredYesEvery order expired.
failedYesAnything else that ended without a fill.

Stop tracking the trade when terminal is true. It can stay false for a short time after every order is final while remaining execution steps finish. If you requested an app-paid referral, continue tracking that payout separately: its purpose: "referral_payout" signing request can appear after the trade is filled. See Referrals.

Poll a quote-based trade

Use this for POST /execution/fill, and for POST /execution/orders using the quoteId in its response.

SDK
let status = await client.getExecutionStatus({ quoteId });
// This loop tracks the trade. Track app-paid referrals separately if requested.
while (!status.terminal) {
  await new Promise((r) => setTimeout(r, status.pollAfterMs ?? 1000));
  status = await client.getExecutionStatus({ quoteId });
}
console.log(status.overallState, status.orders);

Key fields in the response:

FieldMeaning
terminaltrue once the trade is final. App-paid referral payouts can still need a signature.
overallStateThe trade state above.
pollAfterMsWait this long before the next poll. null when terminal.
orders[]Per order: orderId, venue, status, filledAmountRaw, actualSharesRaw, executionPriceRaw, quotedPriceRaw, txHash, errorReason.
steps[]Progress rows for a UI. Group by groupId and show label.
errorReasonWhy the trade failed, on GET /execution/status. The order list calls this errorMessage.
pendingSignatures[]Not self-custody only. An app-paid referral puts purpose: "referral_payout" here after the trade is filled. See Referrals.

filledAmountRaw and actualSharesRaw are 6-decimal integer strings: 1000000 is $1 or 1 share. quotedPriceRaw is a 0–1 decimal string such as "0.53", not a 6-decimal integer. executionPriceRaw is the effective filled USD per received/sold share, including fee effects already reflected in those quantities; it is omitted without usable fill quantities and never falls back to the quote. limitPriceRaw is the price-scaled integer: 530000 is 0.53. sizeRaw (listed as limitSizeRaw on GET /execution/orders) uses the share scale: 1000000 is 1 share. Do not read 530000 as dollars.

404 means no order is recorded for that quoteId for this user yet. Right after a fill request, the request may still be running, so a 404 is not final and should be retried. Do not get a new quote until Errors, retries & idempotency says to.

API reference: Get execution status.

Poll by order id or your own id

GET /execution/orders lists the user's orders. Filter it to one order:

FilterUse for
orderIdLimit orders, cancels, and any order you hold the id of
externalIdA direct order after a timeout, when you never saw the response
quoteIdEvery leg of a quote
status, orderType, venueMarketIdsLists and reconciliation
SDK
const orders = await client.getExecutionOrders({ orderId });

Get user orders.

Each row has status, filledAmountRaw, actualSharesRaw, executionPrice (not executionPriceRaw), txHash, and errorMessage. executionPrice is a 0–1 decimal string such as "0.53", equal to filledAmountRaw / actualSharesRaw. It includes a venue fee only where filledAmountRaw does: a sell's proceeds are net of fee, while most venues report a buy's fee separately in actualVenueFeeRaw. On venues that fold a buy's fee into its cost, a buy near 1 can read slightly above 1. On Hyperliquid it is the quote until the realized fill is recorded, shortly after filled. filledAmountRaw and actualSharesRaw stay 6-decimal integer strings (1000000 is $1 or 1 share).

On a Polymarket market order, filledAmountRaw on a buy is the collateral the order paid, before the venue fee in actualVenueFeeRaw. txHash is the Polygon transaction that settled the fill. Both are read from the fill's on-chain OrderFilled log, usually within a minute of filled. Until then, txHash holds the Polymarket order hash. A limit order keeps the order hash, since it can settle across several transactions.

Poll until status is terminal. A resting limit order stays open until it fills, is cancelled, or expires.

Push instead of polling

ChannelEventsNotes
Webhookstrades.placed, trades.filledServer-to-server. trades.filled carries status filled or partial_fill, and your externalId for direct orders. There is no webhook for a failed trade, so poll to catch failures.
WebSocketorder_eventFor a signed-in user in the browser. Use REST to backfill on page load and reconnect.

Payloads are small. Read the order from the API for amounts and prices.

Retries and idempotency: Errors, retries & idempotency.

On this page