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 open + partially_filled; "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,
"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 | 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 |
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 and runs the matching engine. Returns the created order, any fills, any orders cancelled by the match (e.g. fully consumed resting orders), and your updated balance.
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. |
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.
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 an 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; the new order is re-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 order. 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 |