Skip to main content

BharatStock Python Client

Official Python client for the BharatStock API — reliable Indian stock market data (NSE/BSE): EOD prices, quarterly/annual financials, shareholding patterns, corporate actions, derived per-stock metrics, a screener, bulk/block deals, insider trades, indices, and market-wide FII/DII activity.

📋 Jump to Changelog — what's new in each release.

Install

pip install bharatstock

Requires Python 3.9+ (the only dependency is httpx).

To work on the client from a checkout of this repo, install it editable:

pip install -e sdk/python

Authentication

Every data endpoint is authenticated with your bsk_live_... key, sent in the X-API-Key header. Get one from the dashboard.

from bharatstock import BharatStock

# Pass the key explicitly...
client = BharatStock(api_key="bsk_live_...")

# ...or set BHARATSTOCK_API_KEY in the environment and omit it:
client = BharatStock()

Quickstart

from bharatstock import BharatStock

client = BharatStock(api_key="bsk_live_...")

# A single stock, with latest price + ~70 derived metrics
stock = client.stocks.get("RELIANCE")
print(stock.company_name, stock.exchange)
print("P/E:", stock.metrics.pe_ratio, "ROE:", stock.metrics.roe)

# Batch quotes for a watchlist (one call, up to 50 symbols)
for q in client.stocks.quotes(["TCS", "INFY", "HDFCBANK"]):
    print(q.symbol, q.close, q.change_pct)

# Search
for hit in client.search("tata"):
    print(hit.symbol, hit.company_name)

# Public data-integrity status (no key required)
print(client.status().status)   # "operational" | "degraded"

Pagination

List endpoints return a Page object: iterate it directly for the rows, or read .total_pages / .has_next to page through manually.

# One page
page = client.stocks.prices("RELIANCE", from_date="2026-01-01", page_size=100)
print(page.total_items, page.total_pages)
for row in page:
    print(row.trade_date, row.close, row.adjusted_close)

# Auto-iterate every stock across all pages (lazy generator)
for s in client.stocks.iter_all(sector="Banking"):
    print(s.symbol)

Date ranges use from_date= / to_date= (sent to the API as from / to), in YYYY-MM-DD form.

Screener

results = client.screener.run(
    filters=["pe_ratio.lt.15", "roe.gt.18", "market_cap.gt.10000"],  # Cr
    sort_by="roe",
    sort_order="desc",
    page_size=25,
)
for r in results:
    print(r.symbol, r.pe_ratio, r.roe)

Filter syntax is metric.operator.value where the operator is one of gt | lt | gte | lte | eq. market_cap values are in Crores.

Rate limits & retries

Plans have a daily request cap. When you exceed it the API returns HTTP 429. The client automatically retries a 429 a few times with exponential backoff (the API does not send a Retry-After header, so the wait is client-side); if it's still capped it raises RateLimitError.

from bharatstock import BharatStock, RateLimitError, NotFoundError

client = BharatStock(api_key="bsk_live_...", max_retries=3)

try:
    stock = client.stocks.get("NONEXISTENT")
except NotFoundError:
    print("no such ticker")
except RateLimitError as e:
    print("slow down:", e.detail)

All errors subclass BharatStockError, so you can catch that one type to handle any API failure. Specific subclasses: AuthenticationError (401), NotFoundError (404), RateLimitError (429), BadRequestError (400/422), APIError (everything else).

Method reference

Every method and its parameters. Types ship with the package (py.typed), so your editor autocompletes each method and every field on the returned objects. Keyword-only params show their default; page/page_size are omitted from the notes below but accepted by every paginated method. Methods that return Page are paginated (iterate directly, or use .total_pages / .has_next); the rest return a single object or a plain list.

client.stocks

Method Key parameters Returns
list(...) q=None, sector=None, active_only=True Page[StockSummary]
iter_all(...) q=None, sector=None, active_only=True (lazy, walks all pages) iterator of StockSummary
get(ticker, exchange=None) ticker accepts a symbol or ISIN; exchange = "NSE"/"BSE" to disambiguate a shared ticker StockDetail
quotes(symbols) symbols: list of up to 50 tickers list[QuoteItem] (unknown symbols returned with found=False)
compare(sector, ...) sort = market_cap|pe_ratio|pb_ratio|roe|roce (default market_cap), limit=20 list[ComparisonItem]
prices(ticker, ...) from_date=None, to_date=None (YYYY-MM-DD), exchange=None Page[DailyPricePoint]
financials(ticker, ...) period_type = quarterly|annual (default quarterly), exchange=None Page[FinancialPeriod]
ratios(ticker, exchange=None) — RatioSnapshot
corporate_actions(ticker, ...) action_type=None (dividend|bonus|split|rights|buyback), exchange=None Page[CorporateActionItem]
technical_indicators(ticker, ...) from_date, to_date, sma_period=20, ema_period=20, rsi_period=14, exchange=None Page[TechnicalIndicatorPoint]
shareholding(ticker, ...) exchange=None Page[ShareholdingPatternItem]
mf_holdings(ticker, ...) month=None (YYYY-MM), exchange=None Page[MFHoldingItem]
bulk_deals(ticker, ...) buy_sell=None (BUY|SELL), exchange=None Page[DealItem]
block_deals(ticker, ...) buy_sell=None (BUY|SELL), exchange=None Page[DealItem]
insider_trades(ticker, ...) transaction_type=None (acquisition|disposal), promoters_only=False, exchange=None Page[InsiderTradeItem]

