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
- Sign up at developers.kite.trade/signup. This is separate from your normal Kite login.
- 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:
- A
DELETE /session/tokencall invalidates the token immediately and destroys the API session. If your own code calls logout at the end of a run, make sure it isn't running while something else still needs the token. - A master logout from the Kite Web terminal does the same. If you log out of Kite in your browser during market hours, your running script loses its session. This is a common cause of a bot that dies halfway through the day for no apparent reason.
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
- Open
https://kite.zerodha.com/connect/login?v=3&api_key=YOUR_API_KEYand log in. - Copy the
request_tokenfrom the URL you're redirected to. - 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
- Never share or publish your API secret, password or TOTP secret. Anyone with them can access your account.
- Keep secrets out of your code and out of any public repository. Environment variables or a file outside the repo, with restrictive permissions.
- Never embed the API secret in a mobile or client-side app. The docs say this explicitly, and it's the one mistake you can't walk back.
- Have a manual backup. Automated logins break. Know how to get a token by hand in two minutes.
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
- Kite Connect user and login flow docs
- Kite Connect exceptions and rate limits
- Kite Connect pricing
- Kite Connect sign-up FAQ
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.