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.