Skip to main content
Lock the trade you’re eyeing and stream the fees you can expect to pay. Instead of polling 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:
See Order Updates - Connecting for authentication and setup. Once connected, send the messages below as JSON text frames. API-key subscriptions require 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

  1. You send subscribe_fee_quote describing the trade — instrument, side, and quantity (size) — plus a quote_id you 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 resting price.
  2. The server replies with a fee_quote frame: 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.
  3. 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_quote frame only when the quote value changes. A market that isn’t moving emits once, then stays quiet.
  4. To change the trade (new size, side, market, or limit price), send subscribe_fee_quote again with the same quote_id — it replaces the spec in place.
  5. Send unsubscribe_fee_quote to stop.
Because the market-order subscription contains no price, it does not churn as the BBO (best bid/offer) ticks — the server walks the book each evaluation instead. The server continuously re-evaluates active subscriptions, and unchanged quotes emit nothing, so an idle subscription costs effectively nothing on the wire.
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

Payload fields: *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 an unsubscribe_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 a fee_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.