> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polysimulator.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> API and documentation changes, including breaking changes shipped during the beta.

<Note>
  **Beta notice.** During the closed beta, breaking changes may ship with little or no
  advance notice — this page is the authoritative record. Pin your integration to the
  behaviors documented here and re-check after the dates listed. A formal versioning
  and deprecation policy will be published when the API leaves beta.
</Note>

## 2026-06-20 — Python SDK `0.4.2` (current)

The [`polysimulator`](https://pypi.org/project/polysimulator/) SDK is now at **`0.4.2`** —
`pip install polysimulator`. Hardening across the `0.4.x` beta line: **Python 3.10+
support**, faithful market-order time-in-force (`FOK` / `FAK` preserved through the
default post path), accurate batch-order result mapping, and Decimal-precise money
handling — plus a `retry_on_market_warmup` helper and the `condition_id:UP` / `:DOWN`
shorthand for the Up/Down drop-in. The three import surfaces are unchanged:
`polysim_sdk` (native), `polysim_clob_client` (`py-clob-client` drop-in), and
`polysim_polymarket` (py-sdk mirror). See the [SDK overview](/sdk-polymarket).

## 2026-06-18 — Python SDK `0.4.0`: Polymarket py-sdk mirror (`polysim_polymarket`)

The [`polysimulator`](https://pypi.org/project/polysimulator/) package ships a
**third import surface** in `0.4.0`: `polysim_polymarket`, a **paper-mode mirror**
of Polymarket's newer unified py-sdk
([`polymarket-client`](https://pypi.org/project/polymarket-client/), import
`polymarket`), pinned to **`0.1.0b8`**.

* **Sync + async `PublicClient` / `SecureClient`**, returning typed pydantic models
  (`OrderBook`, `Market`, `LastTradePrice`, …) instead of raw dicts. CLOB market-data
  reads are **full parity**; the authenticated **trading** write surface, account/auth
  reads, on-chain methods (paper no-ops), and the **core realtime streams** (`market` /
  `user` / `crypto_prices`) are implemented with documented paper-mode seams.
* **Port = the same mechanical swap as the v1 drop-in** — change the import **prefix**
  (`polymarket` → `polysim_polymarket`), the **host**, and the **auth** (drop the
  private key + EIP-712 signing for a single `ps_live_…` key). The one deliberate
  divergence: the named errors subclass `PolyException` here, not py-sdk's
  `PolymarketError`.
* **One install ships all three surfaces** — `polysim_sdk` (native),
  `polysim_clob_client` (`py-clob-client` v1 drop-in), and `polysim_polymarket`
  (this py-sdk mirror).

See the [py-sdk Mirror overview](/sdk-polymarket) and the
[py-sdk Compatibility](/concepts/py-sdk-compatibility) deep reference (sim→real seam
contract, compat matrix, fidelity gaps, stream seams) for the full surface and every
documented paper-mode seam.

## 2026-06-16 — Dollar sizing, market-order slippage, GTD + post-only (live)

Three execution / telemetry improvements surfaced by a near-HFT SDK developer
review, now live in production.

* **Dollar-denominated market BUYs spend the full amount.** A market BUY priced
  by `amount` (USD) now sizes to spend \~the full `amount` at the displayed price
  (Polymarket's "spend up to `amount`" semantics), instead of the conservative
  `amount / worst-price-limit` that under-bought — a `$50` BUY had been buying
  only \~`$12` of shares. The `price` field still caps execution: a fill that
  would cross your worst-price limit kills rather than overspending. *Legacy:
  `amount` was sized off the worst-price cap, under-buying \~4×.*
* **Accurate `slippage_bps` on market orders.** A market order's `slippage_bps`
  is now measured against the displayed price you saw at submit, not the
  worst-price cap — a clean fill at the touch now reports a small, true value
  (\~tens of bps), and the `high_slippage_bps_*` warning no longer fires on every
  clean market fill. `impact_bps` (the book-walk cost from top-of-book) is
  unchanged and remains the authoritative depth-impact field. *Legacy: slippage
  was measured vs the cap, reporting price improvement as \~7500 bps of "slippage"
  plus a false high-slippage warning on essentially every clean fill.*
* **`GTD` (Good-Til-Date) and `post_only` orders.** `time_in_force=GTD` (a
  resting limit that auto-expires at its `expiration` timestamp) and
  `post_only=true` (maker-only — rejected if it would cross at placement) are now
  accepted, matching `py-clob-client`. Part of the PM-faithful order-semantics
  rollout (same flag as the entries below). *Legacy: `GTD` returned `422`;
  `post_only` was advisory.*

## 2026-06-11 — Realistic book impact + maker price fidelity (rolling out)

Two further matching-engine fidelity upgrades, **flag-dependent** — they ship
behind the same deployment flag as the "PM-faithful order semantics" entry
below and activate together (announced here). Until activation, the legacy
behaviors remain in force.

* **Book-impact depletion.** Limit-order fills now 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: the depth walk subtracts prior consumption
  before pricing, so repeated orders walk to worse levels within your
  limit, partial-fill, or (FOK) kill — instead of repeatedly buying the
  same displayed shares while the snapshot stayed cached. A fresh upstream
  snapshot supersedes the synthetic depletion (real book truth wins; the
  safety TTL is sized to outlive the snapshot cache window). Applies to
  both resting-limit matching and immediate FOK/FAK/IOC execution.
  **Market-order fills don't yet feed or read the depletion overlay** —
  that path re-resolves fresher books per request; wiring it into the same
  overlay is a documented follow-up. Simulated fills still never move the
  *displayed* book or midpoint — depletion is execution-side only.
  *Legacy: the same displayed level was re-buyable every cycle.*
* **Resting maker orders fill at their limit price.** Polymarket's
  maker/taker rule — price improvement always benefits the taker. A resting
  GTC/GTD limit the market later crosses now executes at the maker's own
  limit price (for BUYs the reserve equals the spend — zero refund delta;
  maker fee remains \$0). Marketable orders (FOK/FAK/IOC and limits that
  cross at placement) are takers and keep their depth-walk price
  improvement, exactly as before. *Legacy: resting orders filled at the
  later touch/VWAP price — systematically better than the maker's own
  limit, which inflated PnL.*

## 2026-06-11 — PM-compat P1 batch (pagination, status enums, prices-history, neg-risk, cursors, full book, WS fills)

The second wire-parity wave, driven by the 2026-06-10 API evaluation's confirmed
P1 findings. Every item below was verified against live `clob.polymarket.com`
probes and/or Polymarket's API reference before changing.

### Breaking changes

* **`GET /v1/data/orders` status enum now uses Polymarket's exact members.**
  `ORDER_STATUS_LIVE` (was the invented `ORDER_STATUS_PENDING`),
  `ORDER_STATUS_MATCHED` (was `ORDER_STATUS_FILLED`), `ORDER_STATUS_CANCELED`
  — single L, per PM — (was `ORDER_STATUS_CANCELLED`), `ORDER_STATUS_INVALID`
  (was `ORDER_STATUS_REJECTED`). Bots string-matching the old values must
  update; the `?status=` filter accepts both spellings.
* **`GET /v1/data/orders` defaults to OPEN orders only** — matching PM
  ("Retrieves open orders for the authenticated user"). History is an explicit
  opt-in via the new polysim-extension `?status=` param
  (`ORDER_STATUS_MATCHED`, `ORDER_STATUS_CANCELED`, `ALL`, friendly aliases).
  Previously every status came back, so open-order counts and cancel-all
  sweeps operated on dead rows.
* **`GET /v1/prices-history` returns PM's exact shape by default**:
  `{"history": [{"t": <unix int>, "p": <float>}]}` — `p` is a JSON number
  here, mirroring PM's wire. The old default (a bare array of
  `{t, o, h, l, c}` string points) remains available via `?format=ohlcv`.
  The endpoint also accepts PM's required `?market=` param (token id;
  `?token_id=` stays as an alias), supports `startTs`/`endTs`/`fidelity` and
  PM's full interval enum (`1h/6h/1d/1w/1m/max/all`), and 400s (not 422s)
  with PM's verbatim message when `market` is missing.
* **`GET /v1/orders` `next_cursor` is now urlsafe-base64.** The raw ISO form
  contained `+`, which broke un-percent-encoded round-trips with a 400.
  Treat cursors as opaque; legacy raw-ISO (and the `+`-mangled-to-space
  form) are still accepted on the way in.
* **`GET /v1/markets?envelope=true` cursors are now PM-format and actually
  round-trip.** `next_cursor` is a base64-encoded offset (`"NTA="` = 50) with
  PM's `"LTE="` terminal sentinel (was a raw int string + `""` terminal that
  NO query param accepted — PM-style paginators looped on page 1 forever).
  Pass it back via `?next_cursor=` or `?cursor=`; raw int offsets are still
  accepted on the way in.
* **Order-book endpoints return the FULL book by default** (`/v1/book`,
  `/v1/clob/book/{token_id}`, `/v1/markets/{condition_id}/book`,
  `POST /v1/books`) — PM's wire contract (PM has no depth param; we measured
  PM 96/113 levels where polysim silently returned 10/10). `?depth=N` remains
  as an explicit best-N trim, cap raised from 50 to 500.
