◈   ⌘ api · Intermediate

CryptoCompare WebSocket API: Real-Time Crypto Data Guide

A practical guide to the CryptoCompare WebSocket API — how to connect, subscribe to live price feeds, and use real-time data in Python and JavaScript trading tools.

Uncle Solieditor · voc · 06.03.2026 ·views 25
◈   Contents
  1. → What Is the CryptoCompare WebSocket API?
  2. → Getting Your API Key and Authenticating
  3. → Subscribing to Live Streams in JavaScript
  4. → Parsing the Data Feed Correctly
  5. → Wiring WebSocket Data Into a Real Trading Workflow
  6. → Frequently Asked Questions
  7. → Conclusion

Polling a REST endpoint every few seconds is already stale by the time the response arrives. In crypto markets — where a Binance liquidation cascade or a surprise OKX listing can move a token 10% in thirty seconds — latency is a P&L problem, not just a technical footnote. The CryptoCompare WebSocket API solves this by pushing data to you as it happens, maintaining a persistent connection that streams trades, aggregate prices, and order book updates across hundreds of pairs simultaneously. This guide walks through authentication, subscription setup, data parsing, and wiring it all into something that actually trades.

What Is the CryptoCompare WebSocket API?

CryptoCompare aggregates market data from over 200 exchanges — including Binance, Bybit, OKX, Coinbase, KuCoin, and Gate.io — and exposes it through a unified WebSocket endpoint at wss://streamer.cryptocompare.com/v2. Unlike REST, you open the connection once and the server continuously pushes updates without you having to ask. There are three channels most traders care about: the AGGREGATE channel (consolidated price across all supported venues), the RAW TRADE channel (individual trades from a specific exchange as they happen), and the ORDER BOOK channel (top-of-book bid/ask updates). The cryptocompare websocket api is the backbone of many algorithmic setups, from simple price alert bots to arbitrage engines that compare Bybit and Binance spot prices tick by tick.

Free API keys have rate limits and may not include all channels. For production bots monitoring more than a handful of pairs, a paid plan is worth it — the free tier is fine for learning and prototyping.

Getting Your API Key and Authenticating

Sign up at the CryptoCompare developer portal, create a project, and generate an API key. Authentication is refreshingly simple — you append the key to the WebSocket URL as a query parameter. No OAuth, no token refresh cycle, no bearer headers. Here is how to establish the initial connection in Python using the websockets library and confirm the handshake:

import asyncio
import websockets
import json

API_KEY = "your_cryptocompare_api_key_here"
WS_URL = f"wss://streamer.cryptocompare.com/v2?api_key={API_KEY}"

async def connect():
    try:
        async with websockets.connect(WS_URL) as ws:
            print("Connected to CryptoCompare WebSocket")

            # Subscribe to BTC and ETH aggregate prices
            sub_message = {
                "action": "SubAdd",
                "subs": [
                    "5~CCCAGG~BTC~USD",
                    "5~CCCAGG~ETH~USD",
                    "5~CCCAGG~SOL~USD"
                ]
            }
            await ws.send(json.dumps(sub_message))
            print("Subscriptions active")

            async for message in ws:
                data = json.loads(message)
                # Skip heartbeats (TYPE 999) and subscription confirmations
                if data.get("TYPE") not in ("5", "0", "2"):
                    continue
                print(data)

    except websockets.exceptions.ConnectionClosedError as e:
        print(f"Connection closed unexpectedly: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")

asyncio.run(connect())

The subscription string format is {channel}~{exchange}~{fsym}~{tsym}. Channel 5 is aggregate, channel 0 is raw trades from a named exchange, channel 2 is order book. So '0~Binance~BTC~USDT' streams every individual trade on Binance's BTC/USDT pair in real time, while '5~CCCAGG~BTC~USD' gives you the consolidated price across all CryptoCompare venues. Use the aggregate channel for general monitoring; drop to channel 0 when you need per-exchange granularity for arbitrage or execution decisions.

Subscribing to Live Streams in JavaScript

If you're building a Node.js service or a browser dashboard, the native WebSocket API connects cleanly. The example below subscribes to multiple pairs and handles reconnection automatically — a non-negotiable feature for anything running in production:

const API_KEY = 'your_cryptocompare_api_key_here';
const WS_URL = `wss://streamer.cryptocompare.com/v2?api_key=${API_KEY}`;

