Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

WebSocket API (Beta)

Three multiplexed endpoints, all JSON text frames. Open one connection to each you need and subscribe to any number of channels on it. /ws and /api-ws use camelCase fields, and the TypeScript SDK wraps them with typed payloads and automatic reconnection. /ws-rtds relays an external price feed in its own format; see Crypto prices.

CarriesMainnetTestnet
/wsNode stream: books, trades, account state, chain datawss://rpc.txflow.com/wswss://rpc.txflow-testnet.xyz/ws
/api-wsAPI-service stream: tickers, trade lists, candles, history snapshots, async Outcome failureswss://rpc.txflow.com/api-wswss://rpc.txflow-testnet.xyz/api-ws
/ws-rtdsCrypto reference prices for Outcome underlyings: spot, Chainlink, TWAPwss://rpc.txflow.com/ws-rtdswss://rpc.txflow-testnet.xyz/ws-rtds

Connection & control plane

A connection carries subscribe, unsubscribe, and ping frames from the client. On /ws, put the channel's subscribe type at the top level as well as inside subscription — the node routes on the top-level type and silently drops a frame without it:

{ "method": "subscribe", "type": "orderUpdates", "subscription": { "type": "orderUpdates", "user": "0x…" } }

/api-ws takes the subscription object alone.

Keep-alive

Send a ping every 10 seconds on every endpoint, starting when the socket opens. The gateway closes a /ws or /api-ws connection that has sent nothing for about 16 seconds (close code 1006); /ws-rtds allows 60 seconds. Server pushes do not count as activity.

EndpointClient sendsServer replies
/ws{"method":"ping"}{"method":"PONG"}
/api-ws{"method":"ping"}{"channel":"pong"}
/ws-rtdsPING (plain text, not JSON)PONG (plain text)

Drop the pong before dispatching messages by channel. Any client frame counts as activity, but use a dedicated timer so a quiet connection stays up.

const ws = new WebSocket('wss://rpc.txflow-testnet.xyz/ws')
let heartbeat: ReturnType<typeof setInterval> | undefined
 
ws.onopen = () => {
  ws.send(JSON.stringify({ method: 'subscribe', type: 'trades', subscription: { type: 'trades', coin: '1' } }))
  heartbeat = setInterval(() => ws.send(JSON.stringify({ method: 'ping' })), 10_000)
}
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data)
  if (msg.method === 'PONG' || msg.channel === 'pong') return // heartbeat reply
  // handle msg.channel / msg.data
}
ws.onclose = () => clearInterval(heartbeat) // then reconnect and re-subscribe

The TypeScript SDK pings every 10 seconds on its own (keepAlive.interval).

WSwss://rpc.txflow-testnet.xyz/wsJSON text frames

A single connection multiplexes every channel. On /ws, every subscribe and unsubscribe frame repeats the channel type at the top level ("type") — the node dispatches on it and ignores a frame without it. The client sends subscribe / unsubscribe / ping frames; the server replies to ping with pong. On /ws there is no subscribe acknowledgement — invalid subscriptions are silently ignored. /api-ws acknowledges each subscribe with a subscriptionResponse frame and answers an invalid one with {"channel":"error","message":…}.

Subscribe
{
  "method": "subscribe",
  "type": "l2Book",
  "subscription": {
    "type": "l2Book",
    "coin": "4"
  }
}
Unsubscribe
{
  "method": "unsubscribe",
  "type": "l2Book",
  "subscription": {
    "type": "l2Book",
    "coin": "4"
  }
}
Ping / Pong · send every 10 s — see Keep-alive above
{
  "method": "ping"
}
Client request envelope · WebsocketRequest
methodSubscriptionMethodrequired

Case-insensitive. Canonical lowercase form shown.

"subscribe""unsubscribe""ping"
actionstringoptional

Alias for `type` (either key name is accepted).

subscriptionSubscriptionoptional

Tagged union discriminated by `type`. One of the data-bearing subscription kinds documented here.

oneOf · variant 1 — type = "orderUpdates"
typestringrequired
"orderUpdates"
userEthAddressrequired

User whose orders to track. Use the account address, not an agent's.

coinstringoptional

