> ## Documentation Index
> Fetch the complete documentation index at: https://docs.derive.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscriptions

> Real-time WebSocket channels: subscribe to a list of channel names and receive server-pushed notification frames.

Subscriptions deliver real-time updates over WebSocket. You call `subscribe` with a
list of channel names, and the server streams an update frame each time the
underlying data changes.

<Note>
  Subscriptions are **WebSocket-only**. The `subscribe` / `unsubscribe` methods
  and the notification frames they produce are not available over HTTP POST. See
  [Endpoints](/getting-started/introduction#endpoints) for the WebSocket endpoints.
</Note>

## Notification model

A subscription update is a JSON-RPC-style **notification frame**: it has a `method` of
`"subscription"` and **no `id`** (it is server-pushed, not a reply to any request). The
payload carries the `channel` that produced it and the `data`:

```json theme={null}
{
  "method": "subscription",
  "params": {
    "channel": "trades.ETH-PERP",
    "data": {}
  }
}
```

The shape of `data` depends on the channel. For the exact per-channel payload schema,
see the generated **Subscriptions reference** under the API Reference tab.

## Subscribing

Send a `subscribe` request with a `channels` array. The response reports a per-channel
`status` and the connection's full `current_subscriptions` set.

<CodeGroup>
  ```typescript TypeScript (SDK) theme={null}
  import { channel, DeriveClient } from '@derivexyz/derive-ts';

  const client = new DeriveClient({ network: 'mainnet' });
  // Subscriptions live on the websocket; connect() must come first.
  await client.connect();

  // channel() builds the concrete channel name from its template and
  // carries the payload type, so each handler below is fully typed.
  const trades = channel('trades.{instrument_name}', {
  instrument_name: 'ETH-PERP',
  });
  const book = channel('orderbook.{instrument_name}.{group}.{depth}', {
  instrument_name: 'ETH-PERP',
  group: '10',
  depth: '20',
  });

  const tradesSub = await client.subscriptions.subscribe(trades, (batch) => {
  for (const trade of batch) {
  console.log(
  `trade: ${trade.direction} ${trade.trade_amount} @ ${trade.trade_price}`
  );
  }
  });

  const bookSub = await client.subscriptions.subscribe(book, (snapshot) => {
  console.log(
  `book #${snapshot.publish_id}: ${snapshot.bids.length} bids / ${snapshot.asks.length} asks`
  );
  });

  // Later, drop the subscriptions (the last handler on a channel sends unsubscribe):
  await tradesSub.unsubscribe();
  await bookSub.unsubscribe();

  ```

  ```python Python (SDK) theme={null}
  import asyncio

  from derive_py import WebSocketClient


  async def main():
      client = WebSocketClient.from_env()
      # Subscriptions live on the websocket; connect() must come first.
      await client.connect()

      # One method per channel shape, each with a typed payload.
      def on_trades(batch):
          for trade in batch:
              print(f"trade: {trade.direction} {trade.trade_amount} @ {trade.trade_price}")

      def on_book(snapshot):
          print(f"book #{snapshot.publish_id}: {len(snapshot.bids)} bids / {len(snapshot.asks)} asks")

      await client.public_channels.trades_by_instrument_name(
          instrument_name="ETH-PERP", callback=on_trades
      )
      await client.public_channels.orderbook_group_depth_by_instrument_name(
          instrument_name="ETH-PERP", group=10, depth=20, callback=on_book
      )

      await asyncio.sleep(30)

      # There is no per-channel unsubscribe yet; disconnect() ends every
      # subscription on the socket.
      await client.disconnect()


  asyncio.run(main())
  ```

  ```rust Rust (SDK) theme={null}
  use derive_rs::{Environment, WsClient};
  use tokio_stream::StreamExt;

  #[tokio::main]
  async fn main() -> Result<(), Box<dyn std::error::Error>> {
      let client = WsClient::new_public(Environment::Mainnet).await?;

      // Each subscribe returns a typed stream of that channel's payload.
      let mut trades = client
          .subscriptions()
          .market_data()
          .trades_by_instrument("ETH-PERP")
          .await?;
      let mut book = client
          .subscriptions()
          .market_data()
          .orderbook("ETH-PERP", "10", "20")
          .await?;

      loop {
          tokio::select! {
              Some(Ok(trade)) = trades.next() => {
                  println!(
                      "trade: {:?} {} @ {}",
                      trade.direction, trade.trade_amount, trade.trade_price
                  );
              }
              Some(Ok(snapshot)) = book.next() => {
                  println!(
                      "book #{}: {} bids / {} asks",
                      snapshot.publish_id, snapshot.bids.len(), snapshot.asks.len()
                  );
              }
              else => break,
          }
      }

      // Later, drop a subscription by channel name:
      client.unsubscribe("orderbook.ETH-PERP.10.20").await?;
      Ok(())
  }
  ```

  ```json Request theme={null}
  {
    "id": 1,
    "method": "subscribe",
    "params": {
      "channels": ["trades.ETH-PERP", "orderbook.ETH-PERP.10.20"]
    }
  }
  ```

  ```json Response theme={null}
  {
    "id": 1,
    "result": {
      "status": {
        "trades.ETH-PERP": "ok",
        "orderbook.ETH-PERP.10.20": "ok"
      },
      "current_subscriptions": ["trades.ETH-PERP", "orderbook.ETH-PERP.10.20"]
    }
  }
  ```
</CodeGroup>

Each `status` entry is `"ok"`, `"already subscribed"`, or an error string for that
channel. If **every** requested channel is invalid, the whole call fails with
[error code 13000 — Invalid channels](/error-codes).

Private channels require an authenticated connection whose session key is authorized
for the target subaccount. See [Authentication](/authentication/session-login) for session login.

## Unsubscribing

Send `unsubscribe` with a `channels` array to drop specific channels. Omit `channels`
(or pass `null`) to unsubscribe from **all** channels on the connection. The response
returns the per-channel `status` and the `remaining_subscriptions` set.

```json theme={null}
{
  "id": 2,
  "method": "unsubscribe",
  "params": { "channels": ["trades.ETH-PERP"] }
}
```

## Channel-name template glossary

Channel names are dot-delimited. Segments in `{braces}` are parameters you fill in.

| Parameter           | Meaning                                     | Values                                                                                                    |
| ------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `{instrument_name}` | An instrument identifier (e.g. `ETH-PERP`)  | See [Instrument names](/trading/instrument-names)                                                         |
| `{instrument_type}` | Instrument class                            | `erc20`, `option`, `perp`                                                                                 |
| `{currency}`        | Settlement/collateral currency (e.g. `ETH`) | See [`public/get_risk_universes`](/trading/managers-and-risk-universes#reading-public-get-risk-universes) |
| `{group}`           | Orderbook price grouping                    | `1`, `10`, `100`                                                                                          |
| `{depth}`           | Orderbook levels per side                   | `1`, `10`, `20`, `100`                                                                                    |
| `{interval}`        | Slim-ticker publish interval (ms)           | `100`, `1000`                                                                                             |
| `{subaccount_id}`   | Numeric subaccount id                       | Your authenticated subaccount                                                                             |
| `{wallet}`          | Wallet address                              | Your authenticated wallet                                                                                 |

<Note>
  The legacy `ticker.{instrument_name}.{interval}` channel is deprecated —
  subscribing to it is rejected. Use `ticker_slim.*` instead.
</Note>

## Public channels

No authentication required.

| Channel                     | Address template                              |
| --------------------------- | --------------------------------------------- |
| Orderbook                   | `orderbook.{instrument_name}.{group}.{depth}` |
| Slim ticker                 | `ticker_slim.{instrument_name}.{interval}`    |
| Spot feed                   | `spot_feed.{currency}`                        |
| Trades (by instrument)      | `trades.{instrument_name}`                    |
| Trades (by type + currency) | `trades.{instrument_type}.{currency}`         |
| Auctions                    | `auctions.watch`                              |
| Margin                      | `margin.watch`                                |

<Warning>
  `margin.watch` is an all-users firehose of margin and mark-to-market state. It
  is intended for administrative/monitoring use, not per-account data.
</Warning>

<Note>
  Trade events are published at match time and carry the full trade detail
  (counterparty wallet and subaccount, fees, realized PnL). Settlement
  lifecycle (`batch_status`, `tx_hash`) is not streamed — poll
  [`public/get_trade_history`](/api-reference/market-data/public-get-trade-history),
  which returns trades in every batch state by default.
</Note>

### Slim ticker payload

Both the `ticker_slim.*` channel and `public/get_ticker` return the same compact snapshot, keyed by single
letters to keep the feed small:

| Key              | Meaning                                                                         |
| ---------------- | ------------------------------------------------------------------------------- |
| `t`              | Snapshot timestamp (ms since epoch)                                             |
| `a` / `A`        | Best ask price / amount available at the best ask                               |
| `b` / `B`        | Best bid price / amount available at the best bid                               |
| `M`              | Mark price                                                                      |
| `I`              | Index price                                                                     |
| `f`              | Current hourly funding rate (perps; `null` otherwise)                           |
| `minp` / `maxp`  | Min / max matchable price (the order price band)                                |
| `stats`          | 24h trading statistics (`c` change, `v` volume, `oi` open interest, …)          |
| `option_pricing` | Greeks and implied vols for options (`d` delta, `g` gamma, `v` vega, `i` IV, …) |

The `public/get_ticker` response schema in the **API Reference** tab carries every field's full
description.

## Private channels

Require an [authenticated](/authentication/session-login) connection. `{subaccount_id}` channels
stream data for one of your subaccounts; `{wallet}` channels stream data for your wallet.

| Channel            | Address template              |
| ------------------ | ----------------------------- |
| Balance updates    | `{subaccount_id}.balances`    |
| Order updates      | `{subaccount_id}.orders`      |
| Trade updates      | `{subaccount_id}.trades`      |
| Quote updates      | `{subaccount_id}.quotes`      |
| Best-quote updates | `{subaccount_id}.best.quotes` |
| RFQ updates        | `{wallet}.rfqs`               |

<Tip>
  The **Subscriptions reference** in the API Reference tab is generated from the
  AsyncAPI spec and documents the exact `data` payload for every channel above.
</Tip>


## Related topics

- [Error Codes](/error-codes.md)
- [RFQ Trading](/trading/rfq.md)
- [Connecting over WebSocket](/connecting.md)
