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.
Both gateways accept the same two schemes as HTTP — X-Api-Key for partners, Bearer JWT for the first-party frontend. Private channels (user_orders, user_fills, vault_positions) require the portfolio:read scope on API keys; JWT callers bypass the scope check as the wallet owner. For vault_positions, each requested vault must be covered by the API key’s associated_vault row. 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)