Real-Time Streams
Sequence streams every kind of real-time event through a single multiplexed WebSocket: /v1/stream. You subscribe to topics, you get JSON envelopes, you cancel by topic, you keep one connection open.
For consumers who only want one specific feed (TCA reports, alert triggers, prediction-market lifecycle), there are dedicated endpoints listed near the bottom of this page.
If you're integrating for the first time, start with /v1/stream and subscribe to the topics you need. One connection serves prices, books, orders, fills, TCA, fair price, traces, and funding rates.
Authentication
All streams require Authorization: Bearer seq_live_....
For browser clients that can't set headers on a WebSocket connect, exchange your bearer token for a short-lived ticket first:
GET /v1/ws/ticket
→ {"ticket": "wst_abcd1234..."}
# Then connect:
wss://api.sequencemkts.com/v1/stream?ticket=wst_abcd1234...
Tickets expire after 30 seconds and are consumed on first use.
Query-parameter auth (?api_key=...) was removed for security. Use the Authorization: Bearer header for non-browser clients, or ?ticket= for browsers.
Unified Stream
/v1/streamA single WebSocket. Subscribe to topics. Receive JSON envelopes shaped:
{"channel": "<topic>", "data": { ... }}Subscribe / unsubscribe
// Subscribe to one or more topics
{"subscribe": ["prices:BTC-USD", "book:BTC-USDT", "orders", "fair_price:BTC"]}
// Unsubscribe
{"unsubscribe": ["prices:BTC-USD"]}The server responds with an ack listing accepted topics and any rejections:
{"subscribed": ["prices:BTC-USD", "book:BTC-USDT", "orders", "fair_price:BTC"], "rejected": []}Subscription is a WS message, not a URL parameter. A URL like wss://api.sequencemkts.com/v1/stream?symbols=BTC-USD&book=1&prices=1 will connect successfully but stream zero events — the server ignores query parameters on /v1/stream and waits for a JSON {"subscribe": [...]} message after the WS upgrade. If you see the connection succeed but get nothing back, this is the first thing to check.
A successful subscribe always returns {"subscribed": [...], "rejected": [...]} as the first message after you send the subscribe payload. If you don't see that envelope, your subscribe message wasn't parsed — verify it's valid JSON sent over the WS data channel.
Topic catalog
| Topic | Source | Payload |
|---|---|---|
prices:<symbol> | NBBO updates (best bid/ask across all venues) | One mid + best per side, per update |
book:<symbol> | TopBook L2 updates per venue | Up to 10 levels per side, per venue, per update |
orders | Order lifecycle (snapshots when state changes) | Full order shape: id, status, qty, fills, etc. |
fills | Per-fill events from every venue | qty, price, fee, venue, timestamps |
tca | Post-completion TCA report per order | Slippage, IS, savings, achieved vs benchmark |
traces:<deployment_id> | Per-callback traces from a deployed native strategy | Function/payload/timing, scoped to one deployment |
funding / funding:<venue> / funding:<venue>:<symbol> | Perp funding rate updates | Rate bps/hr, predicted, mark, OI, next-settlement |
fair_price / fair_price:<underlying> | Cross-venue Chainlink-style consolidated reference price | composite mid, spot/perp legs, basis_bps, contributors |
prices:, book:, funding:, fair_price:, traces: accept a suffix to filter; subscribing to funding (no suffix) means "all venues, all symbols", subscribing to fair_price means "all underlyings".
Example: prices + book + fair_price
import asyncio, json, websockets
async def main():
headers = {"Authorization": f"Bearer {API_KEY}"}
async with websockets.connect(
"wss://api.sequencemkts.com/v1/stream",
additional_headers=headers,
) as ws:
await ws.send(json.dumps({
"subscribe": ["prices:BTC-USD", "book:BTC-USDT", "fair_price:BTC"]
}))
async for msg in ws:
evt = json.loads(msg)
print(evt["channel"], evt["data"])
asyncio.run(main())Topic payload shapes
prices:<symbol> — NBBO updates:
{
"channel": "prices:BTC-USD",
"data": {
"symbol": "BTC-USD",
"instrument_type": "spot",
"best_bid_px_1e9": 76410000000000,
"best_bid_sz_1e8": 12000000,
"best_bid_venue": "coinbase",
"best_ask_px_1e9": 76411000000000,
"best_ask_sz_1e8": 8000000,
"best_ask_venue": "kraken",
"spread_bps": 1,
"ts_ns": 1745979000123456789
}
}book:<symbol> — per-venue L2 updates (top 10 levels):
{
"channel": "book:BTC-USDT",
"data": {
"symbol": "BTC-USDT",
"venue": "binance",
"instrument_type": "spot",
"bids": [[76410.50, 0.421], [76410.40, 1.2], ...],
"asks": [[76411.00, 0.155], [76411.10, 0.8], ...],
"ts_ns": 1745979000123456789,
"is_snapshot": false,
"checksum": 1448524378
}
}Each tick is one venue's L2 update. The same symbol can stream from multiple venues, multiplexed on the same book:<symbol> topic — differentiate by the venue and instrument_type fields.
| Field | Semantics |
|---|---|
is_snapshot | true only on the initial book frame after subscribe / reconnect (a fresh full state). false for every subsequent delta. Clients tracking their own L2 state should reset on true and apply incrementally on false. |
checksum | Optional 32-bit integrity fingerprint, populated for venues that publish one (Polymarket today; truncated SHA-1 of the venue's book hash). Use as an opaque equality check for divergence detection across consecutive ticks of the same state. null for venues that don't report. |
fair_price:<underlying> — Chainlink-style consolidated reference (see Market Data → Fair Price for the full payload shape).
{
"channel": "fair_price:BTC",
"data": {
"underlying": "BTC",
"fair_mid_1e9": 76405095000000,
"spot_mid_1e9": 76407250000000,
"perp_mid_1e9": 76357950000000,
"basis_bps": -6,
"confidence": 0.665,
"contributors": [
{"venue": "binance", "instrument_type": "spot", "mid_1e9": 76407250000000, "weight": 0.84, "staleness_ms": 96},
{"venue": "binance", "instrument_type": "perp", "mid_1e9": 76357950000000, "weight": 0.81, "staleness_ms": 79},
{"venue": "okx", "instrument_type": "spot", "mid_1e9": 76406500000000, "weight": 0.78, "staleness_ms": 156},
{"venue": "hyperliquid", "instrument_type": "perp", "mid_1e9": 76354500000000, "weight": 0.65, "staleness_ms": 226}
],
"cc_ts_ns": 1745979000123456789
}
}orders — order snapshots:
{
"channel": "orders",
"data": {
"node_order_id": "graph:graph_a1b2c3d4:btc_leg:1",
"client_id": "muhammad",
"client_order_id": "order-001",
"symbol": "BTC-USD",
"side": "buy",
"qty_1e8": 1000000,
"filled_qty_1e8": 1000000,
"status": "COMPLETED",
"created_unix_ns": 1745979000000000000,
"updated_unix_ns": 1745979001200000000
}
}fills — per-fill events from every venue:
{
"channel": "fills",
"data": {
"node_order_id": "graph:graph_a1b2c3d4:btc_leg:1",
"venue_execution_id": "kraken-8f3a2b1c",
"symbol": "BTC-USD",
"side": "buy",
"qty_1e8": 700000,
"price_1e9": 76410000000000,
"fee_1e9": 100000000,
"edge_id": "venue-edge-kraken",
"ts_unix_ns": 1745979000498000000
}
}venue_execution_id is the venue-assigned execution/trade id — the idempotency key for fill dedup.
tca — post-completion TCA report (also exposed as a dedicated SSE at /v1/tca/stream):
{
"channel": "tca",
"data": {
"node_order_id": "graph:graph_…:kalshi_yes:1",
"client_id": "muhammad",
"symbol": "BTC-USD",
"side": "BUY",
"achieved_vwap_1e9": 76411000000000,
"expected_fill_1e9": 76410500000000,
"total_fees_1e9": 7641000,
"fee_cost_bps": 1,
"expected_fee_bps": 2,
"fee_prediction_error_bps": -1,
"spread_cost_bps": 0,
"market_impact_bps": -1,
"implementation_shortfall_bps": 0,
"savings_vs_benchmark_bps": 1,
"is_prediction": false,
"execution_time_ms": 124,
"num_fills": 1
}
}Prediction-market fills carry four extra fields:
| Field | Meaning |
|---|---|
is_prediction | true for any prediction-venue fill, false (or omitted) otherwise |
achieved_implied_prob_bps | Achieved fill price translated to implied probability (e.g. 300 = 3.00%) |
arrival_implied_prob_bps | Arrival mid translated the same way |
probability_slippage_bps | arrival_implied_prob_bps - achieved_implied_prob_bps (sign-flipped for sells) |
funding / funding:<venue> / funding:<venue>:<symbol> — perp funding rate updates:
{
"channel": "funding:binance",
"data": {
"venue": "binance",
"symbol": "BTC-USDT",
"rate_bps_per_hour": 1,
"predicted_rate_bps_per_hour": 1,
"mark_price_1e9": 76410000000000,
"open_interest_1e8": 12000000000,
"next_settlement_ns": 1745982600000000000,
"ts_ns": 1745979000000000000
}
}traces:<deployment_id> — per-callback traces from a deployed native strategy, scoped to one deployment.
Prediction-market live book + prices
Polymarket and Kalshi flow through the same prices: / book: channels as CEX symbols. The address forms accepted on the channel suffix:
| Form | Example | Notes |
|---|---|---|
| Venue-prefixed token id | polymarket:71166707665253014908308475311609108419118036985903834861277070930664191157865 | Recommended. Unambiguous and works for any 77-digit CTF token id. |
| Bare token id (no prefix) | 71166707665253014908308475311609108419118036985903834861277070930664191157865 | Accepted; slug-resolver strips the prefix internally and looks up the same canonical symbol. |
| Slug + outcome | polymarket:btc-updown-5m-1778715600:yes | Resolved server-side via Gamma (cached 10 min). Subject to the resolver lag described below. |
| Kalshi ticker | KXBTCZ-26DEC31-T99000:YES | Direct, no slug translation. |
import asyncio, json, websockets
YES = "71166707665253014908308475311609108419118036985903834861277070930664191157865"
NO = "12689298635162541371837661686258341808898518310308696393181273329357280031984"
async def main():
async with websockets.connect(
"wss://api.sequencemkts.com/v1/stream",
additional_headers={"Authorization": f"Bearer {API_KEY}"},
) as ws:
await ws.send(json.dumps({"subscribe": [
f"prices:polymarket:{YES}", f"book:polymarket:{YES}",
f"prices:polymarket:{NO}", f"book:polymarket:{NO}",
]}))
ack = json.loads(await ws.recv()) # {"subscribed": [...], "rejected": [...]}
async for raw in ws:
evt = json.loads(raw)
print(evt["channel"], evt["data"])
asyncio.run(main())Cold-start latency: Subscribing to a freshly-minted prediction market — one created seconds before you subscribed — may take up to ~30 seconds before the first event arrives. The typical case is sub-second; tokens already trading on the venue start streaming immediately on subscribe.
Connecting events to underlying questions: the data.symbol field on every event is the canonical token id (77-digit string for Polymarket). To resolve back to a human-readable question or slug, hit GET /v1/markets?venue=polymarket&slug=<token_id> or use the new-market lifecycle stream described later in this page.
Lagged-client semantics
Each client gets a 4096-entry per-topic buffer. A slow consumer that backs up will receive a Gap event indicating how many messages were dropped, then resume from current. Don't write retry-on-gap loops — bring your own ordering or watermark and re-snapshot via REST if you fell behind.
Dedicated SSE Endpoints
These are kept for one-off integrations that don't want a WS connection. Every event published on these is also available as a topic on /v1/stream — prefer the unified WS unless you have a specific reason to use the SSE.
TCA Reports
/v1/tca/streamSame payload as the tca topic on /v1/stream. SSE event name: tca_report.
Alert Triggers
/v1/alerts/streamUser-defined rule triggers. SSE event name: alert.
Prediction-Market Lifecycle WebSocket
A separate WebSocket that emits prediction-market lifecycle events as they happen on Polymarket and Kalshi. No subscribe message — connecting is the subscription. Each connection first replays the last 128 events, then streams live.
Events are deduped across multi-region edges by (venue, event_id). The server sends a WS ping every 30 s so idle proxies don't close the connection.
Auth: Authorization: Bearer seq_live_... on the WS upgrade, or ?ticket=... for browsers.
New Markets
/v1/ws/new_marketsEvery freshly-minted prediction market. The wire payload IS the same Market schema as /v1/markets returns — same field names, same nullability rules — plus kind: "new_market" and observed_at_ns for stream framing. Existing code that handles seq.markets() rows can process new-market events with no field-mapping layer.
For Polymarket, frames are enriched asynchronously via the CLOB endpoint before broadcast so slug, start_date, end_date, neg_risk, etc. arrive populated. BBO and volume fields are null/0 on freshly-minted rows (no orders or trades yet).
{
"kind": "new_market",
"venue": "polymarket",
"slug": "will-btc-close-above-100k-on-2026-04-30",
"symbol": "73428831...",
"question": "Will BTC close above $100k on April 30, 2026?",
"outcomes": ["Yes", "No"],
"outcome_token_ids": ["73428831...", "89131027..."],
"yes_token_id": "73428831...",
"no_token_id": "89131027...",
"event_id": "0xabc123...",
"neg_risk": false,
"volume_24h": 0.0,
"volume_total": 0.0,
"start_date": "2026-04-30T00:00:00Z",
"end_date": "2026-05-01T00:00:00Z",
"url": "https://polymarket.com/event/will-btc-close-above-100k-on-2026-04-30",
"observed_at_ns": 1745971205123456789
}Resolved Markets
/v1/ws/market_resolved{
"kind": "market_resolved",
"venue": "polymarket",
"event_id": "0xabc123...",
"winning_outcome": "Yes",
"winning_token_id": "73428831...",
"losing_token_id": "89131027...",
"observed_at_ns": 1745971205123456789
}Minimal Consumer Example
import asyncio, json, websockets
async def main():
url = "wss://api.sequencemkts.com/v1/ws/new_markets"
async with websockets.connect(
url, additional_headers={"Authorization": f"Bearer {API_KEY}"}
) as ws:
async for raw in ws:
evt = json.loads(raw)
print(evt["venue"], evt["slug"], evt["question"])
asyncio.run(main())SDK wrappers: seq.stream_new_markets() (Python) · stream_new_markets() (Rust).
End-to-End Order Lifecycle Example
Submit an order, then watch its full lifecycle on one WebSocket connection:
import asyncio, json, websockets, requests
API_BASE = "https://api.sequencemkts.com"
API_KEY = "seq_live_..."
# 1. Submit
order = requests.post(
f"{API_BASE}/v1/orders",
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
json={"client_order_id": "demo-001", "side": "buy", "symbol": "BTC-USD", "qty_1e8": 1_000_000},
).json()
node_order_id = order["node_order_id"]
# 2. Watch the lifecycle on one connection
async def watch():
async with websockets.connect(
f"{API_BASE.replace('https', 'wss')}/v1/stream",
additional_headers={"Authorization": f"Bearer {API_KEY}"},
) as ws:
await ws.send(json.dumps({"subscribe": ["orders", "fills", "tca"]}))
async for raw in ws:
evt = json.loads(raw)
ch, data = evt["channel"], evt["data"]
if data.get("node_order_id") != node_order_id:
continue
print(ch, data.get("status") or data)
if ch == "tca": # final report — we're done
return
asyncio.run(watch())