How to write a basic Python script using kiteconnect
The kiteconnect Python SDK is the official client library for the Kite Connect API from Zerodha . It wraps every REST endpoint and the WebSocket tick stream into a clean, Pythonic interface. This guide takes you from a blank directory to a working script that authenticates with Kite Connect, fetches your portfolio, queries live market prices, and places a paper-trade-style order. By the end you will have a reusable project structure suitable as the foundation for any algo trading strategy.
Project structure
A clean separation between authentication, configuration, and strategy logic makes scripts easier to maintain and test.
kite-project/
├── .env # credentials, never commit this file
├── .gitignore
├── requirements.txt
├── auth.py # daily token generation
├── main.py # portfolio + market data queries
└── orders.py # order placement helpers
Step-by-step procedure
Create a virtual environment and install dependencies
mkdir kite-project && cd kite-project
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install kiteconnect python-dotenv
pip freeze > requirements.txt
The virtual environment isolates project dependencies from your system Python. The python-dotenv package loads credentials from a .env file without exposing them in your code.
Store credentials securely
Create .env in the project root:
KITE_API_KEY=your_api_key_here
KITE_API_SECRET=your_api_secret_here
Create .gitignore:
.env
.venv/
__pycache__/
*.pyc
/tmp/
kite_token.json
Never commit .env to version control. If you use GitHub, add .env to .gitignore before the first commit.
Write the authentication module
Create auth.py:
"""auth.py, Kite Connect daily login flow."""
import os
import json
from pathlib import Path
from dotenv import load_dotenv
from kiteconnect import KiteConnect
load_dotenv()
API_KEY = os.environ["KITE_API_KEY"]
API_SECRET = os.environ["KITE_API_SECRET"]
TOKEN_FILE = Path("kite_token.json")
def get_kite_client() -> KiteConnect:
"""Return an authenticated KiteConnect instance for today's session."""
kite = KiteConnect(api_key=API_KEY)
if TOKEN_FILE.exists():
data = json.loads(TOKEN_FILE.read_text())
access_token = data.get("access_token")
if access_token:
kite.set_access_token(access_token)
# Quick validation call; raises TokenException if stale
try:
kite.profile()
return kite
except Exception:
pass # Token stale; fall through to re-login
# Interactive daily login
print("Open this URL in a browser:")
print(kite.login_url())
request_token = input("Paste the request_token from the redirect URL: ").strip()
session = kite.generate_session(request_token, api_secret=API_SECRET)
kite.set_access_token(session["access_token"])
TOKEN_FILE.write_text(json.dumps({"access_token": session["access_token"]}))
print("Authenticated successfully.")
return kite
if __name__ == "__main__":
kite = get_kite_client()
print("Logged in as:", kite.profile()["user_name"])
For a fully automated (headless) daily login that eliminates the manual browser step, see How to authenticate Kite Connect with TOTP automation .
Fetch portfolio and market data
Create main.py:
"""main.py, Portfolio, positions, and market data queries."""
from auth import get_kite_client
kite = get_kite_client()
# --- Profile ---
profile = kite.profile()
print(f"\nLogged in as: {profile['user_name']} ({profile['user_id']})")
print(f"Broker: {profile['broker']}")
# --- Account margins ---
margins = kite.margins()
print(f"\nEquity available margin: {margins['equity']['available']['live_balance']}")
print(f"Commodity available margin: {margins['commodity']['available']['live_balance']}")
# --- Holdings (delivery positions) ---
holdings = kite.holdings()
print(f"\nHoldings ({len(holdings)} positions):")
for h in holdings:
print(f" {h['tradingsymbol']:20s} qty={h['quantity']:5d} avg={h['average_price']:10.2f} ltp={h['last_price']:10.2f}")
# --- Intraday positions ---
positions = kite.positions()
day = positions["day"]
net = positions["net"]
print(f"\nIntraday positions (day): {len(day)}")
for p in day:
print(f" {p['tradingsymbol']:20s} qty={p['quantity']:5d} pnl={p['pnl']:10.2f}")
# --- Live quotes ---
symbols = ["NSE:RELIANCE", "NSE:INFY", "NSE:HDFCBANK"]
quotes = kite.quote(symbols)
print("\nLive quotes:")
for sym, q in quotes.items():
print(f" {sym:25s} ltp={q['last_price']:10.2f} volume={q['volume']:10d}")
# --- Historical OHLC (last 5 days) ---
import datetime
instruments = kite.instruments("NSE")
reliance_token = next(i["instrument_token"] for i in instruments if i["tradingsymbol"] == "RELIANCE")
to_date = datetime.date.today()
from_date = to_date - datetime.timedelta(days=7)
candles = kite.historical_data(
instrument_token=reliance_token,
from_date=from_date,
to_date=to_date,
interval="day",
)
print(f"\nRELIANCE daily OHLC (last {len(candles)} sessions):")
for c in candles[-5:]:
print(f" {c['date'].date()} O={c['open']:.2f} H={c['high']:.2f} L={c['low']:.2f} C={c['close']:.2f} V={c['volume']}")
Add order placement helpers
Create orders.py:
"""orders.py, Safe wrappers around kite.place_order()."""
from kiteconnect import KiteConnect
from kiteconnect.exceptions import InputException, OrderException, NetworkException
def place_market_order(
kite: KiteConnect,
exchange: str,
symbol: str,
transaction_type: str,
quantity: int,
product: str,
) -> str | None:
"""Place a market order and return the order_id, or None on failure."""
if quantity <= 0:
raise ValueError(f"Quantity must be positive, got {quantity}")
if transaction_type not in ("BUY", "SELL"):
raise ValueError(f"transaction_type must be BUY or SELL, got {transaction_type}")
try:
order_id = kite.place_order(
variety=kite.VARIETY_REGULAR,
exchange=exchange,
tradingsymbol=symbol,
transaction_type=transaction_type,
quantity=quantity,
product=product,
order_type=kite.ORDER_TYPE_MARKET,
)
print(f"Order placed: {order_id}")
return order_id
except InputException as e:
print(f"Input error: {e}")
except OrderException as e:
print(f"Order rejected: {e}")
except NetworkException as e:
print(f"Network error: {e}")
return None
def get_order_status(kite: KiteConnect, order_id: str) -> dict | None:
"""Return the latest status dict for an order_id."""
orders = kite.orders()
for o in orders:
if o["order_id"] == order_id:
return o
return None
To use in main.py:
from orders import place_market_order, get_order_status
# Uncomment to place a real order (ensure you have sufficient margin)
# order_id = place_market_order(kite, "NSE", "RELIANCE", "BUY", 1, "CNC")
# if order_id:
# status = get_order_status(kite, order_id)
# print("Order status:", status["status"])
Run the script
# With virtual environment active:
python auth.py # authenticate once; token saved to kite_token.json
python main.py # fetch portfolio and market data
On subsequent runs during the same trading day, auth.py loads the cached token and main.py runs without prompting.
Common SDK methods reference
| Method | Returns | Notes |
|---|---|---|
kite.profile() | dict | User name, email, broker, exchanges, products |
kite.margins() | dict | Available and used margin for equity and commodity |
kite.holdings() | list | Delivery portfolio with average price and LTP |
kite.positions() | dict | Day and net intraday positions |
kite.orders() | list | All orders placed today |
kite.trades() | list | All executed trades today |
kite.quote(symbols) | dict | Live snapshot for a list of exchange:symbol strings |
kite.ohlc(symbols) | dict | OHLC-only snapshot (lighter than quote) |
kite.ltp(symbols) | dict | LTP-only snapshot (lightest) |
kite.instruments(exchange) | list | All tradable instruments on the exchange |
kite.historical_data(...) | list | OHLC candles; intervals: minute, 3minute, 5minute, 15minute, 30minute, 60minute, day |
What can go wrong
ModuleNotFoundError: No module named 'kiteconnect'. The virtual environment is not activated, orpip install kiteconnectwas run in the system Python rather than the virtual environment. Activate.venvand reinstall.KeyError: 'KITE_API_KEY'. The.envfile is missing, in the wrong directory, orload_dotenv()was not called before readingos.environ. Ensureload_dotenv()is called at the top of the module.TokenExceptionon the validation call inget_kite_client. The saved token has expired (after 6:00 AM IST). Deletekite_token.jsonand runauth.pyagain for a fresh login.- Empty holdings or positions. Correct behaviour if your account has no holdings or no open intraday positions. The API returns an empty list, not an error.
DataExceptiononhistorical_data. Historical data is not available for all instruments (for example, very new listings or delisted stocks) or for intervals shorter than the instrument’s trading history.- Rate limit errors. If your script calls multiple REST endpoints in a loop, you may hit the 10 requests-per-second limit. Add
time.sleep(0.1)between calls.
Related guides
- How to generate a Kite Connect API key
- How to generate the request_token and access_token
- How to authenticate Kite Connect with TOTP automation
- How to place an order via the Kite Connect REST API
- How to stream live ticks via Kite WebSocket
- Kite Connect API overview
- Kite Connect ecosystem
- Zerodha overview
References
- Zerodha, Kite Connect developer documentation, kite.trade/docs/connect/, accessed 2024.
- kiteconnect Python SDK repository and README, github.com/zerodha/pykiteconnect, accessed 2024.
- PyPI, kiteconnect package, pypi.org/project/kiteconnect/, accessed 2024.
- SEBI, Circular on algorithmic trading by retail investors, SEBI/HO/MRD/2021 series, sebi.gov.in.
- Zerodha Z-Connect blog, Building with Kite Connect, zerodha.com/z-connect.