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.
GatewayPathAuthWhat lives here
market/ws/marketX-Api-Keypublic trades, orderbooks, market lifecycle, platform status
user/ws/userX-Api-Key (portfolio:read scope)own orders / fills / account-control events
The protocol is Kalshi-style (envelope with id / cmd / params) and Polymarket-style identifier lists (channel + ids[]).

Connect

Same handshake contract for both gateways:
wss://ws-gateway.adipredictstreet.com/ws/market
wss://ws-gateway.adipredictstreet.com/ws/user
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
The server pushes one greeting after the upgrade:
{
  "type": "connected",
  "data": {
    "gateway":       "user",
    "walletAddress": "0x1111111111111111111111111111111111111111",
    "authMethod":    "api_key"
  }
}
On /ws/market the greeting is just { "gateway": "market", "authMethod": "api_key", "protocolVersion": 2 } — no wallet context, since market data isn’t per-user.
Auth is validated at handshake only. Rotating a key does not invalidate an open socket; close and reopen to switch identity. A bad key closes with 4401 <reason> (e.g. 4401 api_key_revoked). Revoking a key closes every live socket bound to that keyId immediately via apikey:invalidate Redis pub/sub.

Command envelope

Every client-to-server message uses the same shape:
{ "id": 1, "cmd": "subscribe", "params": { /* ... */ } }
  • id — client-side correlation id; echoed in the server’s response
  • cmdsubscribe / update_subscription / unsubscribe / list_subscriptions / ping
  • 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:
ChannelIdentifier modelRequired scopePurpose
token_trade_matchesids = [tokenId, ...]low-latency tape from the matcher (off-chain match)
token_trade_settlementsids = [tokenId, ...]chain-confirmed settlements (OrderFilled indexed)
token_bookids = [tokenId, ...]public orderbook snapshots + updates
token_ohlcids = [tokenId, ...]rolling 5-second OHLC candles
condition_lifecycleids = [conditionId, ...]market lifecycle, pause/unpause, oracle, payout
systemids = ["platform_status"]platform-wide freeze / maintenance banner
Private, on /ws/user:
ChannelIdentifier modelRequired scopePurpose
user_ordersno ids (scoped to the key’s wallet)portfolio:readown order_placed / order_cancelled
user_fillsno ids (scoped to the key’s wallet)portfolio:readown user_fill (chain-confirmed fills)
vault_positionsids = [vaultAddress, ...]portfolio:readper-vault balance changes / split / merge / redeem
Detailed payloads + examples in Subscriptions and Messages.

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 (binary)
  • For neg-risk multi-outcome markets, each question has its own conditionId and yes/no token ids — resolve them through the on-chain NegRiskAdapter before subscribing
Then open /ws/market and subscribe by those native ids:
{
  "id": 1,
  "cmd": "subscribe",
  "params": {
    "subscriptions": [
      { "channel": "token_trade_matches",     "ids": ["12345..."] },
      { "channel": "token_trade_settlements", "ids": ["12345..."] },
      { "channel": "token_book",              "ids": ["12345..."] },
      { "channel": "condition_lifecycle",     "ids": ["0xabc..."] }
    ]
  }
}
Don’t subscribe by app symbol — symbols are app metadata, not the canonical on-chain identity.

What clients should NOT do

  • No symbol-based subscriptions. Use tokenId / conditionId.
  • 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.