Skip to main content

Placing Orders

Place a market or limit order. Requires trade permission.
Polymarket-compatible execution model: On Polymarket, all orders are limit orders — “market orders” are just limit orders with FOK time-in-force at a marketable price. PolySimulator mirrors this exactly: market orders fill at the best available price (BUY at best ask, SELL at best bid), and the price field acts as a required worst-price limit for slippage protection.

Market Orders

Executed immediately at the best available price — BUY at the best ask, SELL at the best bid — matching how Polymarket fills market orders.

Worst-Price Limit (Required)

The price field is required on market orders and sets the worst price you’ll accept. This is identical to Polymarket — there are no “blind” market orders.
  • BUY: the order won’t fill above your price (you won’t overpay)
  • SELL: the order won’t fill below your price (you won’t undersell)

FAK (Fill-and-Kill) Market Order

time_in_force: "FAK" (alias "IOC") is accepted. Engine behavior depends on the PM-faithful order semantics rollout (see the callout below):
  • Legacy behavior (rollout off): FAK executes atomically — it fills your full quantity at the touch price or cancels entirely.
  • Rolling out: Polymarket-faithful partial fills — the engine walks the displayed order-book depth within your price limit, fills what’s actually available (VWAP across levels), and cancels the remainder. A partial fill returns status: "FILLED" with quantity set to the filled slice and a partial_fill:… entry in warnings naming filled vs requested size.
Polymarket migration tip: FAK and IOC are equivalent in PolySimulator. If your Polymarket bot uses FAK, it works here unchanged. If you omit time_in_force, market orders default to FOK (Fill-or-Kill) — the entire order fills or is cancelled.

Rolling out: PM-faithful order semantics. A flag-gated engine upgrade is rolling out that brings seven behaviors in line with real Polymarket. While the rollout completes, behavior depends on the deployment flag — the legacy behavior is documented alongside each. Once fully live:
  1. FAK/IOC partial fills — fill what’s available within your limit against displayed depth (VWAP across levels), cancel the remainder.
  2. FOK depth-aware atomicity — fill entirely iff displayed depth within your limit covers the full size, else kill cleanly (no more full-size fills at the touch price beyond displayed depth). One tolerance: a shortfall of ≤ 1 share vs displayed depth is absorbed and the order fills in full at the walked VWAP — this smooths sub-share rounding on Polymarket’s 1e6 token grid and matches the engine’s general depth-walk dust rule.
  3. GTD (Good-Til-Date) — resting limits carrying a unix-seconds expiration; auto-cancelled by the engine once the timestamp passes. Expired-at-placement is rejected with INVALID_ORDER_EXPIRATION. Auto-expiry is guaranteed for expirations up to ~83 days out; beyond that horizon the order rests like a GTC and you should cancel it explicitly (Polymarket itself accepts far-future GTDs, so we don’t reject them).
  4. post_only — guaranteed-maker orders: rejected with INVALID_POST_ONLY_ORDER if marketable at placement (GTC/GTD only; INVALID_POST_ONLY_ORDER_TYPE when combined with FOK/FAK/IOC/market).
  5. min_order_size — the per-market minimum share count that GET /v1/book advertises is enforced at placement (INVALID_ORDER_MIN_SIZE, X-Polysim-Code: ORDER_BELOW_MIN_SIZE).
  6. Book-impact depletion — limit-order fills consume the displayed liquidity they walk. Within one upstream order-book snapshot, size already consumed at a price level by limit executions (your fills or anyone else’s) is not offered again; once the level is exhausted the walk moves to worse levels within your limit, partial-fills, or (FOK) kills. A fresh upstream snapshot restores the real displayed book. Market-order fills don’t yet feed or read the overlay — wiring them in is a planned follow-up. Legacy: the same displayed size could be bought repeatedly while the snapshot stayed cached. Your simulated fills never alter the displayed book or midpoint — depletion is execution-side only.
  7. Resting maker orders fill at their limit price — Polymarket’s maker/taker rule: price improvement belongs to the taker. A resting GTC/GTD limit that the market later crosses executes at your limit price (BUY reserve = spend, zero refund delta). Marketable orders (FOK/FAK/IOC, and limits that cross immediately at placement) remain takers and keep their depth-walk price improvement. Legacy: resting orders filled at the later touch/VWAP price — systematically better than the maker’s own limit.
Watch the changelog for the activation announcement.

Limit Orders

Queued and filled by the background matching engine (~1s polling cycle).

