Skip to main content
PredictStreet exposes WebSocket streaming through two separate gateways that share one client protocol. Pick the gateway by what data you want; never mix subscriptions across the two on one socket. Both gateways authenticate with X-Api-Key — the same key you use on HTTP, same scope rules. The protocol is Kalshi-style (envelope with id / cmd / params) and Polymarket-style identifier lists (channel + ids[]).

Connect

Same handshake contract for both gateways:
Pick one of:
  • X-Api-Key: ps_live_<keyId>_<secret> header on the upgrade request
  • ?key=<token> query parameter — for browser clients that can’t set custom headers on the WebSocket upgrade
On /ws/user each socket is bound to one wallet, and its private channels serve only that wallet and its vault. A single_wallet key is bound to the partner’s associatedWallet. A multi_wallet key has no wallet of its own, so it must also name one on the upgrade with X-User-Wallet: 0x… (or ?wallet=0x… / ?user_wallet=0x…). Without it the socket closes with 4401 api_key_user_wallet_required; a malformed address closes it with 4401 api_key_user_wallet_invalid. (If a multi_wallet partner also has an associated wallet configured, the socket binds to that wallet and ignores X-User-Wallet.) Open one /ws/user socket per wallet. See WSS /ws/user. The server pushes one greeting after the upgrade:
vaultAddress is null when the wallet has no vault yet. An open socket keeps the value it got at the handshake, so reconnect after the vault is deployed before you subscribe to vault_positions / vault_activity. On /ws/market the greeting is { "gateway": "market", "authMethod": "api_key", "protocolVersion": 2, "partnerId": "…" } — no wallet context, since market data isn’t per-user.
Auth is checked at handshake. Issuing a new key doesn’t affect sockets opened with the old one; close and reopen to switch keys. A bad key closes with 4401 <reason> (e.g. 4401 api_key_expired). Open sockets on a key are closed immediately with 4401 api_key_revoked when the key is revoked, its scopes change, the partner is suspended, or the partner’s associated wallet, kind or per-wallet KYC setting changes. Reconnect once to learn whether the key still works.

Command envelope

Every client-to-server message uses the same shape:
  • id — client-side correlation id; echoed in the server’s response
  • cmd — subscribe / update_subscription / get_book_snapshot / unsubscribe / list_subscriptions / ping; anything else gets an unknown_cmd error
  • params — command-specific payload
Every accepted subscription gets a connection-local sid (numeric). Use sid for subsequent updates / unsubscribes / event routing on the client side. sids are not stable across reconnects.

Channel catalog

Public, on /ws/market: Private, on /ws/user: Detailed payloads + examples in Subscriptions and Messages. The two crypto feeds have their own page: Crypto price feeds.

Where do tokenId and conditionId come from?

Market data is keyed by on-chain native ids, not by app symbol. Pull them from REST first:
  • GET /api/markets/{symbol} → conditionId / questionId / yesTokenId / noTokenId, and each outcome’s outcomes[].tokenId
  • A neg-risk (multi-outcome) event is listed as one market per outcome (negRiskEligible: true). Each has its own symbol, conditionId, yesTokenId and noTokenId in that same response, so no on-chain lookup is needed
Then open /ws/market and subscribe by those native ids:
Token- and condition-scoped channels take these on-chain ids, not app symbols. market_lifecycle and market_price_tick are keyed by market symbol (market_price_tick as <symbol>:<outcomeIndex>), crypto_price_tick by instrument id, and the all_* channels and global_activity take no ids at all.

What clients should NOT do

  • No symbol-based subscriptions on token or condition channels. Use tokenId / conditionId; only market_lifecycle and market_price_tick take a market symbol.
  • No invented room strings like vault:0xabc:erc1155. Use channel + ids only.
  • No mixing of /ws/user and /ws/market on one socket. The gateway rejects cross-gateway channels with forbidden — open one socket per gateway.
  • No assumptions about sid stability across reconnects. After any close, re-subscribe from scratch.

Next

Subscriptions

Per-channel subscribe / update / unsubscribe payloads.

Message shapes

Outbound event envelope and full event catalog by channel.

Reconnect

Heartbeat, sid rebuild, snapshot resync.

API keys

Scope catalog, rotation, revoke.