Deprecation policy
When we remove or materially change a public endpoint or field:- Announce the replacement, what to use instead, with a migration example.
- List affected endpoints, every route, field, or WS channel touched.
- Set a dated removal window, at least 90 days from announcement for breaking REST changes, unless the change is a security fix.
- Log reversals, if we roll back a deprecation, it is recorded here with the reason.
/changelog/rss.xml when published).
New guide
Series, assets, and categories — the nine series, thecategory and feed id tables, how to list every live market
across assets, and the market hours for gold and crude.What is new
Nine series trade in parallel. Three assets × three cadences.category
1–3 is BTC, 4–6 gold (XAU), 7–9 WTI crude, at 60s / 5m / 15m respectively.
GET /v1/markets?status=trading now returns up to nine live slots rather than
one.GET /v1/oracle/price takes an optional feed. ?feed=0 is BTC,
1 gold, 2 WTI crude. Omitting it still means feed 0, so existing
clients are unaffected. The response body does not echo the feed. A feed with
no valid tick answers the usual zero sentinel (price: "0") rather than an
error — that is what a closed commodity session looks like.The oracle WebSocket channel now carries feed on every tick. The
channel is global, so a single subscription receives BTC, gold and crude
interleaved; branch on feed before using price. The field is additive and
a frame without it would read as feed 0.The rounds WebSocket channel carries category (alongside
round_number and generation) on every frame, so the global round stream can
be scoped to one series client-side.GET /v1/rounds?category= filters to one series. category: 0 means
unclassified, not “all” — omit the parameter to ask for everything.What to check in your client
- Anywhere you assumed “the live round” was unique, or that
GET /v1/rounds?limit=1was it. With nine series the top row is whichever series opened last. UseGET /v1/markets?status=trading. - Anywhere you assumed an oracle tick was BTC. Filter on
feed. - Anywhere you cached
market_id→ asset. Slots are recycled and can return in a different series; re-read the round. - Anywhere you assumed
fresh_source_count >= 3. Gold and crude come from a single upstream source and report1. - Anywhere you assumed 24/7. Gold and crude follow their underlying sessions.
Not changing
market_id, round_number, pip ticks, the order surface, fees, scopes and
rate limits are untouched. The market object still carries no category — the
round is where the series lives.New guide
Fees — the per-fill formula, why it is quadratic in price, how a buy reserves its fee before the fill, why sells never reserve, and the full list of what is never charged.Fee correction
The engine computed the per-fill fee asceil(rate × qty × Q(p) / 10^10). With
money in pips the divisor is 10^8: the pip migration scaled Q(p) by 10^4
and the fee’s own unit by 10^2, and only one of the two was carried into the
constant. Every trading fee since then was 1/100th of the configured rate.The live schedule is unchanged — maker 0, taker 250 — and 250 has always
been documented as 2.5% Kalshi-style. It now bills that:Affected: anything that modelled realised fees from observed fills rather
than from the published rate — backtests calibrated on live fills since the pip
migration will have understated costs by 100×. Maker rebates and referral
shares are a percentage of fees collected, so both scale with the correction.
Orders already resting keep the rate pinned at their admission.
Three buckets
Read, write, and cancel are independent token buckets. Cancels (single, batch, mass-cancel) no longer draw the write bucket. Cancel rate is always2 × the effective write rate.Cost stays 1 token per HTTP request. A 20-order batch costs 1 token, not 20.Volume-scaled write rate
vol_30d is that account’s trailing 30 UTC-day fill-share sum (SUM(qty),
maker and taker, $1/share). A new account gets 20/s. The cap is 400/s.GET /v1/account/limits
Additive fields on the existing endpoint: cancel, trailing_volume_30d,
write_floor, usd_per_extra_rps. Ignore unknown response fields.Guide: Rate limits.Affected: bots that assumed cancels shared the write bucket, or that
hard-coded 50/s write. Read the live grant from GET /v1/account/limits.Breaking — ticks are pips, not cents
Contract prices, book levels, andtick / worst_tick / tick_yes are
integer pips (1–9999). 1 pip = 0.01¢. $1 = 10_000 pips.
4700 is 47.00¢. Sending the old 1–99 range is bad_price_tick — it is
never scaled up.Cash, max_cost, balances, fees, deposits, and withdrawals are pips
(decimal strings). 10000 is $1.00. Market-buy worst_tick defaults to
9999 (was 99).YES ask = 10000 − NO bid (was 100 − t). Winning contracts pay 10,000 pips.Rest grid (tick_size)
Market and orderbook snapshots (REST and WS) include tick_size:narrow: whole cents on [400, 9600], 0.10¢ on the wings.
That grid is always live — do not wait for mode: "subcent" to rest 9610.Guide: Ticks, pips, and subcents.Settlement cadence
Rounds open in the first seconds of a Unix minute and freeze when the wall clock enters the next minute (notopen_ts + 60s). winner is "yes"
iff the freeze TWAP is strictly above strike.Find the live slot with GET /v1/markets?status=trading, not
GET /v1/rounds?limit=1.Affected: every client that sends ticks, max_cost, or assumes
100 − t reciprocity.Breaking, cancel batch verb
Batch cancel moved off a separate POST path.Body unchanged:
{ "order_ids": ["…"] } (≤ 20 decimal-string ids). Response is
per-item, index-aligned; partial success is normal.POST /v1/portfolio/orders/batch remains batch placement only.Affected: any client still calling …/cancel_batch. Migrate before relying
on production traffic, the old path is gone (no dual-write window; the route
never had external consumers).Breaking, cancel-all empty body
DELETE /v1/portfolio/orders must have an empty body. A non-empty body
returns 400 unexpected_body instead of cancelling. This prevents a misrouted
batch-cancel (missing /batch) from wiping the book.Affected: DELETE /v1/portfolio/orders only.Round status vocabulary
Round rows use:trading, frozen, proposed, settled, void. There is no
created status on round objects.WebSocket channel inventory
Live:orderbook_snapshot, orderbook_delta, trades, user, oracle, rounds.
ticker is not implemented (subscribe → wscode 4).exchange_active honesty
GET /v1/exchange/status always returns exchange_active: true in v1.
Per-round freeze/settle is the operational pause, see
Pauses and rounds.New introspection endpoints
Dead-man switch
POST /v1/portfolio/deadman with { "timeout_ms": N } (1000–60000) arms a
gateway-side timer. Refresh by POSTing again. On fire: one user-wide cancel-all,
then disarm. DELETE /v1/portfolio/deadman disarms without cancelling.This is not Kalshi-style order-group fill-velocity limiting, that remains
deferred.WebSocket update_subscription
get_snapshot, add_markets, delete_markets.429 shape
Rate limits answer withdetails.retry_after_ms in the error body. There is no
Retry-After HTTP header.