* **`before`/`after` filters on `/v1/data/orders` + `/v1/data/trades` take
  unix-seconds timestamps** (PM convention). Unparseable values now return
  400 `INVALID_TIME_FILTER` instead of being silently dropped (which returned
  the full unfiltered set). ISO datetimes remain accepted as a polysim extra.

### Fixes

* **Pagination request param `next_cursor` honored** on `/v1/data/orders`,
  `/v1/data/trades`, `/v1/orders` and `/v1/markets?envelope=true` —
  py-clob-client sends the cursor back under this name; it was silently
  ignored, so `get_orders()` / `get_trades()` refetched page 1 forever. The
  SDK's `MA==` initial seed and `LTE=` terminal sentinel now behave exactly
  as on real PM. `cursor` remains an accepted alias everywhere.
* **`/v1/data/orders` `asset_id` filter implemented** (was accepted and
  ignored): resolves the token to its market+outcome and filters both;
  unknown tokens short-circuit to an empty terminal envelope.
* **`GET /v1/neg-risk` reports the market's real flag** (was hardcoded
  `false`, contradicting both real PM and polysim's own `/v1/book` for the
  same token). Source is the upstream book payload with cached-book and
  catalog fallbacks; unknown tokens get PM's verbatim
  `404 {"error": "market not found"}`. Sim execution semantics are unchanged
  for neg-risk markets — the flag is for routing/branching parity.
