Persist and refresh the access token daily
Kite access tokens expire daily (~around market open, roughly 6 AM IST invalidation of the previous day's token). A long-running bot needs to detect expiry and re-auth without crashing silently.
Persist the token
# auth/token_store.py
import json, pathlib
TOKEN_FILE = pathlib.Path("tokens/session.json")
def save_token(access_token: str):
TOKEN_FILE.parent.mkdir(exist_ok=True)
TOKEN_FILE.write_text(json.dumps({"access_token": access_token}))
def load_token() -> str | None:
if not TOKEN_FILE.exists():
return None
return json.loads(TOKEN_FILE.read_text()).get("access_token")
Validate on startup instead of blindly trusting a saved token
# auth/session.py (continued)
from kiteconnect import KiteConnect
from kiteconnect.exceptions import TokenException
from config import API_KEY
from auth.token_store import load_token, save_token
def get_authenticated_kite() -> KiteConnect:
kite = KiteConnect(api_key=API_KEY)
token = load_token()
if token:
kite.set_access_token(token)
try:
kite.profile() # cheap call to verify token is live
return kite
except TokenException:
pass # fall through to fresh login
raise RuntimeError(
"No valid session — run auth/login.py to log in manually."
)
Automating the daily human step
Zerodha's 2FA requirement means full headless login needs a TOTP seed (saved when you enabled 2FA) fed programmatically:
import pyotp
totp = pyotp.TOTP(YOUR_TOTP_SECRET)
current_code = totp.now() # use this in place of manually reading your app
Combine this with a browser-automation login (Selenium/Playwright hitting Zerodha's login form) to fully script the chapter 7-9 flow. This is common in production bots but treat the TOTP seed with the same care as api_secret — it's effectively a master key to your 2FA.
For learning, a once-daily manual login (30 seconds) is the simpler and safer choice. Automate it once you're confident in everything downstream.
Checkpoint
You now have durable, verifiable auth. Every later chapter assumes a working kite = get_authenticated_kite().
Next: 011 — Get user profile