How to authenticate Kite Connect with TOTP automation
Algo trading scripts need a fresh access_token every morning before market open. The standard Kite Connect authentication flow requires opening a browser, completing the Zerodha login, and pasting the request_token. For unattended or server-based strategies, that manual step is a bottleneck. This guide shows how to automate the entire login using pyotp to generate the TOTP code, eliminating the need for any browser interaction.
How TOTP automation works
The standard Kite Connect login involves three steps at Zerodha’s end: submit client ID and password, then submit a TOTP OTP, after which Zerodha redirects to your registered URL with a request_token. A browser performs these steps interactively. Automation replaces the browser with a requests.Session that sends the same HTTP POST calls, and replaces the TOTP app with pyotp.TOTP(secret).now() to generate the current OTP.
Zerodha uses an internal REST API at kite.zerodha.com/api/login and kite.zerodha.com/api/twofa for the login steps. These endpoints are not part of the documented Kite Connect API, but they have been stable for several years and are widely used in the community. The flow is:
POST /api/loginwithuser_idandpassword→ receive arequest_id.POST /api/twofawithuser_id,request_id,type=totp, and the current TOTP OTP → Zerodha sets a session cookie and returns the authorisation redirect URL.- Follow the redirect (or extract the
request_tokenfrom the Location header) → obtain therequest_token. - Call
kite.generate_session(request_token, api_secret)→ obtain theaccess_token.
Step-by-step procedure
Retrieve your TOTP secret key
The TOTP secret is displayed only once: when you enable TOTP on your Zerodha account at kite.zerodha.com/user/profile. It appears as a plain-text string alongside the QR code. If you did not save it when enabling TOTP, you must disable and re-enable TOTP to get a new secret.
Store the secret in your .env file:
KITE_API_KEY=your_api_key
KITE_API_SECRET=your_api_secret
KITE_USER_ID=AB1234
KITE_PASSWORD=your_zerodha_password
KITE_TOTP_SECRET=JBSWY3DPEHPK3PXP # example only; your real secret is longer
Treat this file with the same care as your bank password. Restrict file permissions: chmod 600 .env.
Install dependencies
pip install kiteconnect pyotp requests python-dotenv
Write the headless login module
Create totp_auth.py:
"""totp_auth.py, Headless Kite Connect login using TOTP automation."""
import os
import json
import time
from pathlib import Path
import pyotp
import requests
from dotenv import load_dotenv
from kiteconnect import KiteConnect
from kiteconnect.exceptions import TokenException
load_dotenv()
API_KEY = os.environ["KITE_API_KEY"]
API_SECRET = os.environ["KITE_API_SECRET"]
USER_ID = os.environ["KITE_USER_ID"]
PASSWORD = os.environ["KITE_PASSWORD"]
TOTP_SECRET = os.environ["KITE_TOTP_SECRET"]
TOKEN_FILE = Path("kite_token.json")
LOGIN_URL = "https://kite.zerodha.com/api/login"
TWOFA_URL = "https://kite.zerodha.com/api/twofa"
KITE_CONNECT_LOGIN = f"https://kite.trade/connect/login?api_key={API_KEY}&v=3"
def _is_token_valid(kite: KiteConnect) -> bool:
"""Return True if the stored access_token is still valid."""
try:
kite.profile()
return True
except TokenException:
return False
except Exception:
return False
def _login_and_get_request_token() -> str:
"""Perform headless Zerodha login and return the request_token."""
session = requests.Session()
# Step 1: Password login
resp = session.post(
LOGIN_URL,
data={"user_id": USER_ID, "password": PASSWORD},
timeout=15,
)
resp.raise_for_status()
body = resp.json()
if body.get("status") != "success":
raise RuntimeError(f"Login step 1 failed: {body.get('message')}")
request_id = body["data"]["request_id"]
# Step 2: TOTP two-factor authentication
totp = pyotp.TOTP(TOTP_SECRET)
otp = totp.now()
resp2 = session.post(
TWOFA_URL,
data={
"user_id": USER_ID,
"request_id": request_id,
"twofa_value": otp,
"twofa_type": "totp",
},
timeout=15,
)
resp2.raise_for_status()
body2 = resp2.json()
if body2.get("status") != "success":
raise RuntimeError(f"TOTP step failed: {body2.get('message')}")
# Step 3: Follow Kite Connect OAuth login URL to capture request_token
resp3 = session.get(KITE_CONNECT_LOGIN, allow_redirects=False, timeout=15)
# Zerodha issues a redirect to the registered redirect URL with request_token
location = resp3.headers.get("Location", "")
if not location:
# Some configurations require following one more redirect
resp3 = session.get(resp3.url, timeout=15, allow_redirects=False)
location = resp3.headers.get("Location", "")
if "request_token=" not in location:
raise RuntimeError(
f"request_token not found in redirect. Location: {location}"
)
from urllib.parse import urlparse, parse_qs
parsed = urlparse(location)
request_token = parse_qs(parsed.query)["request_token"][0]
return request_token
def get_authenticated_kite() -> KiteConnect:
"""Return an authenticated KiteConnect instance, refreshing token if needed."""
kite = KiteConnect(api_key=API_KEY)
# Try loading a cached token first
if TOKEN_FILE.exists():
data = json.loads(TOKEN_FILE.read_text())
if data.get("access_token"):
kite.set_access_token(data["access_token"])
if _is_token_valid(kite):
return kite
# Token absent or stale; perform fresh TOTP login
print("Performing TOTP-automated login...")
request_token = _login_and_get_request_token()
session_data = kite.generate_session(request_token, api_secret=API_SECRET)
access_token = session_data["access_token"]
kite.set_access_token(access_token)
TOKEN_FILE.write_text(json.dumps({"access_token": access_token}))
print("Login successful. Token saved.")
return kite
if __name__ == "__main__":
kite = get_authenticated_kite()
profile = kite.profile()
print(f"Authenticated as: {profile['user_name']} ({profile['user_id']})")
Schedule the daily login
On Linux or macOS, use cron to run the script at 8:45 AM IST (UTC+5:30 = 3:15 AM UTC) on weekdays:
15 3 * * 1-5 /path/to/kite-project/.venv/bin/python /path/to/kite-project/totp_auth.py >> /var/log/kite_auth.log 2>&1
On Windows, use Task Scheduler with the trigger set to 8:45 AM on weekdays.
If your strategy script starts later in the morning, it can simply call get_authenticated_kite() directly; the function checks the cache first and only performs a fresh login when the token is absent or stale.
Integrate with your strategy script
from totp_auth import get_authenticated_kite
kite = get_authenticated_kite()
# kite is now fully authenticated; proceed with orders, data queries, etc.
orders = kite.orders()
positions = kite.positions()
Handling TOTP timing issues
TOTP codes are valid for a 30-second window. pyotp.TOTP.now() generates the code for the current 30-second slot. If your system clock is more than a few seconds off from the NTP-synchronised time, the code may be rejected by Zerodha.
Synchronise your system clock:
# Linux/macOS
sudo ntpdate pool.ntp.org
# Or ensure chrony/timesyncd is running:
timedatectl status
If the TOTP step occasionally fails near the 30-second boundary, add a retry loop:
import time
for attempt in range(3):
try:
resp2 = session.post(TWOFA_URL, data={..., "twofa_value": pyotp.TOTP(TOTP_SECRET).now()}, timeout=15)
body2 = resp2.json()
if body2.get("status") == "success":
break
except Exception:
pass
time.sleep(2)
else:
raise RuntimeError("TOTP authentication failed after 3 attempts")
Security considerations
Credential exposure. This automation requires storing your Zerodha account password in plaintext (in .env or an equivalent secrets store). Anyone with access to that file can trade on your account. Use OS-level file permissions (chmod 600), full-disk encryption, and a dedicated Zerodha sub-account if available.
TOTP secret exposure. The TOTP secret is effectively a permanent second factor. If it leaks, an attacker can generate valid OTPs indefinitely. Rotate it immediately by disabling and re-enabling TOTP on your Zerodha profile if you suspect exposure.
Regulatory position. SEBI’s algo trading guidelines require that automated order-placement systems for retail investors operate through a registered API. Kite Connect satisfies this requirement. Automated authentication for your own account is not separately restricted, but deploying this automation for third-party accounts without proper authorisation raises legal concerns.
Do not share the .env file. If you post code publicly (GitHub, forums), ensure the .env file is excluded and that no credentials appear in the code history.
What can go wrong
RuntimeError: Login step 1 failed: Invalid credentials. Your Zerodha user ID or password is wrong. Verify by logging in manually at kite.zerodha.com.RuntimeError: TOTP step failed. Either the TOTP secret is wrong, your system clock is out of sync, or the OTP expired between generation and submission. Check NTP sync and retry.RuntimeError: request_token not found in redirect. The Kite Connect app’s redirect URL may not be configured or may not match. Verify the redirect URL in the developer console matches the domain inKITE_CONNECT_LOGIN.requests.exceptions.ConnectionError. Network issue reaching kite.zerodha.com. Check internet connectivity.- Zerodha changes the internal login API. The endpoints used here are internal and undocumented. If Zerodha updates them, the requests will fail. Monitor the community forums at zerodha.com/z-connect for API change announcements.
- Script runs but token is stale by market open. If your script runs well before 9:15 AM, the token should be valid. If for any reason the token is regenerated after 6:00 AM the same day, re-authentication works normally.
Related guides
- How to generate a Kite Connect API key
- How to generate the request_token and access_token
- How to write a basic Python script using kiteconnect
- How to place an order via the Kite Connect REST API
- Kite Connect API overview
- Kite Connect ecosystem
- Zerodha overview
- SEBI
References
- Zerodha, Kite Connect authentication documentation, kite.trade/docs/connect/v3/user/, accessed 2024.
- pyotp library, Python TOTP/HOTP, github.com/pyauth/pyotp, accessed 2024.
- kiteconnect Python SDK, github.com/zerodha/pykiteconnect, accessed 2024.
- SEBI, Circular on algorithmic trading by retail investors, SEBI/HO/MRD/2021 series, sebi.gov.in.
- Zerodha Support, Setting up TOTP, support.zerodha.com.
- RFC 6238, TOTP: Time-Based One-Time Password Algorithm, tools.ietf.org/html/rfc6238.