client.deals (market-wide, across all stocks)

Method Key parameters Returns
bulk(...) buy_sell=None (BUY|SELL) Page[DealItem]
block(...) buy_sell=None (BUY|SELL) Page[DealItem]
insider_trades(...) transaction_type=None (acquisition|disposal), promoters_only=False Page[InsiderTradeItem]

client.screener

Method Key parameters Returns
run(...) filters=None (list of metric.operator.value), sector=None, exchange=None, sort_by="market_cap", sort_order="desc" (asc|desc) Page[ScreenerResult]

client.indices

Method Key parameters Returns
list(...) category=None, active_only=True Page[IndexSummary]
prices(name, ...) from_date=None, to_date=None (YYYY-MM-DD) Page[IndexPricePoint]

client.market

Method Key parameters Returns
fii_dii(...) from_date=None (default 30 days before to), to_date=None (default today), limit=30, latest=False (only the single most recent day; ignores from/to/limit) FiiDiiActivity (.data is a list of FiiDiiDay)

client.mutual_funds

The industry-wide AMFI scheme NAV dataset — every scheme/plan/option across all AMCs, keyed by AMFI scheme_code (distinct from stocks.mf_holdings, which is which schemes hold a given stock).

Method Key parameters Returns
list_schemes(...) q=None, amc=None, category=None (e.g. "Flexi Cap"), plan=None ("Direct"/"Regular"), option=None ("Growth"/"IDCW"), isin=None, limit=50, offset=0 MFSchemeList (.data is a list of MFScheme)
get_scheme(scheme_code) MFScheme (incl. latest_nav, latest_nav_date)
nav(scheme_code, ...) from_date=None (default 1y), to_date=None, limit=400 (free-tier keys limited to a recent window) MFNavHistory (.data is a list of MFNavPoint)
returns(scheme_code) MFSchemeReturns (.returns_pct maps 1m/3m/6m/1y/3y/5y/since_inception → % (CAGR for ≥1y), computed from NAV)
list_portfolios(...) q=None, fund_house=None, limit=50, offset=0 MFPortfolioList (.data is a list of MFPortfolioSummary)
get_portfolio_holdings(scheme_name, ...) page=1, page_size=50, month=None ("YYYY-MM", latest if omitted) Page[MFPortfolioHolding]

The portfolio methods are the inverse of stocks.mf_holdings: given a scheme they return the stocks it holds. They're keyed by the holdings scheme-name string (not the AMFI scheme_code) — AMFI publishes monthly portfolios at the scheme level, so holdings carry no scheme_code and no Direct/Regular distinction. Use list_portfolios(...) to discover a name, then get_portfolio_holdings(name, month="YYYY-MM"). An unknown name returns an empty page; free-tier keys are limited to recent months.

picks = client.mutual_funds.list_portfolios(q="flexi")
name = picks.data[0].scheme_name
page = client.mutual_funds.get_portfolio_holdings(name, month="2026-08")
for h in page:
    print(h.symbol, h.pct_of_aum, h.quantity_change)

Top-level helpers

Method Key parameters Returns
client.search(q, limit=10) fuzzy match on symbol or company name list[StockSummary]
client.movers(category="gainers", limit=20) category = gainers|losers|active list[MoverItem]
client.price_shockers(min_change_pct=5.0, direction="both", limit=50) direction = up|down|both list[PriceShockerItem]
client.status() no auth required StatusReport (.status, .checks, .is_operational)

Notes

  • market_cap units differ by endpoint (this mirrors the current API, so the client reports exactly what the server sends):
    • Rupees: stocks.get, stocks.list / iter_all, search (StockSummary/StockDetail.market_cap), stocks.compare (ComparisonItem), and stocks.ratios (RatioSnapshot).
    • Crores (1 Cr = 10,000,000): the metrics block on stocks.get (StockDetail.metrics.market_cap) and screener.run (ScreenerResult).
    • The screener.run market_cap filter value is also in Crores (e.g. "market_cap.gt.10000" = > 10,000 Cr). So stock.market_cap and stock.metrics.market_cap on the same object are in different units (rupees vs Crores) — divide the rupee value by 1e7 to compare. Convert with crores = rupees / 10_000_000.
  • Use the client as a context manager (with BharatStock(...) as c:) to close the underlying HTTP connection pool when you're done.
  • Types ship with the package (py.typed), so editors autocomplete every method and response field.

