Orders
All order endpoints require authentication. See Authentication.
Rate limit: v1-auth: 120 req / 60 s per IP
Sell-order inversion. Internally, a YES sell at price P is stored as a NO buy at price 100 - P. The API always returns orders in the user-facing form you submitted: side and price reflect what you intended, not what is stored.
GET /api/v1/orders
Lists your orders. Up to 200 orders are returned per call (no pagination cursor; use status to narrow the result).
Auth: Required
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
marketId | UUID | — | Filter to a specific market |
status | string | "open" | "open" returns scheduled + open + partially_filled; "scheduled" returns only live-delay orders; "filled" returns fully filled; "cancelled" returns cancelled; "all" returns all statuses |
Response
{
"data": [
{
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"marketTitle": "Will Reform Party win the most seats?",
"side": "yes",
"price": 55,
"quantity": 100,
"filledQuantity": 40,
"status": "partially_filled",
"orderType": "buy",
"cancelReason": null,
"expiresAt": null,
"releaseAt": null,
"releaseInMs": null,
"budgetCents": null,
"createdAt": "2026-01-10T09:00:00.000Z",
"sharesAhead": 230
}
],
"meta": { "pageSize": 1 }
}| Field | Type | Description |
|---|---|---|
id | UUID string | Order identifier |
userId | UUID string | Your user id |
marketId | UUID string | Market the order is on |
marketTitle | string | Market title (for display) |
side | "yes" | "no" | Side you are buying or selling |
price | integer | Your order price (1–99) in user-facing form |
quantity | integer | Total shares ordered |
filledQuantity | integer | Shares already matched |
status | string | scheduled | open | partially_filled | filled | cancelled |
orderType | "buy" | "sell" | Order type |
cancelReason | string | null | Why it was cancelled; null if not cancelled |
expiresAt | ISO 8601 | null | Expiry time; null = Good Until Cancelled |
releaseAt | ISO 8601 | null | When a scheduled live order becomes eligible for matching; null for orders that were never scheduled |
releaseInMs | integer | null | Remaining live delay computed by the server. Use this duration for countdowns instead of subtracting client wall-clock time from releaseAt |
budgetCents | integer | null | Persisted budget for a scheduled budget buy; null for quantity orders and after release |
createdAt | ISO 8601 | When the order was placed |
sharesAhead | integer | Shares ahead of this order in the queue |
POST /api/v1/orders
Places a new order. On a non-live market, the matching engine runs before the response. On a live event market, the server accepts the order as scheduled and delays matching by the configured live-order delay (normally 3 seconds).
Auth: Required
Request body
{
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 55,
"quantity": 100,
"orderType": "buy"
}| Field | Type | Required | Description |
|---|---|---|---|
marketId | UUID | Yes | Target market |
side | "yes" | "no" | Yes | Side to buy or sell |
price | integer 1–99 | null | Conditional | Limit price. Required for buy orders without budgetCents. For market-sell orders set to null. |
quantity | integer 1–10000 | Conditional | Shares to trade. Required for sell orders and buy orders without budgetCents. |
budgetCents | integer > 0 | Conditional | EUR-cent budget for market-buy orders. Mutually exclusive with quantity. |
orderType | "buy" | "sell" | No | Default "buy" |
idempotencyKey | UUID | No | Supply the same UUID to safely retry without double-placing. Valid for 24 hours. |
expiresAt | ISO 8601 datetime | No | When the order auto-cancels. Must be in the future. Omit for Good Till Cancelled. |
postOnly | boolean | No | Default false. When true, reject rather than execute as a taker if the order would cross at matching time. Incompatible with budget buys and market sells. |
Buy orders: supply exactly one of quantity or budgetCents.
Sell orders: supply quantity. Set price = null for a market sell (fills at any available bid).
Response
{
"order": {
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 55,
"quantity": 100,
"filledQuantity": 20,
"status": "partially_filled",
"orderType": "buy",
"cancelReason": null,
"expiresAt": null,
"createdAt": "2026-01-15T14:32:00.000Z"
},
"fills": [
{
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"buyOrderId": "d4e5f6a7-b8c9-0123-def0-234567890123",
"sellOrderId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"buyerId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"sellerId": "f6a7b8c9-d0e1-2345-f012-456789012345",
"price": 55,
"quantity": 20,
"createdAt": "2026-01-15T14:32:00.000Z"
}
],
"cancelledOrders": [],
"balance": {
"total": 987600,
"available": 987600
},
"feeCents": 110
}| Field | Type | Description |
|---|---|---|
order | object | The placed order in user-facing form |
fills | array | Trades generated by this order (may be empty if no matching liquidity) |
cancelledOrders | array | Orders cancelled as a side effect of this match (e.g. resting orders cancelled due to insufficient funds, expired orders, or overextended sell positions) |
balance.total | integer | Your EUR-cent balance after the transaction |
balance.available | integer | Currently always equal to total; reserved for a future margin model |
feeCents | integer | null | Taker fee charged for this order, in EUR-cents. 0 if nothing filled. null on idempotent replay. See Fees. |
balance.total already reflects the fee, so for a buy: spent = price × quantity + feeCents.
Live-market scheduled response
On a live event market, 201 means the order was accepted, not filled. The response has no fills or fee, the balance is unchanged, and the pending order is not visible in public depth:
{
"order": {
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 55,
"quantity": 100,
"filledQuantity": 0,
"status": "scheduled",
"orderType": "buy",
"cancelReason": null,
"expiresAt": null,
"releaseAt": "2026-01-15T14:32:03.000Z",
"releaseInMs": 3000,
"budgetCents": null,
"createdAt": "2026-01-15T14:32:00.000Z"
},
"fills": [],
"cancelledOrders": [],
"balance": {
"total": 987600,
"available": 987600
},
"feeCents": null
}At releaseAt, the engine validates the order again and matches it against the current book, not the submission-time snapshot. Limit-price protection still applies. The final status may be open, partially_filled, filled, or cancelled; release-time cancellation reasons include changed funds/position, market closure, trading pause, self-trade, post-only crossing, responsible-gaming limits, and insufficient liquidity for a budget buy.
Connect to GET /api/v1/user/stream for order_pending and order_released events, or poll this endpoint with status=scheduled/status=all. Cancelling a scheduled order is immediate.
An idempotent retry returns the original order’s authoritative current status. While it is still scheduled, releaseInMs is the actual remaining time; retrying does not restart the delay or emit another order_pending event.
Status code: 201 on new order, 200 on idempotent replay.
Errors
| Status | Body | When |
|---|---|---|
400 | { "error": "...", "code": "INSUFFICIENT_BALANCE" } | Not enough balance or position |
400 | { "error": "...", "code": "INSUFFICIENT_LIQUIDITY" } | Budget buy with no matching liquidity |
400 | { "error": "...", "code": "SELF_TRADE_BLOCKED" } | The order would match your own resting order on the opposite side. Cancel or amend that order first. |
400 | { "error": "Market is not open for trading" } | Market status is not open |
400 | { "error": "Market not found" } | Unknown marketId |
503 | { "error": "...", "code": "CONFLICT_RETRY" } | Transient lock conflict. Safe to retry. |
PATCH /api/v1/orders/:id
Amends a scheduled, open, or partially-filled order. You can change price, quantity, or both.
Auth: Required
Path parameters
| Param | Type | Description |
|---|---|---|
id | UUID | Order to amend (must belong to you) |
Request body
Supply at least one field:
{
"price": 60,
"quantity": 80
}| Field | Type | Description |
|---|---|---|
price | integer 1–99 | New limit price in user-facing form |
quantity | integer 1–10000 | New total quantity (must be ≥ filled quantity) |
Amendment rules:
- Quantity decrease only: amended in place, queue position preserved.
- Price change or quantity increase: cancel-replace, where the resting order is cancelled and a new order is placed at the new price/quantity. Queue position is lost; outside the live-delay path, the new order is re-matched immediately.
- Any change to a scheduled order: cancel-replace and start a fresh live-delay window.
- Live-market reprice or quantity increase: the replacement becomes
scheduled; it is not matched immediately.
Response
Quantity decrease only (in-place, queue position preserved):
{
"order": {
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 55,
"quantity": 80,
"filledQuantity": 40,
"status": "partially_filled",
"orderType": "buy",
"cancelReason": null,
"expiresAt": null,
"createdAt": "2026-01-10T09:00:00.000Z"
},
"amended": true,
"queuePositionChanged": false
}Price change or quantity increase (cancel-replace, queue position lost):
{
"order": {
"id": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 60,
"quantity": 80,
"filledQuantity": 0,
"status": "open",
"orderType": "buy",
"cancelReason": null,
"expiresAt": null,
"createdAt": "2026-01-10T09:15:00.000Z"
},
"fills": [],
"amended": true,
"queuePositionChanged": true,
"cancelledOrderId": "d4e5f6a7-b8c9-0123-def0-234567890123",
"feeCents": 0
}| Field | Type | Description |
|---|---|---|
order | object | Updated order |
amended | boolean | false if nothing changed |
queuePositionChanged | boolean | true when cancel-replace was used |
cancelledOrderId | UUID | undefined | Id of the cancelled order on cancel-replace |
fills | array | undefined | New fills generated by the replacement order |
feeCents | integer | undefined | Taker fee charged for the replacement order, in EUR-cents. Present only on cancel-replace; 0 if it didn’t fill. See Fees. |
Errors
| Status | Body | When |
|---|---|---|
400 | { "error": "Cannot amend order with status: ..." } | Order is not open or partially_filled |
400 | { "error": "Cannot reduce quantity below filled amount (N)" } | New quantity < filled quantity |
400 | { "error": "Market is not open for trading" } | Market closed during amendment |
400 | { "error": "...", "code": "INSUFFICIENT_BALANCE" } | Insufficient funds for price/quantity increase |
404 | { "error": "Order not found" } | Unknown id or not yours |
503 | { "error": "...", "code": "CONFLICT_RETRY" } | Transient lock conflict. Safe to retry. |
POST /api/v1/orders/cancel-batch
Cancels up to 100 orders in one call. Each cancel runs in its own transaction, so partial success is preserved. One bad id does not roll back the rest.
Auth: Required
Request body
{
"orderIds": [
"d4e5f6a7-b8c9-0123-def0-234567890123",
"e5f6a7b8-c9d0-1234-ef01-345678901234"
]
}| Field | Type | Required | Description |
|---|---|---|---|
orderIds | UUID[] | Yes | 1–100 order ids to cancel. Duplicates are deduplicated. |
Response
{
"cancelled": [
{
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 55,
"quantity": 100,
"filledQuantity": 40,
"status": "cancelled",
"orderType": "buy",
"cancelReason": "user_cancelled",
"expiresAt": null,
"createdAt": "2026-01-10T09:00:00.000Z"
}
],
"failures": [
{ "orderId": "e5f6a7b8-c9d0-1234-ef01-345678901234", "code": "not_cancellable" }
],
"balance": {
"total": 992400,
"available": 992400
}
}| Field | Type | Description |
|---|---|---|
cancelled | array | Orders cancelled by this call, in user-facing form |
failures | array | One entry per id that could not be cancelled |
failures[].orderId | UUID string | The id that failed |
failures[].code | string | not_found (unknown id or not yours) | not_cancellable (already filled or cancelled) | internal (unexpected server error) |
balance | object | null | Balance after the batch; null if the post-cancel balance refresh failed |
Status code: 200, including when every id is in failures.
DELETE /api/v1/orders/:id
Cancels a resting or scheduled order. Cancellation is immediate and never waits for the live-order delay. Open buy orders do not reserve balance, so the cancel leaves your balance unchanged. Balance changes only when an order fills.
Auth: Required
Path parameters
| Param | Type | Description |
|---|---|---|
id | UUID | Order to cancel (must belong to you) |
Response
{
"order": {
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"side": "yes",
"price": 55,
"quantity": 100,
"filledQuantity": 40,
"status": "cancelled",
"orderType": "buy",
"cancelReason": "user_cancelled",
"expiresAt": null,
"createdAt": "2026-01-10T09:00:00.000Z"
},
"balance": {
"total": 992400,
"available": 992400
}
}Errors
| Status | Body | When |
|---|---|---|
400 | { "error": "Cannot cancel order with status: ..." } | Order already filled or cancelled |
404 | { "error": "Order not found" } | Unknown id or not yours |