NineFifteenAM

Pillar guide · updated monthly

Streaming live prices with the Kite Connect websocket in Python

9 min readBy NineFifteenAM

A runnable Python script for Zerodha's Kite Connect websocket: subscribe, pick a mode, read a tick field by field, handle reconnects and shut down cleanly.

Short answer

Install the kiteconnect library, create a KiteTicker with your API key and today's access token, set on_connect and on_ticks callbacks, and call connect(). Inside on_connect, subscribe to instrument tokens and choose a mode: ltp, quote or full. One connection carries up to 3000 instruments, and one API key can hold 3 connections. Keep the callbacks fast, stop retries before you close, and never run the script on yesterday's token.

The Kite Connect websocket sends you live prices with one connection and three callbacks. By the end of this guide you'll have a short Python script that subscribes to two instruments, prints each tick, reconnects when the network drops and shuts down without leaving a connection hanging. You can subscribe to up to 3000 instruments per connection, and one API key can have 3 connections.

This guide only reads prices. It sends no orders. If you want that side, placing your first order is the companion piece.

Before you start

You need Why
The Connect plan Live prices over the websocket are part of the paid plan. What Kite Connect costs has the details.
Today's access token The websocket needs a valid one. Zerodha staff on the developer forum say a 403 means the api_key or access token is invalid. Refreshing the token covers the daily login.
Instrument tokens The websocket subscribes by numeric instrument token, not by symbol.
pip install kiteconnect

The websocket client in that library is called KiteTicker. The methods and defaults below come from the pykiteconnect documentation and the websocket documentation.

The three modes

Every subscribed instrument streams in one of three modes. Each mode sends a packet of a different size:

Mode Constant in the library What the docs say it carries Packet size
ltp MODE_LTP Last traded price only 8 bytes
quote MODE_QUOTE Several fields, excluding market depth 44 bytes
full MODE_FULL Several fields, including market depth 184 bytes

I use the smallest mode that has the fields I need. If all you want is a price to draw on a screen, ltp is enough. If you want volume, open, high, low and close, that's quote. Depth needs full.

In pykiteconnect 4.2.0, subscribe() records each token in MODE_QUOTE unless you call set_mode afterwards. The script below still sets a mode explicitly so the choice is visible in the code.

The script

The script reads your key and token from the KITE_API_KEY and KITE_ACCESS_TOKEN environment variables, so they never sit in the file. The instrument tokens below (738561 and 5633) are the example tokens from the pykiteconnect documentation. Swap in your own. Look tokens up from the instruments list rather than typing them from memory, because a wrong token just streams the wrong instrument.

import logging
import os
import time

from kiteconnect import KiteTicker

logging.basicConfig(level=logging.INFO)

API_KEY = os.environ["KITE_API_KEY"]
ACCESS_TOKEN = os.environ["KITE_ACCESS_TOKEN"]  # today's token
TOKENS = [738561, 5633]  # example tokens from the docs

kws = KiteTicker(API_KEY, ACCESS_TOKEN)  # reconnect=True by default


def on_connect(ws, response):
    # Runs on the first connect and again after every reconnect.
    logging.info("connected: %s", response)
    ws.subscribe(TOKENS)
    ws.set_mode(ws.MODE_QUOTE, TOKENS)


def on_ticks(ws, ticks):
    # Keep this fast. Hand the data to a queue; don't calculate here.
    for t in ticks:
        print(t["instrument_token"], t["last_price"], t.get("exchange_timestamp"))


def on_close(ws, code, reason):
    logging.info("closed: %s %s", code, reason)


def on_error(ws, code, reason):
    logging.error("error: %s %s", code, reason)


def on_reconnect(ws, attempts_count):
    logging.warning("reconnecting, attempt %s", attempts_count)


def on_noreconnect(ws):
    logging.error("gave up reconnecting")


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

kws.connect(threaded=True)  # event loop runs in a background thread

try:
    while True:
        time.sleep(1)  # main thread stays free; put your own logic here
except KeyboardInterrupt:
    pass
