Market Orders (single-contract trades)

A market order is a single-contract order at the best available price. There are three endpoints: estimate (no commit), submit (asynchronous fill), and a state read. The submit returns immediately with a refId, and you either poll or subscribe to webhooks to find out what happens next.

Auth: user-scoped on all three (sub must equal the acting user's UUID, plus subsig). See Authentication.

For multi-leg orders, see Parlays (2–12 leg trades) instead.


1. The flow

POST /private/v1/market-orders/estimate    ─►  see expected price + fee + available qty
POST /private/v1/market-orders             ─►  202 with refId
GET  /private/v1/market-orders/{refId}     ─►  poll status, fill progress, P/L

Polling is the simplest option. For lower-latency updates without polling, subscribe to MarketOrderEvent webhooks — see Webhooks (push events).


2. The state model

FieldValues
statuspendingcompleted | canceled | failed
fillStatusopenpartialfilled
winningStatustbdprofit | loss | push (after the event settles)

Small naming inconsistency to be aware of: in webhook payloads, fillStatus uses resting instead of open for the same state. See Webhooks (push events). Treat them as synonyms.


3. POST /private/v1/market-orders/estimate

A no-commit quote. Use this to show the user what they'd get before they confirm.

Body (EstimateMarketOrderRequest):

{ "contractId": "183c6ffeb2b4b6772a5afb6336293240", "quantity": 25.00 }
  • quantity >= 0 allowed here. Use 0 to see the best available price without specifying a size.

Response (EstimateMarketOrderResponse):

{
  "availableQuantity": 50.00,
  "expectedAveragePrice": -110,
  "expectedAveragePriceAdjusted": -115,
  "expectedPayout": 45.45,
  "expectedPayoutAdjusted": 43.10,
  "expectedFeeAmount": 1.50,
  "maxQuantity": 200.00
}

Field meanings:

  • availableQuantity — fillable at the expected average price.
  • maxQuantity — total liquidity across all price levels. Taking the whole thing would mean a worse blended price.
  • Prices are in American format (-110, +150).
  • The *Adjusted variants include the ISV fee.

The common failures are 422 with INVALID_CONTRACT (the contract isn't valid or has gone stale) or with an insufficient-liquidity error.


4. POST /private/v1/market-orders

Submit the order. Asynchronous — the server returns 202 immediately with a reference ID, and the actual fill happens in the background.

Body (SubmitMarketOrderRequest):

{ "contractId": "183c6ffeb2b4b6772a5afb6336293240", "quantity": 25.00 }
  • quantity > 0 required (strict — 0 is allowed for estimate but not for submit).

Response (202, MarketOrderResponse): same shape as the GET below, with initial status: "pending", fillStatus: "open", filledQuantity: 0.

The refId is not a UUID — it's a string, possibly prefixed with isv_. Treat it as opaque.

Common failure modes:

  • 402 PaymentRequiredError — insufficient balance (relevant on INDIVIDUAL ISVs; SHARED ISVs see this when the pot is constrained).
  • 422 INVALID_CONTRACT — unknown or expired contract.
  • 422 user_pending_kyc — the user hasn't passed KYC yet.

5. GET /private/v1/market-orders/{refId}

Response (MarketOrderResponse):

{
  "refId": "isv_xyz",
  "contractId": "183c6ffeb2b4b6772a5afb6336293240",
  "requestedQuantity": 25.00,
  "fee": 0.50,
  "quantity": 24.50,
  "filledQuantity": 24.50,
  "openQuantity": 0,
  "expectedAveragePrice": -110,
  "currentAveragePrice": -108,
  "status": "completed",
  "fillStatus": "filled",
  "winningStatus": "tbd",
  "createdAt": "...",
  "updatedAt": "..."
}

Read these in pairs:

  • requestedQuantity is what you asked for; fee is what was taken; quantity = requestedQuantity - fee is what actually went on the order.
  • filledQuantity + openQuantity = quantity.
  • expectedAveragePrice is the floor at submission; currentAveragePrice is the actual achieved price so far.

6. Polling guidance

A few notes to save you time:

  • Don't tight-loop. A 1–2 second interval during active fill and longer once status stabilizes is plenty.
  • If you'd rather not poll at all, subscribe to the /push/market-orders webhook and let ProphetX push updates. Each event is wrapped in { id, op, timestamp, data } — see Webhooks (push events).
  • A terminal status (completed / canceled / failed) means no further fills. winningStatus may still flip later when the event settles — that comes through the ContractSettlementEvent webhook.

7. Curl

BASE="https://isv-staging-api.betprophet.co/private/v1"

# 1. Estimate
curl -X POST "$BASE/market-orders/estimate" \
  -H "Authorization: Bearer $JWT_USER" \
  -H "Content-Type: application/json" \
  -d '{ "contractId":"abc...", "quantity":25 }'

# 2. Submit
curl -X POST "$BASE/market-orders" \
  -H "Authorization: Bearer $JWT_USER" \
  -H "Content-Type: application/json" \
  -d '{ "contractId":"abc...", "quantity":25 }'
# → 202 { "refId":"isv_xyz", "status":"pending", ... }

# 3. Poll (or wait for the webhook)
curl "$BASE/market-orders/isv_xyz" -H "Authorization: Bearer $JWT_USER"