◈   ⌘ api · Intermediate

KuCoin API WebSocket: Real-Time Trading Data Guide

Master KuCoin's WebSocket API to stream live prices, order books, and trade data in real time. Includes Python code for connection, authentication, and resilient reconnection logic.

Uncle Solieditor · voc · 14.03.2026 ·views 59
◈   Contents
  1. → Why WebSocket Beats REST for Real-Time Trading
  2. → Connecting to the KuCoin WebSocket API
  3. → Subscribing to Market Data Channels
  4. → Authenticating Private WebSocket Channels
  5. → Building Reconnection-Resilient WebSocket Connections
  6. → Frequently Asked Questions

Real-time market data is the foundation of any serious trading strategy. REST APIs are fine for placing orders or fetching historical candles, but polling for live prices means hammering servers hundreds of times per minute — most responses returning identical data. KuCoin's WebSocket API solves this: one persistent connection delivers a continuous stream of ticks, order book changes, and trade events with millisecond latency. Whether you're building an algorithmic bot, a custom dashboard, or integrating alerts from a platform like VoiceOfChain, the KuCoin WebSocket API is the right tool for the job.

Why WebSocket Beats REST for Real-Time Trading

REST is request-response: you ask, the server answers, the connection closes. For a bot tracking 20 pairs, polling every 100ms generates thousands of HTTP requests per minute. WebSocket keeps the connection open and the server pushes data to you the instant it changes. On fast-moving assets like BTC or ETH, the latency difference matters: REST polling introduces 100–500ms delay even in ideal conditions, while a properly configured WebSocket delivers updates in under 10ms. Exchanges like Binance, Bybit, and OKX all offer WebSocket feeds for this reason — and KuCoin's implementation is one of the more feature-rich among them, supporting up to 100 topic subscriptions per connection.

Connecting to the KuCoin WebSocket API

Unlike Binance where you can connect directly, KuCoin requires a two-step setup. First, you call a REST endpoint to get a temporary connection token — this token contains the WebSocket server URL and keepalive parameters. Second, you append that token to the WebSocket URL and connect. For public data like prices and order books, no API key is needed at this stage. The kucoin websocket api documentation describes this token-based approach as a way to load-balance connections across their server fleet.

import asyncio
import websockets
import json
import requests
import uuid

def get_ws_token():
    url = 'https://api.kucoin.com/api/v1/bullet-public'
    response = requests.post(url)
    data = response.json()
    token = data['data']['token']
    endpoint = data['data']['instanceServers'][0]['endpoint']
    ping_interval = data['data']['instanceServers'][0]['pingInterval']  # milliseconds
    return token, endpoint, ping_interval

async def ping_loop(ws, interval_sec):
    while True:
        await asyncio.sleep(interval_sec)
        await ws.send(json.dumps({'id': str(uuid.uuid4()), 'type': 'ping'}))