finally:
    kws.stop_retry()  # stop any reconnect attempts first
    kws.close()
    logging.info("shut down")

I have not run this against a live connection while writing the guide; it follows the documented method names and signatures, so test it in market hours on a small subscription first.

A few choices worth explaining:

Reading a tick

Each call to on_ticks gets a list of tick dictionaries, one per instrument that updated. Here is an illustrative, made-up tick in full mode. The numbers are invented and don't describe any real market.

{
    "tradable": True,
    "mode": "full",
    "instrument_token": 738561,
    "last_price": 100.0,
    "last_traded_quantity": 10,
    "average_traded_price": 99.5,
    "volume_traded": 123456,
    "total_buy_quantity": 5000,
    "total_sell_quantity": 4000,
    "ohlc": {"open": 99.0, "high": 101.0, "low": 98.5, "close": 99.8},
    "change": 0.2,
    "last_trade_time": datetime.datetime(2026, 9, 30, 11, 15, 2),
    "exchange_timestamp": datetime.datetime(2026, 9, 30, 11, 15, 3),
    "oi": 0,
    "oi_day_high": 0,
    "oi_day_low": 0,
    "depth": {"buy": [...5 entries...], "sell": [...5 entries...]},
}

The key names in the pykiteconnect documentation, and what they mean:

Key Meaning
instrument_token Which instrument this tick is for
tradable Whether the instrument is tradable
mode The mode this tick arrived in
last_price Last traded price
last_traded_quantity Quantity in the last trade
average_traded_price Average traded price for the day
volume_traded Day volume
total_buy_quantity, total_sell_quantity Aggregate quantity waiting on each side
ohlc Open, high, low and close as a nested dictionary
change Price change, as a percentage
last_trade_time, exchange_timestamp Timestamps, as datetime objects
oi, oi_day_high, oi_day_low Open interest and its day high and low, for derivatives
depth Five best bids and five best offers, in full mode

Two practical notes. First, not every key appears in every mode: an ltp tick is much smaller than a full one, so read optional fields with .get() as the script does. Second, the docs say index packets (NIFTY 50, SENSEX) have a different structure from tradable instruments, with fewer fields. If you subscribe to an index and a stock together, don't assume both ticks carry the same keys.

What the wire looks like

You don't need this to use the library, but it explains the sizes above. The websocket docs describe each binary message as one or more quote packets, and the first two bytes are the number of packets in the message. Prices arrive as integers: the docs say to divide by 100 for everything except currencies, which are divided by 10000000. The library does the parsing for you. Zerodha also sends a 1-byte heartbeat every couple of seconds when there is no data to stream, so a quiet instrument is not a dead connection.

Reconnects: what happens when the network drops

The constructor's defaults, from the pykiteconnect docs:

Setting Default Meaning
reconnect True Auto-reconnect on
reconnect_max_tries 50 Maximum reconnect attempts (capped at 300)
reconnect_max_delay 60 Maximum delay between attempts, in seconds (minimum accepted value is 5)
connect_timeout 30 Connection timeout, in seconds

The docs say reconnection uses exponential backoff, starting at 2 seconds and rising to reconnect_max_delay. The docs list the callbacks involved: on_close when the connection closes, on_reconnect(ws, attempts_count) during reconnection attempts, and on_noreconnect when attempts exceed the maximum. The library resubscribes for you. In the pykiteconnect 4.2.0 source, subscribe() and set_mode() store every token and its mode in subscribed_tokens, and on every reconnect (not the first connect) the library's _on_open handler calls resubscribe(), which re-sends all stored tokens grouped by mode. You don't need to write your own resubscribe logic.

Subscribing inside on_connect, as the script does, works: it runs on the first connect and the library's resubscribe covers later reconnects. Because on_connect can also fire again after a reconnect, the script's subscribe call may be sent a second time. Tokens are stored in a dictionary keyed by token, so the library's own record has no duplicates. I have not tested how the server treats a repeated subscribe message. If ticks go quiet after a reconnect, a forum thread on that problem has Zerodha staff asking to see the user's websocket code, so log what your callbacks receive.