const SUBSCRIPTIONS = [
  '5~CCCAGG~BTC~USD',
  '5~CCCAGG~ETH~USD',
  '5~CCCAGG~SOL~USD',
  '0~Binance~BTC~USDT'   // raw trades from Binance specifically
];

function connect() {
  const ws = new WebSocket(WS_URL);

  ws.onopen = () => {
    console.log('Connected');
    ws.send(JSON.stringify({ action: 'SubAdd', subs: SUBSCRIPTIONS }));
  };

  ws.onmessage = (event) => {
    const data = JSON.parse(event.data);

    if (data.TYPE === '5') {
      // Aggregate price update
      const { FROMSYMBOL, TOSYMBOL, PRICE, FLAGS } = data;
      if (PRICE) {
        const dir = (FLAGS & 1) ? '▲' : (FLAGS & 2) ? '▼' : '-';
        console.log(`${FROMSYMBOL}/${TOSYMBOL}: $${PRICE.toLocaleString()} ${dir}`);
      }
    } else if (data.TYPE === '0') {
      // Raw trade from named exchange
      console.log(`Trade @ ${data.M}: ${data.Q} ${data.FSYM} at $${data.P}`);
    }
    // Ignore TYPE 999 heartbeats silently
  };

  ws.onerror = (err) => console.error('WS error:', err);

  ws.onclose = () => {
    console.log('Disconnected. Reconnecting in 5s...');
    setTimeout(connect, 5000);
  };
}

connect();
Always implement reconnection logic. WebSocket connections drop — network hiccups, server restarts, idle timeouts. A bot that doesn't reconnect is a bot that silently stops working, often at the worst possible moment.

Parsing the Data Feed Correctly

The aggregate channel messages use compact single-letter field names to minimize bandwidth. PRICE is the current price, VOLUME24HOUR is the rolling 24-hour volume, CHANGE24HOUR is the price delta from yesterday, and FLAGS is a bitmask indicating tick direction (1 = up, 2 = down, 4 = unchanged). Critically, CryptoCompare only sends fields that changed since the last message — not the full price object every time. This means you must maintain a local state and merge incoming updates into it. Treating each message as a complete snapshot is one of the most common bugs in new CryptoCompare integrations.

import asyncio
import websockets
import json
from collections import defaultdict

API_KEY = "your_cryptocompare_api_key_here"
WS_URL = f"wss://streamer.cryptocompare.com/v2?api_key={API_KEY}"

TRACKED_FIELDS = {"PRICE", "VOLUME24HOUR", "CHANGE24HOUR", "FLAGS", "LASTUPDATE"}

class PriceFeed:
    def __init__(self):
        self.state = defaultdict(dict)

    def update(self, data: dict) -> dict | None:
        if data.get("TYPE") != "5":
            return None
        fsym = data.get("FROMSYMBOL", "")
        tsym = data.get("TOSYMBOL", "")
        if not fsym or not tsym:
            return None
        key = f"{fsym}/{tsym}"
        # Merge only fields that arrived — partial updates are normal
        self.state[key].update({k: v for k, v in data.items() if k in TRACKED_FIELDS})
        return self.state[key]

    def is_up(self, pair: str) -> bool:
        return bool(self.state.get(pair, {}).get("FLAGS", 0) & 1)

async def stream(feed: PriceFeed):
    subs = [
        "5~CCCAGG~BTC~USD",
        "5~CCCAGG~ETH~USD",
        "5~CCCAGG~SOL~USD",
        "5~CCCAGG~BNB~USD"
    ]
    while True:
        try:
            async with websockets.connect(WS_URL) as ws:
                await ws.send(json.dumps({"action": "SubAdd", "subs": subs}))
                async for raw in ws:
                    try:
                        data = json.loads(raw)
                        pair_state = feed.update(data)
                        if pair_state and "PRICE" in pair_state:
                            fsym = data.get("FROMSYMBOL")
                            tsym = data.get("TOSYMBOL")
                            pair = f"{fsym}/{tsym}"
                            arrow = "▲" if feed.is_up(pair) else "▼"
                            print(f"{pair}: ${pair_state['PRICE']:,.4f} {arrow}")
                    except (json.JSONDecodeError, KeyError, TypeError) as e:
                        # Malformed or unexpected message — log and continue
                        print(f"Parse warning: {e}")
                        continue
        except websockets.exceptions.ConnectionClosedError:
            print("Connection lost. Retrying in 3s...")
            await asyncio.sleep(3)