* **`GET /v1/balance-allowance` emits PM's keys**: `balance` / `allowance`
  (base-unit strings, 1.00 USDC = `"1000000"`) so `resp["balance"]` no longer
  KeyErrors in ported SDK code. The legacy `collateral` / `conditional` keys
  remain as extras. `asset_type` / `token_id` / `signature_type` are now
  declared query params. The values stay "effectively unlimited" sentinels —
  read `GET /v1/account/balance` for real sizing.

### New

* **`WS /v1/ws/user` now pushes PM-shape `trade` frames when your orders
  fill** on a subscribed market (`event_type: "trade"`, `type: "TRADE"`,
  `status: "MATCHED"`, unix-string timestamps — PM's documented user-channel
  trade message). Divergences: paper trades terminate at `MATCHED` (no
  `MINED`/`CONFIRMED` — no chain), `maker_orders` is empty, and
  `owner`/`trade_owner` are empty strings. PM's `order`
  placement/update/cancel events are still not emitted — poll
  `GET /v1/data/orders`, or use the polysim-native `/v1/ws/executions`.

## 2026-06-11 — PM-faithful order semantics (rolling out)

The matching-engine fidelity fast-follows announced on 2026-06-10 are
implemented and **rolling out behind a deployment flag**. Until the rollout
activates (announced here), the legacy behaviors below remain in force;
once active, the engine matches real Polymarket on all five:

* **FAK/IOC partial fills.** Synchronous IOC/FAK limit orders walk the
  displayed order-book depth within your limit, fill what's available at the
  level-by-level VWAP, and cancel the remainder. A partial fill returns
  `status: "FILLED"` (PM-compat surfaces map it to `matched`) with `quantity`
  set to the filled slice and a `partial_fill:filled=…,requested=…` entry in
  `warnings`. *Legacy: atomic — full quantity at the touch price or cancel.*
* **FOK depth-aware atomicity.** FOK computes the fillable quantity within
  your limit across book levels first: fills entirely (at the walked VWAP)
  iff depth covers your size, else kills cleanly. *Legacy: full-size fill at
  the touch price whenever the top of book crossed, regardless of depth.*
* **GTD auto-expiry.** `time_in_force=GTD` on `POST /v1/orders`,
  `order_type=GTD` on `/v1/clob/order`, and `orderType=GTD` on the PM-shape
  `POST /v1/order` become true Good-Til-Date resting limits: the engine
  skips and auto-cancels them once their unix-seconds `expiration` passes
  (cancelled rows carry `cancelled_reason: "gtd_expired"`; reservations are
  refunded). Expired-at-placement → `400 INVALID_ORDER_EXPIRATION`.
  *Legacy: GTD coerced to GTC (and rejected on native `/v1/orders`).*
* **post-only.** New `post_only` field (PM-shape: top-level `postOnly`):
  guaranteed-maker orders rejected with `400 INVALID_POST_ONLY_ORDER`
  (`X-Polysim-Code: POST_ONLY_WOULD_CROSS`) when marketable at placement;
  `400 INVALID_POST_ONLY_ORDER_TYPE` when combined with FOK/FAK/IOC/market.
  GTC/GTD limits only. *Legacy: field accepted but ignored.*
