Skip to main content

Overview

The Price Bands API retrieves price band data from NSE archives in two modes: incremental changes (daily price band updates) and complete security lists (full market price bands). Both endpoints use CSV files with automatic date-based discovery to find the latest available data. Source Files:
  • fetch_incremental_price_bands.py
  • fetch_complete_price_bands.py

Endpoint Details

Incremental Price Bands

URL
string
required
https://nsearchives.nseindia.com/content/equities/eq_band_changes_{date}.csv
Method
string
required
GET
Date Format
string
required
ddmmyyyy (e.g., 15012024 for January 15, 2024)

Complete Price Bands

URL
string
required
https://nsearchives.nseindia.com/content/equities/sec_list_{date}.csv
Method
string
required
GET
Date Format
string
required
ddmmyyyy (e.g., 15012024 for January 15, 2024)

Request Headers

{
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
  "Accept": "*/*"
}

Example Requests

Incremental Price Bands

curl -X GET "https://nsearchives.nseindia.com/content/equities/eq_band_changes_15012024.csv" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  -H "Accept: */*"

Complete Security List

curl -X GET "https://nsearchives.nseindia.com/content/equities/sec_list_15012024.csv" \
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  -H "Accept: */*"

Response Format

Both endpoints return CSV files that are parsed and converted to JSON.

Incremental Price Bands CSV Structure

SYMBOL,SERIES,CHANGE_DATE,OLD_PRICE_BAND,NEW_PRICE_BAND,REMARKS
EXAMPLE,EQ,15-JAN-2024,20,10,Price band revised
ANOTHER,EQ,15-JAN-2024,10,5,ASM Stage 1

Complete Security List CSV Structure

SYMBOL,NAME_OF_COMPANY,SERIES,DATE_OF_LISTING,PAID_UP_VALUE,MARKET_LOT,ISIN_NUMBER,FACE_VALUE
RELIANCE,Reliance Industries Ltd.,EQ,29-NOV-1977,10,1,INE002A01018,10
TCS,Tata Consultancy Services Ltd.,EQ,25-AUG-2004,1,1,INE467B01029,1

Processed Response

Incremental Price Bands JSON

SYMBOL
string
Trading symbol
SERIES
string
Series (typically “EQ” for equities)
CHANGE_DATE
string
Date of price band change
OLD_PRICE_BAND
string
Previous price band percentage
NEW_PRICE_BAND
string
New price band percentage
REMARKS
string
Reason for change (e.g., “ASM Stage 1”, “Price band revised”)

Complete Security List JSON

SYMBOL
string
Trading symbol
NAME_OF_COMPANY
string
Company name
SERIES
string
Trading series
DATE_OF_LISTING
string
Listing date
PAID_UP_VALUE
string
Paid-up value
MARKET_LOT
string
Market lot size
ISIN_NUMBER
string
ISIN code
FACE_VALUE
string
Face value per share

Example JSON Outputs

incremental_price_bands.json

[
  {
    "SYMBOL": "EXAMPLE",
    "SERIES": "EQ",
    "CHANGE_DATE": "15-JAN-2024",
    "OLD_PRICE_BAND": "20",
    "NEW_PRICE_BAND": "10",
    "REMARKS": "Price band revised"
  },
  {
    "SYMBOL": "ANOTHER",
    "SERIES": "EQ",
    "CHANGE_DATE": "15-JAN-2024",
    "OLD_PRICE_BAND": "10",
    "NEW_PRICE_BAND": "5",
    "REMARKS": "ASM Stage 1"
  }
]

complete_price_bands.json

[
  {
    "SYMBOL": "RELIANCE",
    "NAME_OF_COMPANY": "Reliance Industries Ltd.",
    "SERIES": "EQ",
    "DATE_OF_LISTING": "29-NOV-1977",
    "PAID_UP_VALUE": "10",
    "MARKET_LOT": "1",
    "ISIN_NUMBER": "INE002A01018",
    "FACE_VALUE": "10"
  },
  {
    "SYMBOL": "TCS",
    "NAME_OF_COMPANY": "Tata Consultancy Services Ltd.",
    "SERIES": "EQ",
    "DATE_OF_LISTING": "25-AUG-2004",
    "PAID_UP_VALUE": "1",
    "MARKET_LOT": "1",
    "ISIN_NUMBER": "INE467B01029",
    "FACE_VALUE": "1"
  }
]

Implementation Details

Date Discovery Strategy

Both scripts use backwards date discovery (up to 7 days) to find the latest available file:
import requests
import pandas as pd
import json
from datetime import datetime, timedelta
import io

