Skip to main content

CLOB Compatibility

PolySimulator mirrors Polymarket’s real execution model:
  • All orders are limit orders — market orders are just limit orders with FOK time-in-force at marketable prices
  • BUY fills at best ask, SELL fills at best bid — not the midpoint. (Don’t confuse fills with quotes: GET /v1/price?side=BUY returns the best bid — your side of the book — while executions cross the spread. Same convention as live Polymarket.)
  • The price field is the worst-price limit — slippage protection built into the order, not a separate parameter
  • FOK (Fill-or-Kill) is the immediate-execution order type on this endpoint. Polymarket’s real CLOB also supports FAK (Fill-and-Kill); on PolySimulator, FAK lives on the PM-raw POST /v1/order path, and POST /v1/clob/order accepts GTC/FOK/GTD only (see below)
The POST /v1/clob/order endpoint mirrors Polymarket’s real CLOB API schema, enabling one-URL-swap migration from paper trading to live trading.

The Migration Promise

Change only the base URL and credentials to go live — the request and response schemas are identical.
Authentication difference: PolySimulator uses the X-API-Key header. Polymarket’s live CLOB requires L2 HMAC credentials (API key + secret + passphrase) derived from your wallet’s private key via py_clob_client. See the Live Migration Guide for full credential setup.

Request Schema

All numeric fields (price, size) must be strings. Both PolySimulator and Polymarket’s real CLOB API expect string-encoded decimals, e.g. "0.65" not 0.65. Using floats will be rejected.
order_type=IOC returns 400 {"error": "VALIDATION_FAILED", "code": "UNSUPPORTED_ORDER_TYPE", "message": "order_type=IOC not yet supported; use GTC or FOK"} on POST /v1/clob/order. The two trading paths have genuinely different time-in-force support: this CLOB path accepts GTC/FOK/GTD, while the PM-raw POST /v1/order path accepts PM’s full GTC/FOK/FAK/GTD set. Polymarket’s own CLOB enum is GTC/FOK/GTD/FAK — there is no IOC on Polymarket.

Response Schema

This is the PolySimulator CLOB-compat response. It is close to Polymarket’s real insert-order response but not byte-identical — the differences are spelled out below so a migrating bot doesn’t string-match on the wrong field.
Differences from Polymarket’s real POST /order response — port defensively:
  • transactID (single string) vs PM’s transactionsHashes / tradeIDs (arrays). PolySimulator returns transactID set to the order ID; Polymarket has no transactID field — it returns transactionsHashes: [] and tradeIDs: []. The PM-raw POST /v1/order path returns the PM-shape arrays.
  • takingAmount / makingAmount are human decimals here (e.g. "6.50", "10.0"); Polymarket returns 6-decimal fixed-point integer strings (e.g. "500000" for 0.50). Don’t divide ours by 1e6.
  • status enum here is matched / live / unmatched. Polymarket’s insert-order statuses are live / matched / delayed — PolySimulator never emits delayed, and uses unmatched (not a PM insert status) for cancelled/rejected orders.

Field Mapping

Fields like signature, salt, maker, and signer that are required for Polymarket’s blockchain settlement are accepted but ignored in virtual mode. This means you can develop your bot with (or without) these fields — either way works.
String vs Float numerics: Both PolySimulator and Polymarket require price and size as strings (e.g., "0.65" not 0.65). Always pass strings to ensure compatibility.

Public CLOB Read Endpoints

These endpoints mirror Polymarket’s public CLOB data API and require no authentication. They accept token_id (the CLOB outcome token) as query parameter.
/v1/prices-history is PM wire-compatible. It accepts PM’s required ?market= (token id; the ?token_id= alias also works), returns PM’s exact {"history": [{"t", "p"}]} envelope by default (p is a JSON number here, mirroring PM), supports startTs/endTs/fidelity, and 400s with PM’s verbatim error message when market is missing. For a bucketed OHLCV array of {t, o, h, l, c} points instead, pass ?format=ohlcv.
These endpoints serve the same cached prices as the authenticated API, refreshed roughly every 30 seconds.

Cancel Endpoints

Bulk cancel endpoints match Polymarket’s cancel response shape: {canceled: [...], not_canceled: {...}}. The cancel-market-orders endpoint accepts either market (condition_id) or asset_id (token_id) as query parameters.
The status word inside a not_canceled reason is the internal uppercase order-status enum (FILLED, CANCELLED, EXPIRED), which differs from the lowercase insert-order status the POST /v1/clob/order response uses (matched / live / unmatched). If you string-match on the status you saw at insert time, don’t expect the casing to line up here — match case-insensitively, or map FILLED → matched, CANCELLED → unmatched.

When to Use CLOB-Compat vs Native API


Next Steps