* **Minimum order size.** The per-market `min_order_size` that `GET /v1/book`
  advertises (5 shares on standard binary markets, 1 on ≤0.001-tick markets)
  is enforced at placement: `400 INVALID_ORDER_MIN_SIZE`
  (`X-Polysim-Code: ORDER_BELOW_MIN_SIZE`). Note this applies to SELL too —
  like real Polymarket, a sub-minimum residual position can't be exited via
  a below-minimum order. *Legacy: any size > 0.0001 shares accepted.*

New request fields (`expiration`, `post_only`) are accepted today on
`POST /v1/orders`, `POST /v1/clob/order`, and the PM-shape `POST /v1/order` /
batch; they are advisory no-ops until the rollout activates. The activation
will be announced in this changelog.

## 2026-06-10 — Polymarket wire-parity wave

A coordinated set of fixes aligning the API with Polymarket's **live wire behavior**
(verified against `clob.polymarket.com`, not just Polymarket's docs — the two
disagree in places), plus truthful fee reporting and wallet scoping.

### Breaking changes

* **`GET /v1/price` side semantics flipped.** `side=BUY` now returns the best **bid**
  (your side of the book) and `side=SELL` the best **ask** — matching Polymarket's
  live wire and its API reference. Previously the values were inverted. Quotes return
  your side of the book; *executions* still cross the spread (market BUY fills at the
  best ask) — same convention as Polymarket.
* **`GET /v1/book` level ordering re-sorted to Polymarket's live wire.** `bids` are
  ASCENDING and `asks` DESCENDING by price — the **best price is the LAST element on
  both sides** (`bids[-1]` / `asks[-1]`), byte-compatible with real
  `clob.polymarket.com/book` responses. Recommendation: read the inside market
  order-independently (max bid price / min ask price) so ordering can never bite you.
  Applies to `/v1/book`, `/v1/clob/book/{token_id}`, `POST /v1/books`, and
  `/v1/markets/{condition_id}/book`.
* **Account reads default to the API wallet.** `GET /v1/account/positions`,
  `/history`, and `/profile-analysis` previously returned rows from ALL your wallets
  (including website MAIN/SANDBOX wallets) when `wallet_id` was omitted. The default
  is now your **API wallet**, consistent with `/balance`, `/portfolio`, and
  `/equity`. Pass `wallet_id=all` for the old behavior, `wallet_id=<id>` for a
  specific wallet. The `all`/`api` keywords work on all five account-read endpoints.

### Fees — now reported truthfully

The engine has always charged Polymarket-V2 per-category taker fees; the reporting
surfaces wrongly claimed zero. Now:

* `GET /v1/fee-rate` returns `{"base_fee": 0|1000, "fee_rate_bps": <effective rate>}`.
  `base_fee` mirrors Polymarket's legacy base-fee parameter (observed: flat `1000` on
  fee-charging markets, `0` on fee-free ones); **`fee_rate_bps` is the effective
  per-category taker rate actually charged** (sports 300, finance/politics/mentions/tech
  400, economics/culture/weather/other 500, crypto 700, geopolitics 0).
* The crypto taker rate was corrected from 7.2% to **7.0%** (Polymarket's published
  rate). Fills before 2026-06-10 may have been charged at 7.2%.
* `GET /v1/data/trades` rows now carry the real `fee_rate_bps` (0 for maker fills).
* Maker/taker classification now follows **marketability at placement**: a limit
  order that crosses the book when placed pays the taker fee even though it fills via
  the \~1s matching cycle; resting orders that fill later remain fee-free makers, and
  the resting remainder of a partial fill is treated as maker. See
  [Trading Fees](/trading/fees) for the schedule and formula.

### Fixes

* Candle intervals documented correctly: `1h/6h/1d/1w/max` (sub-hour intervals are
  not yet available; unknown values return `400 INVALID_INTERVAL`).
* FAK/IOC behavior documented honestly: currently **atomic** (full fill at the touch
  price or cancel) — Polymarket-style partial-fill-then-cancel is a planned
  fast-follow and will be announced here.
* `llms.txt` corrected across both hosts (error codes, bootstrap permissions,
  book-ordering parity claims, fee schedule).

### Coming soon

* Matching-engine fidelity fast-follows: GTD auto-expiry, post-only, per-market
  minimum order size, FAK partial fills. *Update 2026-06-11: implemented and
  rolling out — see the entry above.*

> The official Python SDK shipped — `pip install polysimulator` (see the
> [SDK overview](/sdk-polymarket)).

## 2026-06-09 and earlier

The beta API surface was assembled and hardened through internal audit waves
(error-envelope consistency, public market-data reads without a key, PM-shape
compatibility endpoints, rate-limit headers). The 2026-06-10 entries above are the
first changes shipped after external-facing documentation went live.