def fetch_nse_price_bands():
    base_url = "https://nsearchives.nseindia.com/content/equities/eq_band_changes_{date}.csv"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Accept": "*/*"
    }
    
    today = datetime.now()
    clean_data = []
    
    # Check last 8 days (today + 7 days back)
    for i in range(8):
        check_date = today - timedelta(days=i)
        date_str = check_date.strftime("%d%m%Y")  # Format: ddmmyyyy
        url = base_url.format(date=date_str)
        
        print(f"Checking for price band changes on {date_str}...")
        try:
            response = requests.get(url, headers=headers, timeout=10)
            
            if response.status_code == 200:
                print(f"  Found data for {date_str}!")
                
                # Parse CSV using pandas
                csv_content = response.content.decode('utf-8')
                df = pd.read_csv(io.StringIO(csv_content))
                
                # Convert to list of dictionaries
                raw_data = df.to_dict(orient='records')
                
                # Clean whitespace from keys and values
                for record in raw_data:
                    cleaned_record = {}
                    for k, v in record.items():
                        key = k.strip() if isinstance(k, str) else k
                        value = v.strip() if isinstance(v, str) else v
                        cleaned_record[key] = value
                    clean_data.append(cleaned_record)
                
                break  # Stop after finding latest file
                
            elif response.status_code == 404:
                print(f"  No file found for {date_str} (404).")
            else:
                print(f"  Unexpected status {response.status_code} for {date_str}.")
        
        except Exception as e:
            print(f"  Error checking {date_str}: {e}")
    
    if clean_data:
        output_file = "incremental_price_bands.json"
        with open(output_file, "w") as f:
            json.dump(clean_data, f, indent=4)
        print(f"Successfully saved {len(clean_data)} price band changes to {output_file}")
    else:
        print("Could not find any price band files in the last 7 days.")

CSV Parsing with Pandas

import pandas as pd
import io

# Decode CSV content
csv_content = response.content.decode('utf-8')

# Use pandas for robust parsing
df = pd.read_csv(io.StringIO(csv_content))

# Convert to list of dictionaries
raw_data = df.to_dict(orient='records')

# Clean whitespace
for record in raw_data:
    cleaned_record = {}
    for k, v in record.items():
        key = k.strip() if isinstance(k, str) else k
        value = v.strip() if isinstance(v, str) else v
        cleaned_record[key] = value
    clean_data.append(cleaned_record)

Configuration

Incremental Price Bands

Lookback Period
integer
default:"7"
Days to search backwards for latest file
Output File
string
incremental_price_bands.json

Complete Price Bands

Lookback Period
integer
default:"7"
Days to search backwards for latest file
Output File
string
complete_price_bands.json

Use Cases

Incremental Price Bands

  1. Surveillance Alerts: Track stocks moving to stricter price bands
  2. ASM/GSM Monitoring: Identify stocks entering surveillance
  3. Risk Management: Flag portfolio stocks with band changes
  4. Daily Updates: Monitor price band revisions

Complete Security List

  1. Master Data: Build comprehensive security master file
  2. ISIN Mapping: Cross-reference symbols with ISINs
  3. Listing Data: Track listing dates and face values
  4. Market Lot Info: Determine minimum trading quantities
  5. Data Validation: Verify security details against official NSE data

Price Band Categories

20%
string
Standard stocks (normal volatility)
10%
string
Moderate surveillance or small caps
5%
string
High surveillance (ASM/GSM Stage 1-2)
2%
string
Strictest surveillance (ASM/GSM Stage 3+)
No Band
string
Newly listed stocks (first day) - no upper circuit

Performance Metrics

Incremental Price Bands

  • Typical Changes: 5-20 stocks per day
  • File Availability: 95%+ (weekdays)
  • Fetch Time: 2-5 seconds
  • Lookback Success: 99%+ (finds file within 7 days)

Complete Security List

  • Total Securities: ~2,800-3,000
  • File Size: ~200-300 KB (CSV)
  • JSON Size: ~500-700 KB
  • Fetch Time: 3-7 seconds
  • Lookback Success: 99%+ (finds file within 7 days)

Error Handling

File Not Found

if response.status_code == 404:
    print(f"  No file found for {date_str} (404).")
    # Continue to next date

CSV Parsing Error

try:
    df = pd.read_csv(io.StringIO(csv_content))
    raw_data = df.to_dict(orient='records')
except Exception as parse_error:
    print(f"  Error parsing CSV for {date_str}: {parse_error}")
    continue  # Try next date

No Data Found

if not clean_data:
    print("Could not find any price band files in the last 7 days.")
    # No output file created

Analysis Examples

Stocks with Tighter Bands

# Incremental: Find stocks moving to stricter bands
tighter_bands = [
    change for change in incremental_data
    if int(change["NEW_PRICE_BAND"]) < int(change["OLD_PRICE_BAND"])
]

ASM Additions

# Incremental: Find new ASM stocks
asm_additions = [
    change for change in incremental_data
    if "ASM" in change.get("REMARKS", "")
]

Complete List Filtering

# Complete: Filter by series
eq_series = [
    stock for stock in complete_data
    if stock["SERIES"] == "EQ"
]

# Complete: Filter by face value
low_fv = [
    stock for stock in complete_data
    if float(stock["FACE_VALUE"]) <= 2
]

File Availability

Weekdays

  • Files typically published after market close (3:30 PM IST)
  • Available by 6:00 PM IST same day

Weekends/Holidays

  • No new files published
  • Use lookback strategy to find last trading day’s file

Special Cases

  • Market holidays: No files
  • Technical issues: File may be delayed or missing
  • Lookback of 7 days ensures high success rate

Integration with Other APIs

# Cross-reference with master ISIN map
with open("master_isin_map.json", "r") as f:
    master_map = json.load(f)

with open("incremental_price_bands.json", "r") as f:
    price_bands = json.load(f)

# Find symbols with price band changes
changed_symbols = {pb["SYMBOL"] for pb in price_bands}

# Get full details from master map
affected_stocks = [
    stock for stock in master_map
    if stock["Symbol"] in changed_symbols
]

Notes

  • NSE updates incremental file only when there are changes (may not exist daily)
  • Complete security list is published daily (more reliable)
  • CSV encoding is UTF-8
  • Column names may have leading/trailing whitespace - script cleans this
  • Date format in CSV data is DD-MMM-YYYY (e.g., “15-JAN-2024”)
  • URL date format is DDMMYYYY (e.g., “15012024”)
  • Pandas is required for robust CSV parsing
  • Files are archived and may not be available beyond 1-2 months
  • For historical data, use incremental files to track changes over time
  • Complete list provides snapshot of current price bands for all securities

Build docs developers (and LLMs) love