Download the instrument master
Every tradable instrument across every exchange Zerodha supports is listed in one big CSV/dataframe, refreshed daily. You need this to resolve human-readable symbols into the instrument_token the API actually trades on.
instruments = kite.instruments() # all exchanges, ~90k+ rows
nse_instruments = kite.instruments("NSE") # just NSE equities
nfo_instruments = kite.instruments("NFO") # just NSE F&O
Each row:
{
"instrument_token": 408065,
"exchange_token": 1594,
"tradingsymbol": "INFY",
"name": "INFOSYS LIMITED",
"last_price": 0.0,
"expiry": "",
"strike": 0.0,
"tick_size": 0.05,
"lot_size": 1,
"instrument_type": "EQ",
"segment": "NSE",
"exchange": "NSE",
}
Cache it — don't fetch on every startup
This call returns tens of thousands of rows and the data changes at most once daily (new listings, expiries rolling over). Fetch once per day and cache locally:
import pandas as pd
from pathlib import Path
from datetime import date
CACHE_PATH = Path("data/instruments_cache.parquet")
def get_instruments(kite, exchange: str | None = None) -> pd.DataFrame:
today = date.today().isoformat()
if CACHE_PATH.exists():
df = pd.read_parquet(CACHE_PATH)
if df.attrs.get("fetched_date") == today:
return df[df.exchange == exchange] if exchange else df
raw = kite.instruments()
df = pd.DataFrame(raw)
df.attrs["fetched_date"] = today
df.to_parquet(CACHE_PATH)
return df[df.exchange == exchange] if exchange else df
Note: df.attrs doesn't survive a parquet round-trip reliably across all pandas versions — for production, store the fetch date in a sidecar file or a separate metadata table instead of relying on attrs.