Skip to main content
PredictStreet streams live data over two WebSocket gateways that share a single client protocol. Pick the gateway by what data you want; do not mix public and private subscriptions on one socket. Both gateways authenticate at handshake with X-Api-Key — the same key you use for HTTP, same scope rules. A missing or invalid key closes the socket with 4401 <reason> before any subscribe command is accepted.
GatewayEndpointAuthWhat’s there
MarketWSS /ws/marketX-Api-Key (partners) OR Bearer JWT (retail, SIWE)Public trades, orderbook snapshots, market lifecycle, platform status
UserWSS /ws/userX-Api-Key (partners) OR Bearer JWT (retail, SIWE)Own orders / fills / vault state, deposits, withdrawals, ERC-1155 changes
Both gateways accept the same two schemes as HTTP — X-Api-Key for partners, Bearer JWT for the first-party frontend. Private channels user_activity and vault_activity require the portfolio:read scope on API keys; JWT callers bypass the scope check as the wallet owner. vault_activity is reachable only from JWT-authenticated sockets (partner rows don’t carry a vault claim — by design). See /ws/user for the full handshake matrix and close codes.
Same envelope, same commands (subscribe, update_subscription, unsubscribe, list_subscriptions, ping), same per-subscription sid model on both.

WSS /ws/market

Market data gateway — subscribe by tokenId / conditionId.

WSS /ws/user

Private gateway — X-Api-Key (partners) or Bearer JWT (retail).

Commands

All five client → server commands with payloads + responses.

Server events

Per-channel event catalog (push payloads).

Conventions

  • Numeric fields are decimal strings ("price": "0.52") — don’t parse into Number.
  • Addresses lowercased; tokenId decimal-string with no leading zeroes; conditionId lowercased 32-byte hex.
  • sid is connection-local, not stable across reconnects.
  • Auth is validated at handshake only — rotating a key does not invalidate an open socket. Close + reopen to switch identity. Revoking a key on the admin panel closes every live socket bound to that keyId immediately via Redis pub/sub.
  • Heartbeat: send { "id": N, "cmd": "ping" } every 20–30 s; the server replies { "id": N, "type": "pong", "ts": <ms> }.

Implementation status (testnet)

ChannelStatus
token_trades (/ws/market)✅ live — per-fill events flow as soon as a match prints
user_activity (/ws/user)✅ live — order placement, cancel, fill, account-control events
system (/ws/market)✅ live
condition_status (/ws/market)⚠️ subscribe accepted, but events not yet emitted by upstream
token_book (/ws/market)⚠️ subscribe accepted, but book_snapshot / book_delta not yet pushed — under repair
Until token_book ships, poll GET /api/markets/{symbol}/orderbook on demand if you need a snapshot. The protocol contract stays stable — new events will land on the existing channels without subscription changes.