Skip to main content

tej

PyPI Python License: MIT

Python SDK for tej-api: open, free, end-of-day market data for NSE and BSE. No signup, no keys, no catch.

Zero runtime dependencies. Sync and async clients with identical surface. Returns plain list[dict] rows so you can feed them straight to polars, pandas, or your own code without an extra conversion step.

Install

pip install tejhq

That is the entire install. No httpx, no aiohttp, no pydantic. Just stdlib. Distribution is tejhq; import as tej.

Quick start (sync)

from tej import Client

c = Client()

# Last 90 days of RELIANCE (default range)
rows = c.ohlcv("RELIANCE", "nse")
print(rows[-1])
# {'date': '2026-06-05', 'open': 1442.0, 'high': 1455.7, ...}

# Full history since 2010
rows = c.ohlcv("RELIANCE", "nse", "2010-01-04", "2026-06-05")
print(len(rows))  # ~4040

# Full-market snapshot for one trading date
snap = c.snapshot("nse", "2025-05-28")
print(len(snap))  # ~2700 symbols

# Corporate actions (dividends, splits, bonuses)
acts = c.actions("RELIANCE")
print(acts[0])
# {'exchange': 'NSE', 'type': 'dividend', 'ex_date': '2024-10-28', 'cash_amount': 10.0, ...}

Quick start (async)

import asyncio
from tej import AsyncClient

async def main():
    async with AsyncClient() as c:
        # Fan out 10 symbols concurrently
        symbols = ["RELIANCE", "TCS", "INFY", "HDFCBANK", "ICICIBANK",
                   "SBIN", "HINDUNILVR", "ITC", "LT", "AXISBANK"]
        results = await asyncio.gather(*[c.ohlcv(s, "nse") for s in symbols])
        for sym, rows in zip(symbols, results):
            print(sym, len(rows))

asyncio.run(main())

Use with polars / pandas

tej returns list[dict] so you bring your own dataframe library.

import polars as pl
from tej import Client

rows = Client().ohlcv("RELIANCE", "nse", "2010-01-04", "2026-06-05")
df = pl.DataFrame(rows)
df = df.with_columns(pl.col("date").str.to_date())
df.head()
import pandas as pd
from tej import Client

rows = Client().ohlcv("RELIANCE", "nse", "2025-01-01", "2025-06-01")
df = pd.DataFrame(rows)
df["date"] = pd.to_datetime(df["date"])
df.set_index("date", inplace=True)
df["close"].plot()

Optional extras install the dataframe library alongside the SDK:

pip install "tejhq[polars]"
pip install "tejhq[pandas]"

Free key endpoints

Get a key at tejhq.dev/keys, no card, no password.

from tej import Client

c = Client(api_key="tej_live_...")

# Back-adjusted prices, continuous through splits, bonuses and dividends
adj = c.adjusted("RELIANCE", "nse", "2024-10-01", "2024-11-30")
print(adj[-1]["adj_close"], adj[-1]["adj_factor_cumulative"])

# Symbol history by ISIN or symbol
c.symbols("nse", isin="INE040A01034")
c.symbols("nse", symbol="HDFCBANK")

# Account, keys, today's usage
c.me()

Pro endpoints

c = Client(api_key="tej_live_...")   # a key on the Pro tier

# Derived metrics per day: returns, 52w high/low, average volume and turnover
m = c.metrics("RELIANCE", "nse", "2026-01-01")

# Point-in-time liquidity universe, survivorship-bias-free
members = c.universe("liquid500", "nse", as_of="2019-03-15")
symbols = [x["symbol"] for x in members]

# Up to 50 symbols in one request, dict keyed by symbol
bars = c.batch(["RELIANCE", "TCS", "INFY"], "nse", "2026-01-01")

# Free text to symbol
c.resolve("tata motors", exchange="nse", limit=3)
c.resolve("zomato")   # former ticker resolves to the current one

API reference

Method Endpoint Tier Returns
c.ohlcv(symbol, exchange, from_=None, to=None) GET /v1/ohlcv/{exchange}/{symbol} keyless list[OHLCV]
c.snapshot(exchange, date) GET /v1/snapshot/{exchange}?date= keyless list[SnapshotRow]
c.actions(symbol) GET /v1/actions/{symbol} keyless list[Action]
c.adjusted(symbol, exchange, from_=None, to=None) GET /v1/adjusted/{exchange}/{symbol} free key list[AdjustedRow]
c.symbols(exchange, symbol=None, isin=None) GET /v1/symbols/{exchange} free key list[SymbolInterval]
c.me() GET /v1/me free key dict
c.metrics(symbol, exchange, from_=None, to=None) GET /v1/metrics/{exchange}/{symbol} pro list[MetricsRow]
c.universe(name, exchange="nse", as_of=None) GET /v1/universe/{name} pro list[UniverseMember]
c.batch(symbols, exchange="nse", from_=None, to=None) GET /v1/batch pro dict[str, list[OHLCV]]
c.resolve(q, exchange="both", limit=5) GET /v1/resolve pro list[ResolveHit]
c.health(), c.ready() GET /health, GET /ready keyless dict