Changelog

  • 0.1.12 (2026-11-02): Financials — three new typed fields on FinancialPeriod, all as-filed from the exchange XBRL: segment_results_total (the filing's own segment total — the reconciliation anchor the per-segment segment_results roll up toward profit before interest and tax, as a separate scalar so summing the array never double-counts), and segment_notes / results_notes (the narrative note blocks; the segment note carries the unallocated/elimination/exceptional reconciliation in prose). None/absent for legacy filings. Additive only — segment_results is unchanged.
  • 0.1.11 (2026-10-05): Forward-compatibility — every response now keeps its full raw JSON on .raw, so a field the API adds later is never dropped. Each model exposes obj.raw["new_field"] and a page exposes page.raw["new_field"] (envelope-level), with no SDK upgrade needed; known fields stay fully typed.
  • 0.1.10 (2026-10-05): MutualFunds.get_portfolio_holdings(...) now surfaces coverage_pct on the returned page — the total % of AUM captured by the holdings in that scheme/month. Holdings are Indian listed equities only, so this typically sums to under 100; the shortfall is the uncaptured portion (foreign equities, debt, cash, etc.). None for other paginated endpoints.
  • 0.1.9 (2026-09-20): Added MF portfolio methods to the MutualFunds resource — list_portfolios(...) (discover the schemes we hold monthly portfolio data for, by holdings scheme-name) and get_portfolio_holdings(scheme_name, month=...) (a scheme's equity portfolio for a month, with month-over-month quantity_change). The inverse of per-stock stocks.mf_holdings; keyed by scheme-name, not the AMFI scheme_code. New typed models MFPortfolioList, MFPortfolioSummary, MFPortfolioHolding. Additive and backward compatible.
  • 0.1.8 (2026-09-15): Packaging metadata only — the PyPI sidebar Changelog link now points at this README's own Changelog section on the PyPI page (pypi.org/project/bharatstock/#user-content-changelog) instead of the external docs site. No code, API, or behavior changes.
  • 0.1.7 (2026-09-15): Packaging metadata only — the PyPI sidebar Changelog link now points at the docs-site changelog section (bharatstockapi.com/reference.html#changelog, a real anchor) and a "Jump to Changelog" link was added at the top of this README. No code, API, or behavior changes.
  • 0.1.6 (2026-09-15): Packaging metadata only — added a Changelog project URL and a standalone CHANGELOG.md. No code, API, or behavior changes.
  • 0.1.5 (2026-09-15): Documentation and metadata refresh for the Mutual Funds surface — no code or behavior changes, fully backward compatible. The MutualFunds resource is now completely documented in the README with runnable examples: list and search the full universe of AMFI schemes (client.mutual_funds.list_schemes(...), filter by AMC / plan / option / category), fetch a single scheme's identity and latest NAV (client.mutual_funds.get_scheme(scheme_code)), pull its daily NAV time series going back as far as ~20 years for backtesting and SIP/rolling-return analysis (client.mutual_funds.nav(scheme_code, from_date=..., to_date=...), server-side capped at 5000 points per call — page with from_date/to_date), and read trailing returns computed from that NAV series (client.mutual_funds.returns(scheme_code) → 1m/3m/6m/1y/3y/5y/ since_inception, where ≥1-year horizons are annualized CAGR). All response models are fully typed (MFSchemeList, MFScheme, MFNavHistory, MFSchemeReturns). Free-tier keys are limited to ~1 year of NAV history; paid tiers get the full archive.
  • 0.1.4: Added the MutualFunds resource (client.mutual_funds) — the first release with mutual fund coverage. Introduced list_schemes(), get_scheme(), nav() and returns() plus their typed response dataclasses (MFSchemeList, MFScheme, MFNavHistory, MFSchemeReturns), backed by the AMFI industry-wide daily NAV feed and the returns endpoint (1m/3m/6m/1y/3y/5y/since-inception). No breaking changes to the existing stock / deals / screener / indices / market resources.

License

MIT

Metadata

Release files for bharatstock 0.1.12

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bharatstock 0.1.12
File Size Uploaded Provenance Actions
bharatstock-0.1.12.tar.gz 30.3 kB With provenance Details for bharatstock-0.1.12.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for bharatstock 0.1.12
File Interpreter ABI Platform Actions
bharatstock-0.1.12-py3-none-any.whl Python 3 none any Details for bharatstock-0.1.12-py3-none-any.whl

Total release size: 53.5 kB

Release files / bharatstock-0.1.12.tar.gz

Download URL bharatstock-0.1.12.tar.gz
Size 30.3 kB
Tags Source
SHA-256 checksum
How to use checksums
593d9ad5677b899fe5dd5eccd40bb1c2556c6f30acc7530d0d8ac26709e78445
BLAKE2b-256 checksum
How to use checksums
5bad6a9c9e83e41a326a88616cb41470797c1c9adbbf10c057579f866d52dd64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / bharatstock-0.1.12-py3-none-any.whl

Download URL bharatstock-0.1.12-py3-none-any.whl
Size 23.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1c897969027512265ebb6d35b8bd236e1cbb780ab49c091d3a97ac6852b695b5
BLAKE2b-256 checksum
How to use checksums
27e53de8859b076770ba51e9b79c981cfde650307d317ca7fc9037562dbe7202
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for bharatstock 0.1.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for bharatstock-0.1.12.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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