Skip to main content

Connection

Connect to the Phoenix WebSocket endpoint:
wss://perp-api.phoenix.trade/v1/ws
The Rust SDK uses PHOENIX_WS_URL when it is set. Otherwise, it derives the WebSocket URL from PHOENIX_API_URL by switching http to ws and using /v1/ws. Messages are UTF-8 JSON objects.

Client messages

Subscribe with a type and subscription envelope:
{
  "type": "subscribe",
  "subscription": {
    "channel": "orderbook",
    "symbol": "SOL"
  }
}
Unsubscribe with the same subscription object:
{
  "type": "unsubscribe",
  "subscription": {
    "channel": "orderbook",
    "symbol": "SOL"
  }
}

Supported channels

ChannelSubscription fieldsResponse type
allMidsnoneAllMidsData
exchangeoptional encodingExchangeMessage
fundingRatesymbolFundingRateMessage
orderbooksymbol, optional bypassExecutionBandL2BookUpdate
traderStateauthority, traderPdaIndexTraderStateServerMessage
marketsymbolMarketStatsUpdate
tradessymbolTradesMessage
candlessymbol, timeframeCandleData

Control responses

The server confirms successful subscriptions with a subscriptionConfirmed message:
{
  "type": "subscriptionConfirmed",
  "subscription": {
    "channel": "orderbook",
    "symbol": "SOL"
  }
}
Subscription-level errors use subscriptionError:
{
  "type": "subscriptionError",
  "subscription": {
    "channel": "orderbook",
    "symbol": "SOL"
  },
  "code": "invalid_subscription",
  "message": "invalid subscription"
}
Server errors use the error channel:
{
  "channel": "error",
  "code": 400,
  "error": "unknown channel"
}

Market subscriptions

Exchange

Use the exchange channel to keep local exchange and market-parameter metadata in sync. The server sends an initial snapshot followed by ordered deltas when exchange keys, exchange status, markets, or market parameters change.
{
  "type": "subscribe",
  "subscription": {
    "channel": "exchange",
    "encoding": "json"
  }
}
The encoding field is optional. Omit it to use the default compressed snapshot response:
{
  "channel": "exchange",
  "messageType": "encodedSnapshot",
  "version": 1,
  "sequenceNumber": "123",
  "slot": 123456789,
  "slotIndex": 0,
  "reason": "snapshot",
  "encoding": "base64+zstd",
  "payload": "..."
}
Set encoding to json to receive the initial snapshot as JSON. The examples below are abbreviated to show the fields relevant to cache synchronization:
{
  "channel": "exchange",
  "messageType": "snapshot",
  "version": 1,
  "sequenceNumber": "123",
  "slot": 123456789,
  "slotIndex": 0,
  "reason": "snapshot",
  "exchange": {
    "programId": "program-pubkey",
    "globalConfig": "global-config-pubkey",
    "active": true,
    "gated": false
  },
  "markets": [
    {
      "symbol": "SOL",
      "assetId": 1,
      "marketStatus": "active",
      "marketPubkey": "market-pubkey",
      "splinePubkey": "spline-pubkey"
    }
  ]
}
After the snapshot, apply each delta with the next sequenceNumber to your local cache. If a sequence number is skipped, resubscribe and rebuild from a fresh snapshot.
{
  "channel": "exchange",
  "messageType": "delta",
  "version": 1,
  "sequenceNumber": "124",
  "slot": 123456790,
  "slotIndex": 0,
  "ops": [
    {
      "kind": "marketAdded",
      "market": {
        "symbol": "ETH",
        "assetId": 2,
        "marketStatus": "active",
        "marketPubkey": "market-pubkey",
        "splinePubkey": "spline-pubkey"
      }
    },
    {
      "kind": "marketClosed",
      "symbol": "SOL",
      "previous_market_status": "active",
      "finalized_mark_price": "150000000"
    }
  ]
}
Handle exchange deltas as cache mutations:
Delta kindSync behavior
marketAddedAdd or replace the market config in the local market map.
marketStatusChangedUpdate the market’s marketStatus.
marketClosedMark the market as closed and retain the finalized mark price if your client needs settlement context.
marketTombstonedMark the market as tombstoned and stop treating it as tradeable.
marketDeletedRemove the market from the local market map.
marketParameterUpdatedPatch the market’s risk, funding, fee, cap, or commodity metadata fields from the update payload.
exchangeStatusChangedUpdate exchange-level active, gated, and status feature flags.
exchangeKeysUpdatedReplace exchange-level account and authority keys.

All mids

{
  "type": "subscribe",
  "subscription": {
    "channel": "allMids"
  }
}
{
  "channel": "allMids",
  "mids": {
    "SOL": 150.25,
    "BTC": 65000.5
  },
  "slot": 123456789,
  "slotIndex": 0
}

Funding rate

{
  "type": "subscribe",
  "subscription": {
    "channel": "fundingRate",
    "symbol": "SOL"
  }
}
{
  "channel": "fundingRate",
  "symbol": "SOL",
  "funding": 0.0000125
}

Orderbook

Set bypassExecutionBand to true to receive the full orderbook during commodities after-hours, including price levels outside the tradeable execution band.
{
  "type": "subscribe",
  "subscription": {
    "channel": "orderbook",
    "symbol": "SOL",
    "bypassExecutionBand": false
  }
}
{
  "channel": "orderbook",
  "symbol": "SOL",
  "orderbook": {
    "bids": [[150.25, 100.0], [150.2, 200.0]],
    "asks": [[150.3, 150.0], [150.35, 250.0]],
    "mid": 150.275
  },
  "bypassExecutionBand": false
}

Market stats

{
  "type": "subscribe",
  "subscription": {
    "channel": "market",
    "symbol": "SOL"
  }
}
{
  "channel": "market",
  "symbol": "SOL",
  "openInterest": 1000.0,
  "markPx": 150.1,
  "midPx": 150.12,
  "oraclePx": 150.0,
  "prevDayPx": 148.7,
  "dayNtlVlm": 2500000.0,
  "funding": 0.0000125
}

Trades

{
  "type": "subscribe",
  "subscription": {
    "channel": "trades",
    "symbol": "SOL"
  }
}
{
  "channel": "trades",
  "symbol": "SOL",
  "trades": [
    {
      "slot": "123456789",
      "slotIndex": 5,
      "timestamp": "1775578550",
      "symbol": "SOL",
      "taker": "taker-pubkey",
      "tradeSequenceNumber": "100",
      "side": "bid",
      "baseLotsFilled": "1000",
      "quoteLotsFilled": "150000",
      "feeInQuoteLots": "30",
      "baseAmount": 10.0,
      "quoteAmount": 1500.0,
      "numFills": 2
    }
  ]
}

Candles

Supported timeframes are 1s, 5s, 1m, 5m, 15m, 30m, 1h, 4h, and 1d.
{
  "type": "subscribe",
  "subscription": {
    "channel": "candles",
    "symbol": "SOL",
    "timeframe": "1m"
  }
}
The server sends candle updates with channel set to candle:
{
  "channel": "candle",
  "symbol": "SOL",
  "timeframe": "1m",
  "candle": {
    "time": 1727181985,
    "low": 149.8,
    "high": 151.5,
    "open": 150.25,
    "close": 150.9,
    "volume": 1234.56,
    "tradeCount": 89
  }
}

Trader subscriptions

Trader state

{
  "type": "subscribe",
  "subscription": {
    "channel": "traderState",
    "authority": "wallet-pubkey",
    "traderPdaIndex": 0
  }
}
Trader state messages are either snapshot or delta payloads.
{
  "channel": "traderState",
  "authority": "wallet-pubkey",
  "traderPdaIndex": 0,
  "slot": 123456789,
  "messageType": "snapshot",
  "version": 1,
  "capabilities": {
    "flags": 63,
    "state": "active",
    "capabilities": {
      "placeLimitOrder": { "immediate": true },
      "placeMarketOrder": { "immediate": true },
      "riskIncreasingTrade": { "immediate": true },
      "riskReducingTrade": { "immediate": true },
      "depositCollateral": { "immediate": true },
      "withdrawCollateral": { "immediate": true }
    }
  },
  "makerFeeOverrideMultiplier": 1.0,
  "takerFeeOverrideMultiplier": 1.0,
  "subaccounts": []
}
{
  "channel": "traderState",
  "authority": "wallet-pubkey",
  "traderPdaIndex": 0,
  "slot": 123456790,
  "messageType": "delta",
  "deltas": [
    {
      "subaccountIndex": 0,
      "sequence": 42,
      "collateral": "1000000",
      "positions": [],
      "orders": [],
      "splines": [],
      "tradeHistory": [],
      "orderHistory": []
    }
  ]
}