async def listen():
    token, endpoint, ping_ms = get_ws_token()
    connect_url = endpoint + '?token=' + token + '&connectId=' + str(uuid.uuid4())

    async with websockets.connect(connect_url) as ws:
        welcome = json.loads(await ws.recv())
        print('Connected:', welcome['type'])  # prints: welcome

        asyncio.create_task(ping_loop(ws, ping_ms // 1000))

        sub = {
            'id': str(uuid.uuid4()),
            'type': 'subscribe',
            'topic': '/market/ticker:BTC-USDT',
            'privateChannel': False,
            'response': True
        }
        await ws.send(json.dumps(sub))

        async for msg in ws:
            data = json.loads(msg)
            if data.get('type') == 'message':
                print(data['data'])

asyncio.run(listen())
KuCoin's server includes a pingInterval value in the connection response (typically 18000ms). You must send a ping message within that window or the server silently closes the connection. This is the single most common reason WebSocket connections drop unexpectedly — don't skip the ping loop.

Subscribing to Market Data Channels

Every subscription uses a topic string in the format /channel/type:SYMBOL. You can subscribe to multiple symbols in a single message by comma-separating them in the topic field — far more efficient than separate subscriptions. KuCoin offers channels for everything from individual tickers to full order book depth. Platforms like Bybit and OKX follow a similar multiplexing pattern, so if you've worked with their WebSocket APIs the structure will feel familiar.

KuCoin WebSocket Channels Overview
TopicData DeliveredCommon Use Case
/market/ticker:BTC-USDTPrice, best bid/ask, 24h volumePrice alerts, dashboards
/market/level2:BTC-USDTIncremental order book changesMarket making, spread analysis
/market/match:BTC-USDTIndividual trade executionsVolume analysis, footprint charts
/market/candles:BTC-USDT_1minReal-time OHLCV candlesChart data, pattern detection
/account/balanceAccount balance changes (private)Portfolio tracking
/spotMarket/tradeOrdersOrder status updates (private)Order lifecycle management
async def subscribe_channels(ws, symbols):
    # Subscribe to tickers for multiple symbols in one message
    sub_msg = {
        'id': str(uuid.uuid4()),
        'type': 'subscribe',
        'topic': '/market/ticker:' + ','.join(symbols),
        'privateChannel': False,
        'response': True
    }
    await ws.send(json.dumps(sub_msg))

    # Also subscribe to Level 2 order book for the first symbol
    book_sub = {
        'id': str(uuid.uuid4()),
        'type': 'subscribe',
        'topic': '/market/level2:' + symbols[0],
        'privateChannel': False,
        'response': True
    }
    await ws.send(json.dumps(book_sub))

def handle_message(data):
    topic = data.get('topic', '')
    payload = data.get('data', {})

    if 'ticker' in topic:
        symbol = topic.split(':')[1]
        price = payload.get('price', 'N/A')
        best_ask = payload.get('bestAsk', 'N/A')
        best_bid = payload.get('bestBid', 'N/A')
        print(symbol + ': price=' + price + ' ask=' + best_ask + ' bid=' + best_bid)

    elif 'level2' in topic:
        changes = payload.get('changes', {})
        asks = changes.get('asks', [])  # format: [price, size, sequence]
        bids = changes.get('bids', [])
        print('Order book delta: ' + str(len(asks)) + ' ask levels, ' + str(len(bids)) + ' bid levels')

Authenticating Private WebSocket Channels

Account-level data — balance changes, order fills, position updates — requires a private WebSocket channel. This means authenticating the token request with your KuCoin API key, secret, and passphrase. The signing process uses HMAC-SHA256: concatenate the timestamp, HTTP method, and endpoint path, sign with your secret key, and base64-encode the result. KuCoin also requires the API passphrase to be signed separately starting from API key version 2. If you've set up authenticated REST calls on KuCoin before, the same credentials work here.

import hmac
import hashlib
import base64
import time
import requests

def generate_signature(api_secret, timestamp, method, endpoint, body=''):
    str_to_sign = timestamp + method + endpoint + body
    signature = base64.b64encode(
        hmac.new(
            api_secret.encode('utf-8'),
            str_to_sign.encode('utf-8'),
            hashlib.sha256
        ).digest()
    ).decode()
    return signature

def get_private_ws_token(api_key, api_secret, api_passphrase):
    timestamp = str(int(time.time() * 1000))
    endpoint = '/api/v1/bullet-private'
    sig = generate_signature(api_secret, timestamp, 'POST', endpoint)

    passphrase_encrypted = base64.b64encode(
        hmac.new(
            api_secret.encode('utf-8'),
            api_passphrase.encode('utf-8'),
            hashlib.sha256
        ).digest()
    ).decode()

    headers = {
        'KC-API-KEY': api_key,
        'KC-API-SIGN': sig,
        'KC-API-TIMESTAMP': timestamp,
        'KC-API-PASSPHRASE': passphrase_encrypted,
        'KC-API-KEY-VERSION': '2'
    }

    res = requests.post(
        'https://api.kucoin.com/api/v1/bullet-private',
        headers=headers
    )
    data = res.json()['data']
    return data['token'], data['instanceServers'][0]['endpoint']

# Usage
API_KEY = 'your-api-key'
API_SECRET = 'your-api-secret'
API_PASSPHRASE = 'your-passphrase'

token, endpoint = get_private_ws_token(API_KEY, API_SECRET, API_PASSPHRASE)
print('Private token obtained:', token[:20] + '...')
Generate API keys in KuCoin's API Management console. Enable only the permissions you actually need — 'General' for read-only data, 'Trade' for order management. Always restrict the key to your server's IP address. If your key leaks on KuCoin or any exchange including Binance or Gate.io, IP restriction is often the difference between a scary alert and a drained account.

Building Reconnection-Resilient WebSocket Connections

Production trading systems must survive network hiccups, server restarts, and token expiry. A WebSocket that drops and doesn't recover could mean missed signals or worse — an open position that never gets managed. The pattern below combines exponential backoff with automatic token refresh on reconnect. It also cleanly cancels the ping task before each reconnection attempt, avoiding orphaned coroutines that can cause subtle memory leaks in long-running bots. VoiceOfChain uses a similar resilient streaming pattern to maintain continuous data feeds from multiple exchanges simultaneously.

import asyncio
import websockets
import json
import uuid

async def keep_alive(ws, interval):
    while True:
        await asyncio.sleep(interval)
        await ws.send(json.dumps({'id': str(uuid.uuid4()), 'type': 'ping'}))

async def resilient_stream(symbols, on_tick, max_retries=10):
    retries = 0
    while retries < max_retries:
        try:
            token, endpoint, ping_ms = get_ws_token()  # fresh token each attempt
            url = endpoint + '?token=' + token + '&connectId=' + str(uuid.uuid4())

            async with websockets.connect(url) as ws:
                await ws.recv()  # consume welcome message
                retries = 0    # reset counter on successful connect

                await ws.send(json.dumps({
                    'id': '1',
                    'type': 'subscribe',
                    'topic': '/market/ticker:' + ','.join(symbols),
                    'privateChannel': False,
                    'response': True
                }))

                ping_task = asyncio.create_task(keep_alive(ws, ping_ms // 1000))

                try:
                    async for raw in ws:
                        msg = json.loads(raw)
                        if msg.get('type') == 'message':
                            symbol = msg['topic'].split(':')[1]
                            on_tick(symbol, msg['data'])
                finally:
                    ping_task.cancel()

        except Exception as e:
            retries += 1
            backoff = min(2 ** retries, 60)  # exponential, capped at 60s
            print('Connection lost. Retrying in', backoff, 'seconds. Attempt', retries)
            await asyncio.sleep(backoff)

    raise RuntimeError('Max reconnection attempts exceeded')

def handle_tick(symbol, data):
    print(symbol + ': price=' + data.get('price', 'N/A'))

asyncio.run(resilient_stream(['BTC-USDT', 'ETH-USDT', 'SOL-USDT'], handle_tick))

Frequently Asked Questions

How many subscriptions can I have per KuCoin WebSocket connection?
KuCoin allows up to 100 topic subscriptions per connection. For setups monitoring hundreds of pairs, open multiple connections — there is no hard cap on simultaneous WebSocket connections, though KuCoin may throttle abusive patterns. Batch symbols into a single topic string where possible to stay under the limit.
Do I need an API key to access KuCoin WebSocket market data?
No. For public channels — ticker prices, order books, trade history — you call /api/v1/bullet-public with no authentication to get a connection token. API keys are only required for private channels like account balances, order status updates, and position changes.
Why does my KuCoin WebSocket connection keep dropping silently?
The most common cause is a missing ping loop. KuCoin requires you to send a ping within the pingInterval window (typically 18 seconds). If you miss it, the server closes the connection without an error message. Make sure your ping coroutine is running concurrently and check that your network is not blocking WebSocket traffic on port 443.
What is the difference between Level 2 and Level 3 order book data on KuCoin?
Level 2 gives you aggregated order book data — total quantity at each price level, sent as incremental deltas. Level 3 delivers individual order events including every single order placed, modified, or cancelled. Level 3 is much higher volume but is necessary for detailed microstructure analysis or high-frequency market-making strategies.
How does KuCoin WebSocket API compare to Binance or Bybit WebSocket?
All three follow similar patterns: topic subscriptions, ping/pong keepalive, and message-type routing. The main difference is that KuCoin requires a preliminary REST call to fetch a connection token, while Binance lets you connect to stream URLs directly. Bybit uses a different auth timing for private channels. Performance and data quality are comparable across all three for retail algorithmic use.
Can I use the KuCoin WebSocket API in JavaScript or Node.js?
Yes. Use the 'ws' npm package or the browser's native WebSocket API. The flow is identical: fetch a token via REST, then connect with that token appended to the URL. The subscription topic format, message structure, and ping/pong requirements are exactly the same regardless of language.

The KuCoin WebSocket API gives you everything needed to build professional-grade market data infrastructure. Start with the public ticker stream to get your connection logic right, then layer in order book subscriptions and private account channels as your system matures. The patterns covered here — token-based connection, ping keepalive, multi-symbol batching, and exponential backoff reconnection — handle the vast majority of production edge cases. For traders who want real-time signals without building infrastructure themselves, platforms like VoiceOfChain aggregate live streaming data from KuCoin, Binance, Bybit, OKX, and other exchanges into actionable trade signals. Whether you build your own pipeline or consume aggregated feeds, the key is having data that is genuinely real-time — not REST-polled approximations with half-second lag.

◈   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