GTC (Good-Til-Cancel)

Persists until filled, cancelled by the user, or the market closes.

FOK (Fill-or-Kill)

All-or-nothing immediate fill — matches Polymarket’s FOK order type. If the full quantity can’t fill at the limit price, the entire order is cancelled. With the PM-faithful semantics rollout (see the callout above), FOK is depth-aware: the engine computes the fillable quantity within your limit across displayed book levels first, fills the whole order at the walked VWAP iff depth covers your size, and kills cleanly otherwise. (Legacy behavior filled the full size at the touch price whenever the top of book crossed.)

IOC (Immediate-or-Cancel)

Evaluated synchronously, in the request against the cached order book — it fills immediately or is cancelled before the response returns; it never rests on the book or waits for the background matching cycle. (FOK behaves the same way. The ~1s polling cycle applies only to resting GTC limits.)
When to use IOC: Sniping a specific price level without the risk of stale orders sitting in the book. If the current market price is worse than your limit, the order cancels immediately.
With the PM-faithful semantics rollout (see the callout above), IOC limit orders partial-fill like on real Polymarket: the available size within your limit fills at the depth-walk VWAP and the unfilled remainder is cancelled + refunded in the same request. (Legacy behavior was atomic full-fill-or-cancel.) Fill conditions:
  • BUY limit: Fills when market ask ≤ your limit price. Funds reserved upfront.
  • SELL limit: Fills when market bid ≥ your limit price. Shares reserved upfront.

Request Fields

quantity (shares) vs amount (USD) — Polymarket parity

Polymarket’s BUY market-order convention uses amount (USD) — the dollar amount you want to spend. SELL and limit orders use share counts. PolySimulator accepts either field, with these rules:
  • quantity is shares for both BUY and SELL (the polysim-native convention). Always usable.
  • amount is USD for BUY market orders only. Sending amount=5 with side=BUY, order_type=market means “spend up to $5”; the handler derives quantity = floor4(amount / price) — using your worst-price limit (the price field, not the eventual fill price) and rounding down to the matching engine’s 4-decimal share quantum. Sending amount with side=SELL or order_type=limit returns 400.
  • Sending both quantity and amount returns 400 with "Specify either quantity or amount, not both".
Bots ported from Polymarket’s SDK should keep using amount for BUY market orders unchanged. Bots written for PolySimulator can keep using quantity for everything.

Time in Force


Response

fee is a real per-category taker fee — not zero. Every taker fill is charged Polymarket’s V2 per-category schedule, and the charged amount is price-dependent (it peaks near 0.50andshrinkstowardthepriceextremes).Theexampleaboveisa10shareBUYat0.50 and shrinks toward the price extremes). The example above is a 10-share BUY at 0.65 on a crypto market, returned as 0.16. Always read OrderResponse.fee when computing realized PnL — see the canonical formula and full rate table on Trading Fees. Discover a market’s rate programmatically via GET /v1/fee-rate?token_id=… and read its fee_rate_bps field (the effective category rate in bps).

Fill Quality Telemetry

Every market order returns six fields HFT bots can use to score fill quality, detect stale-quote fills, and decide whether to flatten the position on the next tick.

price_source — common values

Today the engine emits the labels below. Some get a transport prefix (e.g. ws: when the underlying price snapshot arrived over a streaming source rather than a REST poll) — strip prefixes before string comparison, or just log the raw label and grep on substring. Bots should treat the clob_midpoint_* and cached-display-price paths as lower-confidence and the emergency-exit paths as informational-only.
Idempotent-replay caveat: a duplicate POST with the same Idempotency-Key/client_order_id returns the original order envelope. price_source, slippage_bps, and quote_age_ms rehydrate from the persisted order row, but spread_bps, impact_bps, and book_walk_levels are only available on the first response — they return null on replay. Persist them on first receipt if your bot needs them.

Suggested latency budget for HFT bots

A round-trip-aware budget for an UpDown 5-minute bot trading 1 contract clip: Breach any threshold → close the position on the next bar rather than hold it.

Idempotency

Use the Idempotency-Key header or client_order_id field to prevent duplicate executions on retries:
If the same key is submitted twice, the second request returns the result of the first execution without creating a new order.
Include a timestamp or sequence number in your idempotency key to make debugging easier: "bot-alpha-20260206-001"

Error Handling

For the full, canonical trading error-code list (and the order-status enum) see Error Handling. The codes most relevant to order placement:

Next Steps