NineFifteenAM

Pillar guide · updated monthly

Zerodha Kite Connect: get your API key and refresh the token daily

9 min readUpdated 18 October 2026By NineFifteenAM

A practical guide to getting a Kite Connect API key and handling the access token that expires at 6 AM each day, with scripted login and scheduling.

Short answer

Sign up at developers.kite.trade, create an app to get your API key and secret, and pay ₹500/month if you need market data. The access token expires at 6 AM every day, so you need to log in again each trading morning, either by hand or with a scheduled script.

Getting a Kite Connect API key takes ten minutes. The part that catches everyone is that the access token expires every day at 6 AM. Here's how to handle both.

Step 1: Get your API key

Sign up and pick a plan

  1. Sign up at developers.kite.trade/signup. This is separate from your normal Kite login.
  2. Choose a plan:
Plan Cost What you get
Personal Free Place orders, view positions, holdings and funds
Connect ₹500/month Everything above, plus live and historical market data

The ₹500 is per API key, not per account. If you run two apps with two keys and both need market data, you pay twice. Zerodha's support page also notes that from October 2026, paid users who generate ₹2,000 or more in brokerage in a calendar month get 500 Developer Credits, which can renew the next month's subscription.

Create the app

Go to My Apps → Create New App and fill in the form:

Field What goes in it
App Name Anything you like
Zerodha Client ID Your trading account ID
Redirect URL Where Zerodha sends you after login
Postback URL Optional. Receives order update notifications

You'll get an API key and an API secret. Keep the secret private, like a password.

Get the redirect URL right

This is where most first attempts fail. After a successful login, Zerodha redirects the browser to the redirect URL registered on the developer console for that API key, with a request_token attached. It has to match what you configured. A different port, a missing path, localhost in one place and 127.0.0.1 in the other — any of those and the login either errors out or lands somewhere your script isn't watching.

For personal use, http://127.0.0.1:8000 is fine. Write down exactly what you entered and use that exact string everywhere else.

Step 2: Understand the daily token

Credential Lasts
API key and secret Until you regenerate them
Access token Until 6 AM the next day

Every API call needs a valid access token, so you need a new one each trading day.

Why 6 AM

The docs are explicit: unless the token is invalidated earlier, "it'll expire at 6 AM on the next day (regulatory requirement)". It isn't a setting and it isn't negotiable. Nothing you do to the token extends it.

What else kills a token early

Two things, and both surprise people mid-session:

Note that DELETE /session/token does not log you out of the official Kite web or mobile apps — it only kills the API session.

What about refresh_token?

You'll see a refresh_token field in the response from the token exchange, and for most people it comes back as an empty string. The docs say it gives "long standing read permissions" and is "only available to certain approved platforms". If you're an individual with a personal app, you don't have it and you can't apply for it by filling in a form. Stop looking for it and plan around the daily login instead.

Step 3: Refresh the token each morning

By hand

  1. Open https://kite.zerodha.com/connect/login?v=3&api_key=YOUR_API_KEY and log in.
  2. Copy the request_token from the URL you're redirected to.
  3. Exchange it for an access token:
from kiteconnect import KiteConnect

kite = KiteConnect(api_key="YOUR_API_KEY")
data = kite.generate_session("REQUEST_TOKEN", api_secret="YOUR_API_SECRET")
kite.set_access_token(data["access_token"])

Do step 3 straight away. The docs call request_token a "one-time token" and say "this token's lifetime is only a few minutes and it is meant to be exchanged for an access_token immediately after being obtained". It is single-use: once you've exchanged it, it's spent. If you fumble it, go back to step 1 and get a new one — there's nothing to retry.

The checksum, if you're not using the Python client

The Python client hides this, but the underlying call is a POST to https://api.kite.trade/session/token with three parameters: api_key, request_token and checksum. The checksum is the SHA-256 hash of api_key + request_token + api_secret, concatenated in that order with nothing between them:

import hashlib

checksum = hashlib.sha256(
    (API_KEY + REQUEST_TOKEN + API_SECRET).encode("utf-8")
).hexdigest()

Send X-Kite-Version: 3 as a header. After that, every other request carries Authorization: token api_key:access_token.

Scripting the login

Most people script the login so they don't have to do it every morning. Before you copy this: Zerodha's forum team has said plainly that the exchange requires a trader to log in manually at least once a day, and that they don't recommend automating the login flow. The password and 2FA endpoints below are internal Kite Web endpoints, not part of the documented Kite Connect API, so they can change without notice and take your script with them.

With that said, here's the shape of it:

import re
import pyotp
import requests
from kiteconnect import KiteConnect

API_KEY, API_SECRET = "...", "..."
USER_ID, PASSWORD, TOTP_SECRET = "AB1234", "...", "..."

s = requests.Session()

# 1. user id + password
r = s.post("https://kite.zerodha.com/api/login",
           data={"user_id": USER_ID, "password": PASSWORD})
r.raise_for_status()
request_id = r.json()["data"]["request_id"]

# 2. TOTP as the second factor
r = s.post("https://kite.zerodha.com/api/twofa",
           data={"user_id": USER_ID,
                 "request_id": request_id,
                 "twofa_type": "totp",
                 "twofa_value": pyotp.TOTP(TOTP_SECRET).now()})
r.raise_for_status()

