instrument_token vs tradingsymbol

Two different identifiers, used in different API calls — mixing them up is a very common source of confusing bugs.

IdentifierTypeUsed forStable?
instrument_tokenintegerWebSocket subscription, historical data APIStable for the life of the instrument; F&O contracts get a *new* token each expiry cycle
tradingsymbol (+ exchange)stringOrder placement, quote lookups by symbolHuman-readable, but options/futures symbols change every expiry
# Order placement uses tradingsymbol + exchange
kite.place_order(
    exchange="NSE",
    tradingsymbol="INFY",
    ...
)

# Historical data and WebSocket use instrument_token
kite.historical_data(instrument_token=408065, ...)
kws.subscribe([408065])

Why two systems exist

tradingsymbol is what a human (and the exchange's own order-matching system) recognizes; instrument_token is Zerodha's internal, numeric, collision-free identifier optimized for high-frequency lookups (ticks arrive tagged by token, not by string, because parsing/comparing integers is cheaper at WebSocket tick volumes).

The trap: F&O tokens change every expiry

NIFTY24SEPFUT's instrument_token this month is not the same token as NIFTY24OCTFUT next month, even though both represent "NIFTY near-month future" conceptually. Any code that hardcodes a token for a derivative contract will silently start tracking the wrong (or a dead) instrument after rollover.

def resolve_current_month_future(df, underlying: str) -> dict:
    futures = df[
        (df.name == underlying) &
        (df.instrument_type == "FUT")
    ].sort_values("expiry")
    return futures.iloc[0].to_dict()   # nearest expiry = current month

Always resolve derivative tokens dynamically from a freshly-cached instrument master (chapter 21), never hardcode them.

Next: 024 — Get LTP for symbols