Need the response envelope (with meta)? Use c.ohlcv_envelope(...), which returns the raw {"data": [...], "meta": {...}} dict.

AsyncClient exposes the same surface with await.

Configuration

from tej import Client

c = Client(
    base_url="https://api.tejhq.dev",   # override for self-hosted or staging
    api_key=None,                        # tej_live_... from https://tejhq.dev/keys; keyless works for ohlcv, snapshot, actions
    timeout=30.0,                        # seconds
    max_retries=3,                       # exponential backoff on 5xx/429/network
    user_agent_suffix="my-app/1.0",      # for attribution
    default_headers={"X-My-Header": "hi"},
)

Errors

All errors inherit from tej.TejError. The specific subclass tells you what happened:

Exception When
BadRequestError HTTP 400, bad path or query parameter (also raised locally on invalid args before the request goes out, as a plain ValueError)
AuthError HTTP 401, key_required when no key was sent to a gated endpoint, invalid_key when the key is malformed, unknown, or revoked
NotFoundError HTTP 404
ProRequiredError HTTP 402, the key's tier is too low for this endpoint (/v1/universe, /v1/metrics need Pro)
TejError with error_code == "key_required" HTTP 401, the endpoint needs a key and none was sent (/v1/adjusted, /v1/symbols)
TejError with error_code == "invalid_key" HTTP 401, the key is malformed, unknown, or revoked
RateLimitError HTTP 429
ServerError HTTP 5xx
NetworkError DNS, connection, TLS, or timeout failure
TejError Anything else
from tej import Client, ProRequiredError

try:
    Client().ohlcv("RELIANCE", "nse")
except ProRequiredError as e:
    print(e.error_code)  # 'pro_required'
    print(e.request_id)  # tej-api request id from response headers

Why a separate package

tej-api is a Go service. tej-bazaar is the Python ingestion pipeline. This repo is the thin client that anyone can pip install without dragging in either, and that releases on its own cadence as the API evolves.

Coverage

  • NSE bhavcopy + corp actions: 2010-01-04 to today, ~4,047 trading days, ~7M rows
  • BSE bhavcopy + corp actions: 2024-07-08 to today (the SEBI CMTS cutover), ~470 days, ~1M rows
  • Cron refresh: weekdays 20:00 IST

Keyless endpoints are served at the Cloudflare edge from pre-rendered JSON, so most requests return in well under a second and repeat requests are cache hits. Keyless access is rate limited to 100 requests per 10 seconds per IP at the edge and 120 per minute at origin; the SDK retries 429s with backoff. Keyed requests bypass the edge cache and carry X-RateLimit-Limit-Day and X-RateLimit-Remaining-Day headers: 1,000 a day and 300 a minute on a free key.

License

MIT. Use it, fork it, ship products on top of it.

Links

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tejhq-0.2.0.tar.gz (12.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tejhq-0.2.0-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file tejhq-0.2.0.tar.gz.

File metadata

  • Download URL: tejhq-0.2.0.tar.gz
  • Upload date:
  • Size: 12.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tejhq-0.2.0.tar.gz
Algorithm Hash digest
SHA256 00d821ad02db41f5687b9299673aaa09dccbc1c2927710a8974c5b6684fc11d8
MD5 c41514252cd572793af6f7f00ac05cc9
BLAKE2b-256 326f461323b53946b91d02169a056112ed5227aa27b44c0f1c5eb3025150b878

See more details on using hashes here.

Provenance

The following attestation bundles were made for tejhq-0.2.0.tar.gz:

Publisher: release.yml on tejhq/tej-sdk-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tejhq-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: tejhq-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tejhq-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 56ecd3380eb86c6a13dc71d871d034e56787febdd204d5c5cb565b0ea774b630
MD5 b797aa17b9d463a205e1dcf463ac3abc
BLAKE2b-256 743358ceb47743ecb3f6b2ba4681915548a420ef7eb3f4fcad30586cf3626eb9

See more details on using hashes here.

Provenance

The following attestation bundles were made for tejhq-0.2.0-py3-none-any.whl:

Publisher: release.yml on tejhq/tej-sdk-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.0

2 files

This release

0.2.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page