Optional trading-pair filter. Omitted or "" means all coins.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 2 — type = "trades"
coinstringrequired
typestringrequired
"trades"
userstringnullableoptional

Optional filter: only trades where this address is taker or maker are pushed. Omit or "" for all trades. Use the account address, not an agent's.

oneOf · variant 3 — type = "l2Book"
coinstringrequired
typestringrequired
"l2Book"
mantissaintegernullableoptional
nSigFigsintegernullableoptional

Optional significant-figure aggregation.

tickstringnullableoptional

Optional explicit price tick.

oneOf · variant 4 — type = "l2BookDepth"
coinstringrequired

Depth and price granularity are fixed server-side.

typestringrequired
"l2BookDepth"
oneOf · variant 5 — type = "positionUpdates"
typestringrequired
"positionUpdates"
userEthAddressrequired

User to follow. Use the account address, not an agent's.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 6 — type = "activeAssetCtx"
coinstringrequired
typestringrequired
"activeAssetCtx"
oneOf · variant 7 — type = "activeAssetData"
coinstringrequired
typestringrequired
"activeAssetData"
userEthAddressrequired

User to follow. Use the account address, not an agent's.

modestringnullableoptional
oneOf · variant 8 — type = "userAssetData"
typestringrequired
"userAssetData"
userEthAddressrequired

User to follow. Use the account address, not an agent's.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 9 — type = "webData2"
typestringrequired
"webData2"
userEthAddressrequired

User to follow. Use the account address, not an agent's.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 10 — type = "allMids"
typestringrequired
"allMids"
exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 11 — type = "clearinghouseState"
typestringrequired
"clearinghouseState"
userEthAddressrequired

User to follow. Use the account address, not an agent's.

dexstringnullableoptional

Optional perp dex name. Defaults to the first dex.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 12 — type = "bbo"
coinstringrequired
typestringrequired
"bbo"
oneOf · variant 13 — type = "allAssetCtxs"
typestringrequired
"allAssetCtxs"
exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 14 — type = "userFundings"
typestringrequired
"userFundings"
userstringrequired

User to follow. Use the account address, not an agent's.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 15 — type = "twapStates"
typestringrequired
"twapStates"
userstringrequired

User to follow. Use the account address, not an agent's.

dexstringoptional

Defaults to "" (first/default dex).

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 16 — type = "userTwapHistory"
typestringrequired
"userTwapHistory"
userstringrequired

User to follow. Use the account address, not an agent's.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 17 — type = "userTwapSliceFills"
typestringrequired
"userTwapSliceFills"
userstringrequired

User to follow. Use the account address, not an agent's.

exchangeIdExchangeScopeoptional

Filter by an instrument's configured exchange_id. Same three-state semantics as the HTTP /info ExchangeScope: missing, null, or 0 -> Default (only instruments with no configured exchange_id); the string "all" (case-insensitive) -> All (no filter, every exchange); a positive integer n -> Exchange(n) (only that exchange_id).

oneOf · variant 18 — type = "deploy"
typestringrequired
"deploy"
oneOf · variant 19 — type = "outcomeState"
typestringrequired
"outcomeState"
userEthAddressrequired

User to follow. Use the account address, not an agent's.

typestringoptional

The channel's subscribe type, e.g. `orderUpdates`. Required on every subscribe and unsubscribe. Case-insensitive. Alias: `action`.

Server pong
methodstring
"PONG"

Channels

Grouped like the Info API. Each page lists the subscription parameters and the pushed payload, and has a Try it panel that subscribes live.

Shared

Books, trades, and order updates take a coin. The explorer channels stream the chain itself and take no parameters.

ChannelUse it to
tradesDrive the public trade tape for one instrument.
l2BookDrive an order-book widget for one instrument; each push is a full snapshot.
l2BookDepthMaintain a local fixed-depth book from one snapshot plus deltas (lower bandwidth, you apply updates).
bboStream best bid / best ask for one instrument (spread display, quoting bots).
allMidsStream the mid price of every instrument — the lightest way to keep a ticker live.
orderUpdatesTrack a user’s orders live: placed, filled, cancelled, rejected.
blockFeed an explorer’s latest-blocks list.
txFeed an explorer’s latest-transactions list.
newBlockTick the current block height once per second.