# 3. follow the Connect login URL; the redirect carries request_token
try:
    s.get(f"https://kite.zerodha.com/connect/login?v=3&api_key={API_KEY}")
    raise RuntimeError("expected a redirect to the app's redirect URL")
except requests.exceptions.ConnectionError as e:
    request_token = re.search(r"request_token=([A-Za-z0-9]+)", str(e)).group(1)

# 4. exchange it, immediately
kite = KiteConnect(api_key=API_KEY)
data = kite.generate_session(request_token, api_secret=API_SECRET)
print(data["access_token"])

TOTP_SECRET is the base32 string behind the QR code you scanned when you set up 2FA, not the six-digit code. pyotp turns it into the current code.

Step 3 deserves a word. The redirect goes to your registered redirect URL, which for a personal app is a local port with nothing listening on it, so requests raises a ConnectionError and the token is sitting in the error message. It works, but it's a hack built on a failure. The tidier version is to run a tiny local HTTP server on that port and read the request_token query parameter out of the request it receives.

Either way, this is the brittle part of your whole setup. Treat a login failure as expected behaviour, not an emergency.

Step 4: Schedule it and alert when it fails

The token dies at 6 AM, so refresh after 6 AM and well before the open. Give yourself enough room to fix a failure by hand.

With cron, for weekday mornings:

15 8 * * 1-5 /usr/bin/python3 /home/you/refresh_token.py >> /home/you/logs/token.log 2>&1

A systemd timer is worth the extra file if you want the job to catch up after the machine was asleep, since Persistent=true runs a missed job on the next boot:

[Unit]
Description=Refresh Kite access token

[Timer]
OnCalendar=Mon..Fri 08:15
Persistent=true

[Install]
WantedBy=timers.target

Then the part people skip: alert yourself when it fails. A silent failure at 8:15 means you find out at the open, which is the worst possible time to be reading a stack trace. Have the script send you a message — Telegram, email, a push notification, whatever you'll actually see — on failure, and write the new token somewhere your other code reads rather than embedding it. Checking that the token works is one extra call:

kite.profile()

If that returns, you're good for the day.

Step 5: Handle TokenException by logging in again

TokenException comes with a 403 and means, in the docs' words, "the expiry or invalidation of an authenticated session". The guidance is equally direct: "you should clear the user's session and re-initiate a login."

So don't put it in a retry loop. A retry with the same dead token fails exactly the same way, and you'll burn through your rate limit discovering that. Catch it, stop making calls, run the login flow again, and carry on with the new token. If the re-login also fails, alert and stop — don't keep hammering.

Treat it as different in kind from NetworkException or a timeout, which are worth retrying with a backoff.

Rate and order limits

Worth knowing before you build, because hitting these looks like a bug:

Limit Value
Quote endpoint 1 request/second
Historical candles 3 requests/second
Order placement 10 requests/second
All other endpoints 10 requests/second
Orders per minute 400
Orders per day 5,000 per user or API key
Modifications per order 25, then cancel and place again

Troubleshooting

Symptom Likely cause What to check
TokenException, 403, first call of the day Token expired at 6 AM Run the login flow again. Check the stored token's date
TokenException, 403, mid-session Master logout from Kite Web, or your own DELETE /session/token Did you log out of Kite in a browser? Does your code call logout anywhere?
Login succeeds but no request_token arrives Redirect URL doesn't match the app config Compare the console's redirect URL character by character with where you're listening
"Invalid request_token" Already used, or more than a few minutes old Get a fresh one. Exchange it immediately. Don't cache it
"Invalid checksum" Wrong concatenation order or wrong secret SHA-256 of api_key + request_token + api_secret, in that order, nothing between them
Scripted login breaks overnight Internal login endpoints or page changed Log in by hand to confirm the account is fine, then fix the script
Market data calls fail, orders work No paid subscription on that key Check the plan on the key you're using
Random 429s or dropped calls Rate limit Check your requests/second against the table above, especially quote and historical

Stay safe

Conclusion

The API key part is paperwork. The daily token is the bit you actually have to design for, and the honest summary is that Kite Connect expects a human to log in once a day and nothing you build removes that. So build around it: refresh after 6 AM and before the open, verify the token with one call, alert loudly when the login fails, treat TokenException as "log in again" rather than "retry", and keep a two-minute manual path for the mornings when the script loses. Get that right once and the token stops being something you think about.

Sources

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

Questions people ask me

Why does my access token stop working every day?

Zerodha expires every Kite Connect access token at 6 AM the next day. It's a regulatory rule, so there's no way to keep one alive longer.

Is Kite Connect free?

Placing orders and reading your positions and holdings is free. Live and historical market data costs ₹500/month per API key.

What's the difference between the API key and the access token?

The API key and secret identify your app and don't change. The access token comes from a fresh login and expires every morning.

Can I automate the daily login?

You can script it, but Zerodha doesn't recommend it. Their forum team says the exchange requires a trader to log in manually at least once a day, and a scripted login uses internal endpoints that can change without notice. Keep a manual fallback.

Why do I get a TokenException in the middle of the day?

Something invalidated the session, usually a DELETE /session/token call in your own code or a master logout from the Kite Web terminal. Log in again to get a fresh token. Retrying the same call won't help.

zerodhakite connectbroker apiaccess tokengetting started
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
30 Sept 2026
Streaming live prices with the Kite Connect websocket in PythonA 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.
Guides