How to stream live ticks via Kite WebSocket

From WebNotes, a public knowledge base. Last updated . Reading time ~10 min. Level: Intermediate.

The Kite Connect API provides a binary WebSocket feed for real-time market data through a class called KiteTicker in the kiteconnect Python SDK. Unlike the REST endpoints, which are request-response, KiteTicker maintains a persistent connection and pushes price updates as they occur. This guide explains how to establish that connection, subscribe to instruments, decode ticks, and handle network interruptions reliably.

How the KiteTicker WebSocket works

Kite Connect’s WebSocket endpoint at wss://ws.kite.trade streams binary-encoded tick packets. Each packet contains price, volume, and depth data for one or more instruments. The KiteTicker class handles the binary decoding, connection lifecycle, and reconnection internally, so your application receives clean Python dictionaries.

The connection is authenticated via the access_token and api_key passed as query parameters in the initial HTTP upgrade request. There is no separate authentication handshake after the WebSocket connection is established.

A single connection can subscribe to up to 3,000 instrument tokens simultaneously. Tokens are numeric identifiers, distinct from the human-readable tradingsymbol. You must look up tokens from the instruments master file.

Tick modes

ModeConstantFields included
LTPMODE_LTPLast traded price only
QuoteMODE_QUOTELTP, OHLC, volume, buy/sell quantity
FullMODE_FULLQuote fields plus 5-level market depth (order book)

You can mix modes across subscriptions on the same connection. Instruments subscribed at MODE_FULL generate more data per tick and consume more bandwidth.

Step-by-step procedure

Find instrument tokens

Download the instruments master:

import os
import csv
import requests

resp = requests.get("https://api.kite.trade/instruments")
resp.raise_for_status()
lines = resp.text.splitlines()
reader = csv.DictReader(lines)

# Find token for RELIANCE on NSE
for row in reader:
    if row["tradingsymbol"] == "RELIANCE" and row["exchange"] == "NSE":
        print("instrument_token:", row["instrument_token"])
        break

Instrument tokens change only rarely (on corporate actions or expiry), but it is best practice to refresh the instruments file daily before the session starts.

Define callbacks

def on_ticks(ws, ticks):
    """Called on every incoming tick packet."""
    for tick in ticks:
        print(
            tick["instrument_token"],
            tick.get("last_price"),
            tick.get("volume"),
        )

def on_connect(ws, response):
    """Called once the WebSocket connection is established."""
    tokens = [738561, 341249]   # RELIANCE, INFY instrument tokens
    ws.subscribe(tokens)
    ws.set_mode(ws.MODE_FULL, tokens)
    print("Subscribed to", tokens)

def on_close(ws, code, reason):
    """Called when the connection closes."""
    print(f"Connection closed: {code} {reason}")

def on_error(ws, code, reason):
    """Called on WebSocket error."""
    print(f"Error: {code} {reason}")

def on_reconnect(ws, attempts_count):
    """Called before each reconnection attempt."""
    print(f"Reconnecting, attempt {attempts_count}")

def on_noreconnect(ws):
    """Called when KiteTicker gives up reconnecting."""
    print("Reconnection exhausted. Exiting.")

Instantiate and start KiteTicker

import os
from kiteconnect import KiteTicker

API_KEY = os.environ["KITE_API_KEY"]
ACCESS_TOKEN = os.environ["KITE_ACCESS_TOKEN"]

ticker = KiteTicker(API_KEY, ACCESS_TOKEN)

ticker.on_ticks = on_ticks
ticker.on_connect = on_connect
ticker.on_close = on_close
ticker.on_error = on_error
ticker.on_reconnect = on_reconnect
ticker.on_noreconnect = on_noreconnect

# Start in threaded mode so the main thread stays free
ticker.connect(threaded=True)

threaded=True runs the WebSocket event loop in a daemon thread. The main thread continues executing. If your main thread exits, the daemon thread also exits automatically.

Process ticks in the main thread

A common pattern is to push ticks into a queue.Queue from on_ticks and consume them in the main thread:

import queue
import threading

tick_queue = queue.Queue()

def on_ticks(ws, ticks):
    for tick in ticks:
        tick_queue.put(tick)

def process_ticks():
    while True:
        tick = tick_queue.get()
        # Your strategy logic here
        ltp = tick.get("last_price")
        token = tick["instrument_token"]
        print(f"Token {token}: LTP = {ltp}")

ticker.on_ticks = on_ticks
ticker.connect(threaded=True)

# Run processing on the main thread
process_ticks()

This pattern prevents slow strategy logic from blocking the WebSocket receive loop.

Full working example

