Production-grade NSE data engine for Python. Cache-first, circuit-protected, async-ready.
pip install nsefetchfrom nsefetch import MarketService
with MarketService() as service:
quote = service.get_stock("RELIANCE")
print(quote.data.last_price) # e.g. 2947.35
chain = service.get_option_chain("NIFTY")
print(chain.data.expiry) # nearest expiry date- ⚡ Cache-first runtime — in-process or Redis-backed, with per-operation TTLs
- 🔌 Circuit breaker — automatic failure isolation and recovery
- 🔄 Request coalescing — deduplicates concurrent identical requests within a process
- 🚦 GCRA rate limiter — Redis or in-memory, prevents NSE from rate-limiting you
- 🌊 Stale fallback — serves cached data gracefully on live fetch failure
- ⚙️ Async parity — every sync method has an exact async equivalent
- 🛡️ Typed responses — Pydantic v2 models on every response, always a
ResponseEnvelope[T] - 🧪 131 tests — zero live-network calls in normal test runs
- 🔒 NSE-only — no silent Yahoo Finance substitution; you always know what you're getting
All methods are available on both MarketService (sync) and AsyncMarketService (async).
| Method | Description |
|---|---|
get_nifty50() |
NIFTY 50 index snapshot |
get_index(index_name) |
Any supported NSE index snapshot |
get_stock(symbol) |
Single-symbol equity quote |
get_bulk_stocks(symbols) |
Multi-symbol equity quotes with smart batching |
get_option_chain(symbol, expiry=None) |
Option chain; defaults to nearest expiry |
get_option_chain_expiries(symbol) |
All available option expiry dates |
get_futures_data(symbol) |
F&O futures snapshot |
get_delivery_data(symbol) |
Delivery / DPIIT data |
get_market_depth(symbol) |
Level-2 order book (optional data) |
get_sector_data() |
All NSE sector snapshots |
Every method returns ResponseEnvelope[T] with:
envelope.success # bool
envelope.data # typed payload
envelope.cache_hit # bool — was this served from cache?
envelope.stale # bool — was this a stale cache fallback?
envelope.degraded # bool — did a backend component fail?
envelope.fetched_at # datetime
envelope.warnings # list of WarningInfoSimple. No Redis, no background infrastructure needed.
from nsefetch import MarketService
service = MarketService()
quote = service.get_stock("RELIANCE")
service.close()
# or use as a context manager
with MarketService() as service:
quote = service.get_stock("RELIANCE")Adds in-process LRU cache + rate limiter. Best for local scripts that poll repeatedly.
from nsefetch import create_cached_market_service
with create_cached_market_service() as service:
# First call hits NSE; subsequent calls within TTL hit cache
quote = service.get_stock("RELIANCE")For dashboard backends and multi-worker deployments. Keeps frontend traffic reading from cache instead of hammering NSE directly.
from nsefetch import create_production_market_service
with create_production_market_service() as service:
snapshot = service.get_nifty50()All three modes have async counterparts:
from nsefetch import AsyncMarketService, create_async_cached_market_service
async with AsyncMarketService() as service:
quote = await service.get_stock("INFY")
async with create_async_cached_market_service() as service:
chain = await service.get_option_chain("BANKNIFTY")with MarketService() as service:
# Discover all available expiries
expiries = service.get_option_chain_expiries("NIFTY")
print(expiries.data) # ['2026-05-29', '2026-06-26', ...]
# Fetch a specific expiry
chain = service.get_option_chain("NIFTY", expiry="2026-06-26")graph TD
A[MarketService / AsyncMarketService] --> B[Rate Limiter]
B --> C[Cache Layer]
C -->|cache miss| D[Circuit Breaker]
D --> E[NSE HTTP Client]
E --> F[curl_cffi Transport]
F --> G[NSE API]
C -->|cache hit| H[ResponseEnvelope]
D -->|open| I[Stale Fallback Cache]
I --> H
| Layer | Component | Purpose |
|---|---|---|
| Service | MarketService |
Public API, context manager lifecycle |
| Reliability | CircuitBreaker |
Blocks requests when provider is failing |
| Reliability | RateLimiter |
GCRA — prevents NSE from blocking you |
| Caching | CacheManager |
In-memory or Redis, request coalescing |
| Transport | NSEHttpClient |
Browser-impersonating session (curl_cffi) |
| Normalizer | NSEMarketDataNormalizer |
Raw payload → typed Pydantic schemas |
All settings are configurable via environment variables prefixed with NSEFETCH_:
# Redis connection (for production/cached modes)
NSEFETCH_REDIS_URL=redis://localhost:6379/0
# Cache TTLs (seconds)
NSEFETCH_CACHE_TTL_STOCK=15
NSEFETCH_CACHE_TTL_INDEX=10
NSEFETCH_CACHE_TTL_OPTION_CHAIN=30
# Circuit breaker
NSEFETCH_CIRCUIT_BREAKER_FAILURE_THRESHOLD=5
NSEFETCH_CIRCUIT_BREAKER_RECOVERY_WINDOW=60
# Rate limiter
NSEFETCH_RATE_LIMIT_REQUESTS_PER_SECOND=2
# Live probe gate
NSEFETCH_LIVE_PROBE_ENABLED=1Or pass a Settings object directly:
from nsefetch import MarketService, Settings
settings = Settings(redis_url="redis://localhost:6379/0")
service = MarketService(settings=settings)Test your connectivity to NSE before deploying:
# Transport-level probe
NSEFETCH_LIVE_PROBE_ENABLED=1 python -m nsefetch.live_probe
# Service-level probe (exercises all 10 methods)
NSEFETCH_LIVE_PROBE_ENABLED=1 python -m nsefetch.live_probe --service
# Both together
NSEFETCH_LIVE_PROBE_ENABLED=1 python -m nsefetch.live_probe --allContributions are welcome! See CONTRIBUTING.md for setup instructions, coding standards, and PR guidelines.
Good first issues include: new index names, BSE provider, pandas integration, and export utilities.
This library is for educational and research purposes only. It is not affiliated with, endorsed by, or connected to the National Stock Exchange of India (NSE). Use at your own risk.
See DISCLAIMER.md for the full legal notice and LICENSE for the MIT license.