Skip to main content
ExchangeSocketClient exposes a unified API for subscribing to live data from any supported exchange. Every SubscribeTo* method can target a single exchange or fan out to multiple exchanges in one call. Handlers receive a DataEvent<T> which carries the payload along with the source exchange name and timestamp.

Setup

using CryptoClients.Net;
using CryptoExchange.Net.SharedApis;

// Direct construction
var socketClient = new ExchangeSocketClient();

// Or inject IExchangeSocketClient via DI
// builder.Services.AddCryptoClients();

Ticker updates

Subscribe to the latest price and 24 h statistics for a symbol.
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");

// Single exchange
var result = await socketClient.SubscribeToTickerUpdatesAsync(
    "Binance",
    new SubscribeTickerRequest(symbol),
    data => Console.WriteLine($"[{data.Exchange}] {data.Data.Symbol} last: {data.Data.LastPrice}"));

// Multiple exchanges in one call
var results = await socketClient.SubscribeToTickerUpdatesAsync(
    new SubscribeTickerRequest(symbol),
    data => Console.WriteLine($"[{data.Exchange}] {data.Data.Symbol} last: {data.Data.LastPrice}"),
    exchanges: ["Binance", "Bybit", "OKX"]);

foreach (var r in results.Where(r => !r.Success))
    Console.WriteLine($"{r.Exchange} error: {r.Error}");

All-ticker subscription

Some exchanges support subscribing to all symbols at once:
var result = await socketClient.SubscribeToAllTickerUpdatesAsync(
    "Binance",
    new SubscribeAllTickersRequest(),
    data =>
    {
        foreach (var ticker in data.Data)
            Console.WriteLine($"{ticker.Symbol}: {ticker.LastPrice}");
    });

Trade updates

Subscribe to a stream of public trades (fills) for a symbol. The handler receives a SharedTrade[] batch.
var symbol = new SharedSymbol(TradingMode.PerpetualLinear, "ETH", "USDT");

// Subscribe on multiple exchanges — from the StreamTrades example
var results = await socketClient.SubscribeToTradeUpdatesAsync(
    new SubscribeTradeRequest(symbol),
    update =>
    {
        foreach (var trade in update.Data)
            Console.WriteLine($"{update.Exchange,-10} | {trade.Quantity} @ {trade.Price}");
    },
    exchanges: ["Binance", "HTX", "OKX"]);

foreach (var r in results)
    Console.WriteLine($"{r.Exchange} subscribe: {(r.Success ? "OK" : r.Error)}");

Kline / candlestick updates

var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");

var result = await socketClient.SubscribeToKlineUpdatesAsync(
    "Binance",
    new SubscribeKlineRequest(symbol, SharedKlineInterval.OneMinute),
    update => Console.WriteLine(
        $"[{update.Exchange}] {update.Data.OpenTime:HH:mm} " +
        $"O:{update.Data.OpenPrice} H:{update.Data.HighPrice} " +
        $"L:{update.Data.LowPrice} C:{update.Data.ClosePrice}"));

// Subscribe across exchanges
var results = await socketClient.SubscribeToKlineUpdatesAsync(
    new SubscribeKlineRequest(symbol, SharedKlineInterval.FiveMinutes),
    update => Console.WriteLine($"[{update.Exchange}] Close: {update.Data.ClosePrice}"),
    exchanges: ["Bybit", "Kraken", "OKX"]);

Order book updates

Receive periodic order book snapshots (not a locally managed book — use ExchangeOrderBookFactory for that).
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");

var result = await socketClient.SubscribeToOrderBookUpdatesAsync(
    "Binance",
    new SubscribeOrderBookRequest(symbol, 20),
    update =>
    {
        var book = update.Data;
        Console.WriteLine($"[{update.Exchange}] Asks: {book.Asks.Count()}, Bids: {book.Bids.Count()}");
    });

Book ticker updates

Subscribe to best ask / best bid price updates (lower bandwidth than a full order book):
var result = await socketClient.SubscribeToBookTickerUpdatesAsync(
    "Bybit",
    new SubscribeBookTickerRequest(symbol),
    update => Console.WriteLine(
        $"[{update.Exchange}] Best ask: {update.Data.BestAskPrice} / Best bid: {update.Data.BestBidPrice}"));

The DataEvent<T> handler pattern

Every subscription handler receives a DataEvent<T>. Key properties:
PropertyTypeDescription
DataTThe deserialized payload
ExchangestringThe name of the exchange that sent this update
TimestampDateTimeUTC time the update was received
OriginalDatastring?Raw JSON (only when OutputOriginalData is enabled)
Action<DataEvent<SharedSpotTicker>> handler = update =>
{
    string exchange  = update.Exchange;
    DateTime time    = update.Timestamp;
    SharedSpotTicker ticker = update.Data;

    Console.WriteLine($"[{time:HH:mm:ss}] [{exchange}] {ticker.Symbol}: {ticker.LastPrice}");
};

Managing subscriptions

SubscribeTo* methods return ExchangeResult<UpdateSubscription> (single exchange) or ExchangeResult<UpdateSubscription>[] (multi-exchange). Use UpdateSubscription to close individual subscriptions:
var result = await socketClient.SubscribeToTickerUpdatesAsync(
    "Binance",
    new SubscribeTickerRequest(symbol),
    data => Console.WriteLine(data.Data.LastPrice));

if (result.Success)
{
    var subscription = result.Data;

    // Close just this subscription
    await subscription.CloseAsync();
}

Unsubscribe everything

To disconnect all subscriptions and close all WebSocket connections at once:
await socketClient.UnsubscribeAllAsync();

Monitoring connection state

Console.WriteLine($"Active connections:   {socketClient.CurrentConnections}");
Console.WriteLine($"Active subscriptions: {socketClient.CurrentSubscriptions}");
Console.WriteLine($"Incoming data rate:   {socketClient.IncomingKbps:F1} kb/s");
WebSocket connections are multiplexed — multiple subscriptions share a single connection where the exchange protocol allows it. Calling CloseAsync on an UpdateSubscription only unsubscribes that stream; it does not necessarily close the underlying connection if other subscriptions are still active on it.

Build docs developers (and LLMs) love