Developers
Two machine-readable datasets and the manifest that lists them, documented field by field — including the one rule a consumer gets wrong, and everything deliberately left out.
contains_market_prices: false. There is no availability guarantee, no uptime commitment and no support channel. Do not put them on a code path that has to answer during market hours: fetch, cache, and fail loudly if the file is missing or its schema_version is not the one you built against.The three files
| File | dataset id | Contents | Human page |
|---|---|---|---|
| /data/index.json | public-reference-index | The discovery manifest: one entry per dataset with its URL, record count and documentation page, the not_publishedlist naming what is left out and why, and links to the site's full URL inventory and curated page map. | This page |
| /data/lot-sizes.json | index-derivative-lot-sizes | 6 index derivative lot sizes, each with the circular that set it, the date it was announced, the date it took effect, and the date every live contract carried it. | Lot sizes |
| /data/market-holidays.json | nse-market-holidays | 18 holiday records for 2026, plus 1 published special session, 1 scheduled session whose timing is not published yet, and 1 settlement-date exception. | Holidays |
Where the facts come from, and when they change
Neither file is hand-written. Both are generated at build time from the same modules the lot sizes and market calendar pages render from, so the page and the feed cannot disagree: there is one copy of each fact, read twice.
Lot sizes
Every row is read off a primary exchange document, and 6 of 6 rows cite that document on the row itself. The current data version is 2026-09-04. A record whose last_verified_on is more than 30 days old is treated as stale and re-checked against the circular.
Market holidays
Sourced from NSE/CMTR/71775 (12 Dec 2025), with NSE/CMTR/72260 (12 Jan 2026) as the addendum. Last checked against the source on Mon, 22 Jun, 2026. The exchange publishes the calendar a year at a time, so coverage ends on Thu, 31 Dec, 2026 and there is nothing beyond it.
Cadence, honestly
There is no refresh schedule to quote. A file changes when its source document changes and a human re-verifies the affected rows against that document. The payloads carry no generated-at timestamp on purpose, so an unchanged file is byte-identical between publishes — which means you detect a real change by comparing data_version, last_verified_on or the bytes themselves, never by reading a clock the file does not contain.
The shared envelope
All three files open with the same block, so one parser can read any of them.
| Field | What it is | Value on /data/lot-sizes.json |
|---|---|---|
dataset | Stable machine identifier. Key your storage on this rather than on the filename. | index-derivative-lot-sizes |
schema_version | Integer version of the envelope and record shape. Check it, and fail loudly when it is not the value you built against. No future value is promised here — promising one would be a commitment this page has no way to keep. | 1 |
title, description | Human-readable name and one-line summary of the dataset. | NSE and BSE index derivative lot sizes |
publisher, publisher_url | Who mirrored the data. Not the primary source — that is named per record. | NiftyScanner — https://niftyscanner.in |
canonical_url | The one address this file is published at. Fetch it here rather than from a copy. | https://niftyscanner.in/data/lot-sizes.json |
documentation_url | The human page explaining the dataset, including caveats a JSON file cannot carry. | https://niftyscanner.in/lot-sizes/ |
contains_market_prices | An explicit assertion that the payload carries no exchange-licensed market data. It is false on every file here and is meant to stay that way. | false |
terms | The terms of use, carried inside the payload so they travel with the data. | Quoted in full below |
Each dataset then adds its own top-level rules — resolution_rule, coverage_rule and staleness_rule on the lot sizes; coverage_rule, trading_day_rule and coverage_ends_on on the holidays. Those strings are the specification. Read them before writing the parser.
Per-record provenance, and what source_level means
Every record in both datasets carries four provenance fields. They are the reason the files exist at all: a number without the document it came from is a rumour with a schema.
| Field | Meaning |
|---|---|
last_verified_on | The date a human last checked this record against its source document. Deliberately not the build date and not a publication date — either of those drifts away from the actual review the moment a rebuild happens without one. |
source_ref | The primary document reference as the exchange writes it — a circular or notice number with its date. This is what you cite. |
source_level | Either "row" or "dataset". See below; this is the honest part. |
source_url | A link to the document where one is available, null where it is not. Null on 1 of the 6 lot-size rows today: the reference is named, a stable link is not, and the field says so rather than pointing you at a search page. |
source_level: "row" — a precise citation
The document named in source_ref is the document that record came from. Follow the reference and you will find that specific date, or that specific lot size, inside it. All 6 lot-size records and 16 of the 18 holiday records are row-level, as is every special session, pending session and settlement exception.
source_level: "dataset" — an inherited citation
The record carries no reference of its own, so it is attributed to the dataset's primary circular instead. 2 holiday records sit at dataset level today. The payload says the same thing in sources.fallback_note: A record with source_level 'dataset' carries no circular reference of its own in the canonical calendar; it is attributed to primary_circular. A record with source_level 'row' cites the document named on that row.
The distinction exists so that an inherited citation is never presented as an exact one. Both kinds of record are equally verified; only one of them can be checked by opening the named document at the named line. If your product shows sources to its own users, carry the flag through — a citation the reader cannot resolve should not look identical to one they can.
The lot-size resolution rule — read this one twice
This is the single mistake a consumer of this file will actually make. An exchange publishes a revision weeks before it applies, so for the whole announcement-to-effect window the newest record in the file is the wrong answer — and it is wrong silently, because it is a plausible number attached to a real circular.
Worked example, run against the file as published today
Using NIFTY, whose row records a revision from 75 to 65, announced on Fri, 3 Oct, 2025 and effective from Tue, 28 Oct, 2025. The middle column applies the rule; the right-hand column shows what the shortcut would have said.
| Date asked about | Rule applied correctly | Newest row (the shortcut) |
|---|---|---|
| Mon, 27 Oct, 2025Announced, not yet in effect | No record — the file does not cover this dateNo record for this symbol has an effective_from on or before this date, so the file has no answer and a consumer must say so. The newest-row shortcut answers anyway — and answers wrong. | 65Answers confidently where the file has no answer at all. |
| Tue, 28 Oct, 2025In effect for new contracts | 65 — the record effective from 2025-10-28The revised lot now applies to newly introduced contracts. Contracts that already existed may still carry the superseded lot — read fully_effective_from before assuming otherwise. | 65Same answer here, by luck of timing. |
| Wed, 31 Dec, 2025Carried by every live contract | 65 — the record effective from 2025-10-28From this date the source states that every live contract carries the revised lot. | 65Same answer here, by luck of timing. |
| Fri, 4 Sept, 2026This page's build date | 65 — the record effective from 2025-10-28The same rule run for today. Same record — and it stays the answer until a newer row lands. | 65Same answer here, by luck of timing. |
Note what the first row does not do: it does not fall back to 75. The record tells you 65 superseded 75 through previous_lot_size, but that superseded lot has no effective_fromof its own in this file, so nothing here establishes the date it started applying. The correct output for that date is “not covered”, and the circular named on the row is where the answer actually lives.
Effective is not the same as everywhere
effective_from and fully_effective_from are two different dates and the gap between them is real. effective_from is when the revised lot applies to NEWLY INTRODUCED contracts. fully_effective_from is when EVERY live contract carries it. When full_coverage_known is false the source names no such single date and full coverage must not be assumed on any date.
The example row puts it in the circular's own terms: “The circular came into effect from 28 October 2025 EOD. Weekly and monthly contracts that already existed kept the old lot until the 30 December 2025 expiry, and existing quarterly and half-yearly contracts were revised at 30 December 2025 EOD — so every live contract carried the revised lot from 31 December 2025.”
How the files fail closed — null means “not published”
Where a source does not state something, these files say nothing rather than fill the gap. That produces nulls and false flags in places a consumer might not expect, and every one of them is deliberate.
A null full-coverage date
SENSEX ships fully_effective_from: null with full_coverage_known: false, because BSE Notice 20241021-13, 21 October 2024 names no single date on which every live contract carried the revised lot — it gives separate run-off rules and leaves it there. The pair of fields is what makes the gap readable by a machine: the boolean tells you the null is an absence of published fact, not an absence of restriction.
fully_effective_from: null means the exchange did not publish that date. It does not mean there was no transition window, and it does not license assuming full coverage on any date. Treat the null as an unanswered question and go to source_ref.A calendar that runs out
The holiday file covers 2026 and then stops. Only the years in years_covered are covered. For a date outside them this file has no answer — do not substitute a weekday rule, and do not treat an absent date as a trading day.
A session that exists without a timing
1 date is listed in pending_sessions with timing_published: false— the exchange has scheduled a session and has not yet notified its hours. They sit in their own list precisely so that “no session times” is never read as “the market is shut all day”. The payload spells the three-way distinction out in trading_day_rule: A date is a trading day when it is inside years_covered, is not a Saturday or Sunday, and does not appear as a trading_holiday — unless a special_sessions entry overrides it with trading_status 'open'. A pending_sessions date is NOT a normal trading day and also NOT a full closure: a session is expected and its timing has not been published.
Weekend rows that are not redundant
2 records carry type: "weekend_holiday" — a named Saturday or Sunday. The market is shut anyway, so the row exists for the name and for any note attached to it. Filter on type if you only want closures that remove a working day.
Fetching the files
Plain HTTPS GET, no headers required, application/json; charset=utf-8 back. Both examples below are complete and can be pasted as they are.
Shell
# 1. Discover what is published — and what deliberately is not.
curl -s https://niftyscanner.in/data/index.json
# 2. Resolve a lot size the way resolution_rule requires: the LATEST effective_from
# on or before your date, not the newest row in the file.
curl -s https://niftyscanner.in/data/lot-sizes.json | jq --arg d "2026-09-04" '
[ .records[] | select(.symbol == "NIFTY" and .effective_from <= $d) ]
| sort_by(.effective_from) | last
| { lot_size, effective_from, fully_effective_from, full_coverage_known, source_ref }
'
# 3. Check the calendar's coverage before asking it about a date.
curl -s https://niftyscanner.in/data/market-holidays.json | jq '{ years_covered, coverage_ends_on, last_verified_on }'JavaScript
// Fail loud at every step. A reference file that 404s or changes shape must stop
// your job, never quietly resolve to a default.
const response = await fetch("https://niftyscanner.in/data/lot-sizes.json");
if (!response.ok) throw new Error(`lot-sizes.json returned ${response.status}`);
const feed = await response.json();
if (feed.schema_version !== 1) {
throw new Error(`unexpected schema_version ${feed.schema_version}`);
}
function lotSizeOn(symbol, isoDate) {
const eligible = feed.records
.filter((row) => row.symbol === symbol && row.effective_from <= isoDate)
.sort((a, b) => a.effective_from.localeCompare(b.effective_from));
const row = eligible[eligible.length - 1];
// No record on or before the date means the file does not cover it. Say so.
if (!row) throw new Error(`no ${symbol} record effective on or before ${isoDate}`);
return row;
}
const row = lotSizeOn("NIFTY", "2026-09-04");
console.log(row.lot_size, row.source_ref, row.last_verified_on);
// full_coverage_known === false means the source never published the date on which
// every live contract carried this lot. It does NOT mean there is no restriction.
if (!row.full_coverage_known) {
console.warn(`${row.symbol}: full-coverage date not published — read ${row.source_ref}`);
}Cache the response. Nothing in these files changes between one minute and the next, and a job that re-reads them in a loop is doing no useful work.
Terms of use
Quoted verbatim from the terms field carried in all three payloads, so this page and the files can never say different things:
Static reference data mirrored from primary exchange documents by NiftyScanner. Free to read and cite; please cite the exchange circular named on the record as the primary source and verify it before relying on it. Educational reference only — nothing here is investment advice and no exchange-licensed market data is included.
Attribution
Cite the exchange, not this site, for the fact itself: the primary source is whatever source_ref names on the record you used, and source_urllinks it where a link exists. If you want to credit the mirror as well, link to the dataset's documentation_url — /lot-sizes or /holidays — rather than to the raw JSON, because the documentation page carries the caveats a payload can only compress into a sentence.
Carry last_verified_on through to your own users if you display these facts. The date a number was checked is worth more than the date it was downloaded.
What is not published, and why
The manifest names its own gaps in not_published, so absence cannot be mistaken for an oversight. Reproduced here from that field:
| Not published | Reason, as stated in the manifest |
|---|---|
| index and stock expiry calendar | No verified expiry data exists on this site. Expiry weekday assignments are set by their own circulars and were revised more than once during 2025, so publishing one means proving which circular is in force. Nothing here should be used to infer an expiry date. |
| single-stock F&O lot sizes | Roughly 200 symbols on their own revision cycle; not verified against primary documents here, so not published. |
| prices, quotes, index levels and volumes | Exchange-licensed market data. Redistribution is separately chargeable, so no price data is published in these datasets. |
The lot-size file says it again on its own terms
excludes_expiry_days is true in the payload, with the reason attached: Expiry weekday assignments are set by their own circulars and were revised more than once during 2025. No verified expiry data is published anywhere on this site, so none is published here. Do not infer an expiry day from this file.
On scope: Index derivatives only. Single-stock F&O lot sizes are NOT included: that list runs to roughly 200 symbols on its own revision cycle and is not verified here.
No prices, ever
There are no prices, quotes, index levels, volumes or open-interest figures in any of these files, and none is planned. Exchange market data is licensed and its redistribution is separately chargeable, which is why contains_market_prices: false is written into every envelope as an assertion rather than left as an inference. For live market data, license it from the exchange or a licensed vendor.
Questions developers ask
Is there an API key, a rate limit or a quota?
None is published, because none exists. These are static JSON files produced by the same build that produces every page on this site. There is no account to create, nothing to authenticate against, and no published quota. Please cache what you fetch rather than re-reading the files in a loop.
How often do the files change?
They change when a source document changes and a human re-verifies the affected rows against it, not on a fixed schedule. The payloads deliberately carry no generated-at timestamp, so a file that has not changed is byte-identical between publishes. Detect a real change by comparing data_version and last_verified_on, or the bytes themselves.
Why is there no expiry-day dataset?
Because no verified expiry data exists on this site to publish. Expiry weekday assignments are set by their own exchange circulars and were revised more than once during 2025, so publishing one means proving which circular is in force. Nothing in these files should be used to infer an expiry date.
Can I use these datasets in my own project?
Yes, on the terms carried inside every payload: they are free to read and cite, and you should cite the exchange circular named on the record as the primary source and verify it before relying on it. A link back to the documentation page for that dataset is appreciated.
Are these files a trading feed?
No. They carry static reference data only — every payload asserts contains_market_prices false. There are no prices, quotes, index levels or volumes, and there is no availability commitment of any kind. Do not put them on a code path that has to answer during market hours.
Related pages
Index Lot Sizes
The human view of /data/lot-sizes.json, with the transition notes each circular actually published.
Open →NSE Market Holidays
The human view of /data/market-holidays.json, including Muhurat and the next holiday.
Open →Is the Market Open Today?
The trading-day rule from the holiday dataset, resolved live for the current IST moment.
Open →Data Sources
Where the rest of the site's data comes from, and what each source does and does not cover.
Open →Glossary
Plain-English definitions of the NSE and F&O terms these payloads use.
Open →NiftyScanner provides general educational information and user-input mathematical utilities. It does not provide personalised investment advice, research recommendations, trade calls, price targets or suitability assessments. Market-calendar, settlement and transaction-cost information may change and should be verified against current official exchange, clearing, broker, depository and tax records. Trading and investing involve risk; you remain responsible for your own decisions and should seek appropriately qualified professional advice where needed.