Perpetuals

Mark, oracle and funding context, position and margin state, and the TWAP streams.

ChannelUse it to
activeAssetCtxKeep one market header live: mark, oracle, funding, OI (Outcome: price, volume, OI).
allAssetCtxsKeep a perp markets table live — every instrument’s context in one subscription.
activeAssetDataKeep the order ticket’s leverage / max size / available margin live for one instrument.
userAssetDataSame as activeAssetData, for every instrument in one subscription.
clearinghouseStateKeep the Positions panel and margin summary live.
positionReact to individual position changes (opened, resized, closed).
webData2One-subscription account view: positions + margin + open orders (Hyperliquid-compatible).
userFundingsAppend funding settlements to Funding History as they happen.
twapStatesShow progress of a user’s running TWAP orders.
userTwapHistoryAppend TWAPs to the history list as they finish or are cancelled.
userTwapSliceFillsStream TWAP child fills as they execute.

Outcome

Dedicated batch-BBO, trade, and order-lifecycle channels, plus the shared single-market streams with the numeric instrument_id as coin. outcomeTrades follows several instruments in one subscription; trades follows one market. There is no Outcome balance channel — poll clearinghouseState with accountType: "outcome".

ChannelUse it to
outcomeBbosKeep prices live in an Outcome discovery grid or watchlist (up to 50 instruments per subscription).
outcomeOrderUpdatesTrack the matching lifecycle of a user’s Outcome orders: filled, rejected, cancelled.
outcomeTradesOne public tape across several Outcome instruments (every leg of an event).
l2BookDrive an order-book widget for one instrument; each push is a full snapshot.
tradesDrive the public trade tape for one instrument.
activeAssetCtxKeep one market header live: mark, oracle, funding, OI (Outcome: price, volume, OI).

API service

Subscribe on /api-ws, not /ws.

ChannelUse it to
tickerShow last price, 24-hour change, high/low, and volume for one instrument.
tradesShow the latest 300 trades for one instrument from /api-ws, refreshed every few seconds.
candleKeep the last bar of a chart live after seeding it with candleSnapshot.
multiCandleKeep the last bar of up to 8 coins live on one subscription, e.g. a markets list with sparklines.
outcomeOrderUpdateCatch async Outcome order submissions that fail (by traceId). Only failures are pushed; not a fill signal.
userHistoricalOrdersLoad a user’s order history over /api-ws — one snapshot, no updates.
userFillsLoad a user’s fill history over /api-ws — one snapshot, no updates.
webData2Load a user’s open orders over /api-ws — one snapshot, no updates.

userHistoricalOrders, userFills, and webData2 on /api-ws send one snapshot on subscribe and nothing after.

Crypto prices (/ws-rtds)

Reference prices for crypto Outcome underlyings, relayed from Polymarket's real-time data service. Frames use snake_case fields and have no channel envelope.

{
  "action": "subscribe",
  "subscriptions": [
    { "topic": "crypto_prices", "type": "update", "filters": "btcusdt,ethusdt" },
    { "topic": "crypto_prices_chainlink", "type": "update", "filters": "{\"symbol\":\"btc/usd\"}" }
  ]
}

unsubscribe takes the same shape.

TopicPricefiltersSymbols
crypto_pricesBinance spotComma-separated: "btcusdt,ethusdt"btc, eth, sol, xrp, doge, bnb, hype + usdt
crypto_prices_chainlinkChainlinkJSON: "{\"symbol\":\"btc/usd\"}"The above plus zec, as <asset>/usd
crypto_prices_twap_thirty30s TWAP of ChainlinkSame as ChainlinkSame as Chainlink
crypto_prices_twap_sixty60s TWAP of ChainlinkSame as ChainlinkSame as Chainlink

Each symbol pushes about once a second:

{
  "topic": "crypto_prices_chainlink",
  "type": "update",
  "timestamp": 1792047647886,
  "payload": {
    "symbol": "btc/usd",
    "timestamp": 1792047647000,
    "value": 80856.15721525438,
    "full_accuracy_value": "80856157215254380000000"
  }
}

Limits: 20 connections per IP, 128 symbol subscriptions per connection, 64 subscriptions per frame.