Skip to main content
Beta: not yet enabled; the launch venues are Polymarket, predict.fun and Limitless (Kalshi comes later). Requests answer 404 with {“detail”: “Not Found”}, as an unregistered path does, until trading is switched on.
Keys granted trade place, list, read and cancel limit orders on their own user’s connected venue accounts, on the venues enabled for trading. Every order gets our id, ord_ and 24 hex digits, the moment we accept it.

Placing an order

Name the market by its universal_id, the same id every market-data route uses. The order goes to that market’s leg on venue, so moving the same order to another venue is one field:
The answer (201) is the order, with our id:
The same order on Limitless changes venue only: "venue":"limitless". There is no "venue":"best": the API does not choose a venue for you (a possible later addition, not built). You may also send the venue’s own market id, venue_market_id, instead of or beside universal_id. Sent together, both must name the same leg, else the order is refused with 422 invalid_request; an order is never re-routed. Every answer carries both ids, plus the leg’s title on the venue when we hold it.

Routes

Every route answers only for the key’s own account: another account’s order is 404 order_not_found. {id} is ord_ and 24 lowercase hex digits, else 422 invalid_request; an unknown query parameter is also 422, and client_order_id on GET /public/v1/orders is sent alone. These routes read our own record of the orders you placed through the API; GET /public/v1/account/orders?venue= is different: it is the venue’s own list of your account’s orders, however they were placed (a key also granted account:read).

The order

Prices are decimal strings from 0 to 1 of your outcome on every venue, and size is the whole contracts you sent.

The order’s lifecycle

  1. We accept the request and the order gets its id. Nothing has been sent yet.
  2. We send it to the venue once. Its answer gives open, partially_filled, filled or cancelled (201), or a refusal (4xx, nothing placed).
  3. If the venue’s answer is lost, the order is unknown (202): it may or may not exist. Do one of:
    • retry the same request with the same client_order_id, or
    • read GET /public/v1/orders/{id}.
    Either looks the order up at the venue (at most once every few seconds) and answers its state, or not_placed once we can prove it never arrived. We never send it a second time.
  4. An order on the book ends filled, partially_filled or cancelled. Reading it by its id (or by client_order_id) re-reads an open order at the venue, at most once every few seconds, and records new fills or its final state, including a cancel made on the venue’s own website. Between those reads, and when the venue cannot be read, the answer is our last recorded state, and updated_at is when it last changed. A final order is never re-read.
Some venues cannot prove that an order is absent (see the table below). There an order whose answer was lost can stay unknown for good: it is never reported not_placed. Check the order on the venue itself before you trade the same intent again under a new client_order_id.

Who can trade

Trading is granted by hand, per account, with its limits and the account holder’s jurisdiction. A trade key is refused at creation unless the account holds an active trade grant and the key’s ip_allowlist is:
  • non-empty;
  • made only of global (public internet) address ranges;
  • no wider than /24 for IPv4 or /48 for IPv6;
and its lifetime is at most 90 days. An example allowlist entry is 198.51.100.0/24. That range is reserved for documentation, so a real key needs your own server’s public range instead: a documentation, private or shared range is not global and is refused.

The request

Every field is required unless marked optional or a default is shown. Unknown fields are refused with 422 invalid_request, and nothing is ever coerced or rounded. Before any order is sent, its market leg is checked: a leg we cannot verify is the venue market you meant is refused with 409 venue_leg_unverified, and nothing is sent.

Venues

What differs by venue. A venue answers only once it is enabled for trading. Where an order can stay unknown, retry the same request with the same client_order_id or read it by its id (it is found once the venue lists it), or cancel it, which checks it first.

Idempotency

client_order_id makes an order idempotent: one order per id, ever, across all your keys.
  • The same request again returns the latest answer for that order with Idempotent-Replay: true: the first 201, or the 200 a later check produced, with the same id. It does so even after the market closed, the limits changed or trading was halted.
  • A different request with a used id is 409 idempotency_conflict.
  • While the first is still in flight the answer is 409 request_in_progress with Retry-After.
A refusal answers this request only; after any lost or unclear answer, retry with the same client_order_id.

Listing your orders

GET /public/v1/orders answers {"orders": [...], "next_cursor": ...}, newest first, from our own record: no venue is called, so each status is as of our last check. Read an order by its id for a fresh one, or GET /public/v1/account/orders?venue= for the venue’s own list. venue and status filter it; limit is 1 to 100 (default 50). Pass next_cursor back as cursor for the next page, with the same filters: a cursor from other filters or another account is 422 invalid_request. next_cursor is null on the last page.

Limits

Each account has a per-order and a daily limit on maximum loss: a buy can lose its price, a sell the rest of the dollar, times the size. Fees are not included: a fill’s cash outlay can exceed the counted amount by the venue’s fee. The daily limit counts submitted notional per UTC day: a cancelled, unknown or never-placed order still counts that day. Only a rejection by the venue, a 503 venue_unavailable answer for an order that never reached the venue, and the unfilled part of an ioc order give it back. Orders also have their own per-key rate limit; cancels and reads never count against it.

Cancelling

Cancel an order this account placed through the API by our id (DELETE /public/v1/orders/ord_6702291f8e1c2a0012ab34cd) or by yours (DELETE /public/v1/orders?client_order_id=s1-0001); the venue is the order’s own. Both answer the order with cancelled_size, the contracts this cancel removed. The order in the answer is the venue’s view after the cancel: cancelled, or partially_filled when part of it filled first, with filled_size saying how much. On Kalshi the venue answers only the removed amount, so there filled_size is our last recorded count. Cancels are never blocked by a halt, a revoked trade grant or the order rate, so you can always reduce risk. An order still unknown is checked at the venue first. Found, it is cancelled. Proven never placed, the answer is 200 with status: not_placed and nothing cancelled. Not yet proven, the answer is 409 request_in_progress with Retry-After, and nothing was cancelled. An order already final (filled, partially filled, cancelled or never placed) answers 200 with its state and cancelled_size "0", and nothing is sent to the venue.

Codes