import os
import queue
import time
from kiteconnect import KiteTicker

API_KEY = os.environ["KITE_API_KEY"]
ACCESS_TOKEN = os.environ["KITE_ACCESS_TOKEN"]

TOKENS = [738561, 341249]   # RELIANCE, INFY on NSE
tick_queue = queue.Queue()

def on_ticks(ws, ticks):
    for tick in ticks:
        tick_queue.put(tick)

def on_connect(ws, response):
    ws.subscribe(TOKENS)
    ws.set_mode(ws.MODE_FULL, TOKENS)

def on_close(ws, code, reason):
    print("Closed:", code, reason)

def on_error(ws, code, reason):
    print("Error:", code, reason)

ticker = KiteTicker(API_KEY, ACCESS_TOKEN)
ticker.on_ticks = on_ticks
ticker.on_connect = on_connect
ticker.on_close = on_close
ticker.on_error = on_error

ticker.connect(threaded=True)

print("Streaming. Press Ctrl-C to stop.")
try:
    while True:
        try:
            tick = tick_queue.get(timeout=1)
            print(tick["instrument_token"], tick.get("last_price"))
        except queue.Empty:
            pass
except KeyboardInterrupt:
    ticker.close()
    print("Stopped.")

Understanding the tick dictionary

A MODE_FULL tick dictionary contains:

KeyDescription
instrument_tokenNumeric token identifying the instrument
last_priceLast traded price
last_quantityQuantity in the last trade
average_priceVolume-weighted average price for the day
volumeTotal volume traded for the day
buy_quantityTotal pending buy quantity in the order book
sell_quantityTotal pending sell quantity in the order book
ohlcDict with open, high, low, close
depthDict with buy and sell lists (5 levels each); each level has price, quantity, orders
timestampExchange timestamp of the tick
tradableBoolean; False for indices (NIFTY 50, SENSEX)

Index ticks (for NIFTY, SENSEX, etc.) do not have depth or volume fields and always have tradable: False.

Reconnection configuration

KiteTicker handles reconnection automatically using exponential backoff. You can configure it:

ticker = KiteTicker(
    API_KEY,
    ACCESS_TOKEN,
    reconnect=True,
    reconnect_max_tries=50,    # default is 50
    reconnect_max_delay=60,    # seconds; default is 60
    connect_timeout=30,        # seconds for initial connect
)

Setting reconnect=False disables automatic reconnection entirely; useful when you manage reconnection in your own supervisor process.

What can go wrong

  • Empty ticks during pre-market and post-market hours. The exchange does not send ticks outside trading hours. on_ticks will not be called even though the WebSocket connection remains open. This is expected behaviour.
  • TokenException on connect. The access_token has expired or is invalid. Regenerate the token. The WebSocket authenticates at connection time; it does not refresh mid-session.
  • Ticks arrive but LTP is missing. Some fields are absent in MODE_LTP and MODE_QUOTE. Always use .get("field_name") rather than ["field_name"] to avoid KeyError.
  • Queue buildup under high load. If your strategy logic is slower than the tick rate during volatile sessions, tick_queue grows unboundedly. Use queue.Queue(maxsize=N) and handle queue.Full exceptions, or use a discard-oldest policy.
  • Connection drops at 6:00 AM IST. The WebSocket is forcibly closed when the access_token expires at 6:00 AM the next trading day. Plan for a daily restart.
  • Max 3,000 instruments. Subscribing more than 3,000 tokens on a single connection results in a server-side error. For larger watchlists, open multiple connections with separate access_token sessions.

References

  1. Zerodha, Kite Connect WebSocket streaming documentation, kite.trade/docs/connect/v3/websocket/, accessed 2024.
  2. kiteconnect Python SDK, KiteTicker class, github.com/zerodha/pykiteconnect, accessed 2024.
  3. Zerodha Support, Kite Connect market data and WebSocket, support.zerodha.com.
  4. SEBI, Guidelines on algorithmic trading, sebi.gov.in.
  5. Zerodha Z-Connect blog, Building with Kite Connect, zerodha.com/z-connect.

Reviewed and published by

The WebNotes Editorial Team covers Indian capital markets, payments infrastructure and retail investor procedures. Every article is fact-checked against primary sources, principally SEBI circulars and master directions, NPCI specifications and the official support documentation published by the intermediary in question. Drafts go through a second-pair-of-eyes review and a separate compliance read before publication, and revisions are tracked against the SEBI and NPCI rule changes referenced in the methodology section.

Last reviewed
Conflicts of interest
WebNotes is independent. No relationship with any broker, registrar or bank named in this article.