if __name__ == "__main__":
    asyncio.run(stream(PriceFeed()))

Wiring WebSocket Data Into a Real Trading Workflow

Raw price data becomes useful when it drives decisions. The most common patterns built on top of the cryptocompare websocket api are price alert systems, threshold-based signal triggers, and arbitrage spread monitors. Platforms like VoiceOfChain use real-time data streams exactly this way — ingesting continuous price feeds and surfacing actionable signals to traders the moment conditions are met, without the latency of polling-based approaches.

For a concrete example: monitoring whether BTC's price on Binance diverges more than 0.15% from OKX. Subscribe to both raw trade channels simultaneously — '0~Binance~BTC~USDT' and '0~OKX~BTC~USDT' — maintain separate price states for each exchange, and compute the spread on every incoming update. When the spread exceeds the threshold, fire an alert or trigger an execution. This runs comfortably in a single async Python process and scales to dozens of pairs with minimal overhead.

A few things that bite people in production: CryptoCompare limits concurrent subscriptions based on API tier, so be deliberate about which pairs you need. The aggregate channel already consolidates Bybit, Gate.io, KuCoin, OKX, Binance, and others — for most price monitoring use cases, channel 5 is sufficient. Only use channel 0 when you specifically need per-exchange granularity. Also, TYPE 999 heartbeat messages arrive periodically to keep the connection alive — your parser should ignore them rather than trying to process them as price data.

CryptoCompare WebSocket Channel Reference
ChannelType CodeDescriptionExample Subscription
Aggregate Index5Consolidated price across all exchanges5~CCCAGG~BTC~USD
Raw Trade0Individual trades from a specific exchange0~Binance~BTC~USDT
Order Book L22Top-of-book bid/ask updates2~Binance~BTC~USDT
Heartbeat999Server keep-alive ping — ignore in parserReceived automatically

Frequently Asked Questions

Is the CryptoCompare WebSocket API free to use?
Yes, CryptoCompare offers a free tier with API key access covering the aggregate and trade channels. Free plans have rate limits and restrict the number of concurrent subscriptions. For production bots monitoring many pairs continuously, a paid plan is the practical choice.
What is the difference between channel 5 (aggregate) and channel 0 (raw trade)?
Channel 5 gives you a consolidated price computed across all exchanges CryptoCompare tracks — ideal for general monitoring. Channel 0 streams individual trades from a specific named exchange like Binance or OKX as they happen, which you need when exact per-exchange data matters, such as for arbitrage spread calculations.
Why are some fields missing from the messages I receive?
This is by design — CryptoCompare only sends fields that changed since the last update, not a full price object every time. You must maintain a local state dictionary and merge each incoming message into it. The Python PriceFeed class in this guide demonstrates the correct approach.
How should I handle WebSocket disconnections in production?
Implement reconnection with a short fixed delay (3-5 seconds) for simple setups, or exponential backoff for more resilient systems. Also monitor for absence of any messages including TYPE 999 heartbeats — if nothing arrives for 30+ seconds, proactively close and reconnect rather than waiting for the OS to detect the dead connection.
Can I track altcoins on Gate.io or KuCoin using this API?
Yes. CryptoCompare aggregates data from both Gate.io and KuCoin. For the raw trade channel, use the exchange name exactly as CryptoCompare lists it in their exchange directory. The aggregate channel (5~CCCAGG) automatically includes both exchanges in its consolidated price without any extra configuration.
Is the CryptoCompare WebSocket API suitable for algorithmic trading?
It is well-suited for medium-frequency strategies — signal generation, spread monitoring, alert triggers, and execution event sourcing. For true sub-millisecond HFT you would need colocation and direct exchange feeds. For anything from second-level to minute-level decision making, the CryptoCompare WebSocket delivers more than adequate speed and reliability.

Conclusion

The CryptoCompare WebSocket API is one of the cleanest ways to get a broad, reliable real-time price feed without maintaining individual connections to Binance, Bybit, OKX, and a dozen other exchanges separately. The subscription model is straightforward, the aggregate channel covers the vast majority of monitoring use cases, and the Python and JavaScript patterns in this guide give you a working foundation to build on. Start with the connection template, get data flowing, then layer your logic on top — whether that is alerting, signal generation, or feeding a platform like VoiceOfChain that surfaces real-time trading signals from live market data. The infrastructure is the easy part; what you do with the stream is where the edge lives.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies