Skip to main content
Use REST for snapshots, historical reads, and server-built transaction workflows. Use WebSockets for live exchange, market, orderbook, and trader-state updates. Use Solana RPC for blockhashes, simulation, transaction submission, confirmation, and custom account reads.

Default Pattern

  1. Fetch startup snapshots over REST.
  2. Start WebSocket subscriptions for exchange metadata, market stats, orderbooks, and trader state.
  3. Apply snapshots and deltas through SDK primitives.
  4. Build instructions from the exchange cache.
  5. Sign and send through the user’s wallet or signer service.
  6. Reconcile from trader-state streams; refetch REST snapshots after reconnects or sequence gaps.
import { createPhoenixClient, createTraderStateStore } from "@ellipsis-labs/rise";

const client = createPhoenixClient({
  apiUrl: "https://perp-api.phoenix.trade",
  rpcUrl: "https://api.mainnet-beta.solana.com",
  ws: { connectMode: "eager" },
  exchangeMetadata: { stream: true },
});

await client.exchange.ready();

const traderState = createTraderStateStore(client);
const trader = traderState.resource({
  authority: "AUTHORITY_PUBKEY",
  traderPdaIndex: 0,
});

const release = trader.retain();
await trader.ready();

Rate Limits

Most public market and trader reads do not require auth, but authenticated sessions receive higher API limits. Auth is required for referral-code onboarding and notifications. To avoid rate-limit pressure:
  • Prefer WebSocket streams for frequently changing state.
  • Share one process-level Phoenix client where possible.
  • Share one subscription per market or trader and fan out locally.
  • Cache exchange params with client.exchange or PhoenixMetadata.
  • Cache trader state with createTraderStateStore(client) or Trader::apply_update(...).
  • Use REST polling for slow reconciliation, not frame-by-frame UI updates.
  • Back off on 429 responses and retry after the server-provided delay when available.

Snapshots And Deltas

Do not hand-roll snapshot/delta application unless you are building SDK internals. In TypeScript:
  • client.exchange bootstraps from API or RPC, applies exchange WebSocket snapshots and deltas, and emits cache events.
  • createTraderStateStore(client) applies trader-state snapshots and deltas and exposes derived positions, orders, triggers, history, and marginInputs().
  • client.marketData() combines all-mids, market-stats, and mark-price streams into per-symbol rows.
In Rust:
  • PhoenixMetadata::new(exchange) creates the exchange cache from an HTTP snapshot.
  • metadata.apply_market_stats(&stats) updates market mark-price inputs used by margin math.
  • Trader::apply_update(&msg) applies trader-state WebSocket updates to a local trader container.
  • L2Book::apply_update(&msg) maintains an orderbook container.

Reconnects

WebSocket clients can reconnect, but your application still needs a reconciliation policy. After a reconnect, sequence gap, or prolonged disconnect:
  • Refresh exchange metadata over REST or wait for a fresh exchange snapshot.
  • Refetch or await a fresh trader-state snapshot before showing account health as final.
  • Drop stale L2 book state unless the SDK resource confirms it has been resynced.
  • Recompute margin only after both trader state and market prices are current.

Notifications

Notifications are an authenticated workflow. Use a Phoenix auth session when subscribing to user-specific notifications over REST or WebSocket. Public market streams should stay unauthenticated unless your integration needs authenticated API limits for the rest of its traffic. References: