GET /orders/fee-quote, you send one subscribe_fee_quote message and the server pushes a fee_quote frame immediately, then again whenever the number changes — a request-for-quote (RFQ) feed suitable for both UIs and bots.
The REST GET /orders/fee-quote endpoint remains available; this stream is the lower-latency, push-based equivalent.
Connection
This uses the same WebSocket connection as Order Updates and Order Execution:trade:read scope and platform access to exchange_id. If either check fails, the server sends a message-only fee_quote_error frame and does not create the subscription. If access is later disabled or cannot be verified, the subscription is removed and a message-only fee_quote_error frame is emitted.
How it works
- You send
subscribe_fee_quotedescribing the trade — instrument,side, andquantity(size) — plus aquote_idyou choose (or let the server assign one). For a market order you do not send a price: the server prices the size from the live order book. For a limit order you send your restingprice. - The server replies with a
fee_quoteframe: the size-weighted executable price (VWAP — the average price you’d pay walking the book for your size) for your size, the fees on that notional, and the all-in cost. - While the subscription is active, the server re-evaluates every active subscription on a fixed tick (100 ms by default) and re-sends a
fee_quoteframe only when the quote value changes. A market that isn’t moving emits once, then stays quiet. - To change the trade (new size, side, market, or limit price), send
subscribe_fee_quoteagain with the samequote_id— it replaces the spec in place. - Send
unsubscribe_fee_quoteto stop.
Quotes are estimates (
is_estimate: true) until the order executes. For a market order, avg_price_usdc is the VWAP of walking the book for your size and sufficient_liquidity is false when the book can’t fill the whole size. Polymarket charges fees on taker fills only — a limit (maker) order shows $0 exchange fee.Client messages
subscribe_fee_quote
*For Polymarket, provide either
token_id or market_id (required). For Kalshi, market_id is optional but strongly recommended: it is the series ticker (e.g. KXNFLGAME-…) used to resolve the per-series fee tier. Without it the quote assumes the standard tier, which under-estimates the fee on non-standard series (most sports/macro markets price makers very differently); the exchange_fee_note will say … assumed — pass market_id in that case. For Predict.fun, market_id is optional and lets the quote use the live per-market rate instead of the 2% default.
Numbers may be sent as JSON strings to preserve precision (recommended) or as JSON numbers.
exchange_id is trimmed and lower-cased before validation, and the deprecated
kalshi_offchain alias is canonicalized to kalshi. The payload fields may
also be sent flat at the top level instead of nested under payload — the
server reads payload when it is an object and falls back to the message
root.
Gotcha:
request_id is not echoed on fee-quote frames. It is accepted on
the envelope, but no fee_quote, fee_quote_error, or
fee_quote_unsubscribed frame carries it. Correlate on quote_id instead.unsubscribe_fee_quote
Unsubscribing an unknown
quote_id is a no-op and still returns an ack.
Server frames
fee_quote
Gotcha: guard on
pricing_unavailable before reading any cost field.
When the server has no client price and no fresh book yet, it emits a frame
with pricing_unavailable: true, all-zero numerics, and
exchange_fee_note: "live price unavailable"; it re-emits a real quote
(pricing_unavailable: false) once the book arrives. Render a “pricing…”
state — never $0.00.fee_quote_error
Sent when a subscribe/unsubscribe is malformed or the spec is invalid. The subscription is not created; the connection stays open.quote_id is null when the offending message didn’t carry one.
fee_quote_unsubscribed
Acknowledges anunsubscribe_fee_quote.
Sequencing and recovery
seq increases monotonically per quote_id, so a client that renders
frames out of order can discard anything with a seq below the one it has
already applied. Frames are only emitted when the quote value changes, so
silence means “unchanged”, not “stalled”.
On disconnect, all subscriptions are dropped. After reconnect, re-send your subscribe_fee_quote messages — the server does not replay them. Close codes and reconnect backoff for this socket are documented on Order Updates → Server-initiated close codes.
Errors
Every condition below produces afee_quote_error frame; the subscription is not created and the connection stays open.
Limits
WebSocket vs REST
Both compute fees identically (the same server-side path), so a REST quote and a streamed quote for the same spec match.