Shutting down cleanly

stop() stops the event loop and prevents reconnection. stop_retry() stops ongoing reconnect attempts. close() closes the connection. On the developer forum, a Zerodha reply about closing the ticker suggested calling stop_retry() before close(), and the same thread reports that calling close() alone caused error 1006 for some users. The script does it in that order.

As for market hours, I found nothing in the docs about the connection closing itself at the end of the session. A staff reply on the forum recommended keeping your own map of exchange timings and acting on that. So decide in your own code when to stop: after the session you trade ends, and on days with no session (check the exchange's holiday list). Outside those hours the connection can be open with nothing to send. What arrives before 9:15 is covered in the pre-open session guide.

Common mistakes

Each of these is backed by the docs or a Zerodha staff reply on the forum.

  1. An expired access token. Error 1006 with a 403 means the api_key or access token is invalid. Log in again each morning.
  2. Slow work inside on_ticks. Staff said the streaming connection will be closed if you block the on_ticks method with calculations. Push ticks onto a queue and process them elsewhere.
  3. ws.stop() in error handlers. It ends reconnection for good.
  4. Writing your own resubscribe logic. The library already re-sends stored subscriptions on each reconnect. Subscribe once in on_connect and let it do the rest.
  5. Going past the limits. 3000 instruments per connection and 3 connections per API key. Split large lists across connections rather than adding more to one.
  6. Trusting the price as a fill price. A last traded price is not a price you can necessarily trade at. That gap is the subject of paper trading vs live trading.

What I'd log while you test

Print a count of ticks per minute per instrument and the gap between exchange_timestamp and your own clock. If either looks wrong, you'll see it long before a bad number reaches anything that matters. I haven't published measurements of my own here, because they'd depend on your network and your subscription.

Sources

This guide explains how a broker API works, for information only. It is not investment advice or a recommendation to buy or sell any security. I am not registered with SEBI as an investment adviser or research analyst.

Questions people ask me

How many instruments can I stream over the Kite Connect websocket?

Up to 3000 instruments on a single websocket connection, and a single API key can have up to 3 websocket connections. Both numbers come from Zerodha's websocket documentation.

What is the difference between ltp, quote and full mode?

ltp sends only the last traded price (8 bytes per packet). quote sends several fields but no market depth (44 bytes). full sends several fields including market depth (184 bytes). Pick the smallest mode that has what you need.

Does KiteTicker reconnect by itself?

Yes. Auto-reconnection is on by default, with up to 50 tries (reconnect_max_tries) and a delay that starts at 2 seconds and grows up to reconnect_max_delay, which defaults to 60 seconds. If it gives up, on_noreconnect is called.

Why does my websocket connect and then close with error 1006 and a 403?

Zerodha staff say a 403 means the api_key or the access token is invalid, and that a valid access token is needed to connect. Tokens expire every morning, so generate a fresh one first.

Does the websocket close by itself when the market closes?

Not as far as I could verify. A Zerodha staff reply on the developer forum suggested keeping your own map of exchange timings and acting on it, and I found no documented market-close event. Your script should decide when to stop.

zerodhakite connectwebsocketkitetickerpythonlive pricesmarket data
N

NineFifteenAM

One trader building an options bot for Indian index markets since early 2026. I write down how it is built, what broke, and what it cost — no tips, no calls, no returns.

Related

29 Sept 2026
Zerodha Kite Connect: what it costs and whether it's worth itWhat Kite Connect costs in 2026, what the free and ₹500 plans include, the rate and data limits you'll hit, and who the paid plan is worth it for.
Guides
29 Sept 2026
How to place your first order with the Kite Connect API in PythonPlace, check and cancel your first order with Zerodha's Kite Connect API in Python, after rehearsing it in a paper mode that sends nothing to the exchange.
Guides
27 Sept 2026
Zerodha Kite Connect: get your API key and refresh the token dailyA short, practical guide to getting a Kite Connect API key and handling the access token that expires every morning.
Guides