Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Anny26022/chartsmaze_clone/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Surveillance Lists API retrieves NSE’s Additional Surveillance Measure (ASM) and GSM (Graded Surveillance Measure) lists. This endpoint uses a triple-fallback strategy: Google Sheets Gviz API (primary), Dhan Next.js API (secondary), and web scraping (tertiary) to ensure maximum reliability. Source File: fetch_surveillance_lists.py

Endpoint Details

Primary Source: Google Sheets Gviz API

URL
string
required
https://docs.google.com/spreadsheets/d/{SHEET_ID}/gviz/tq?tqx=out:json&gid={GID}
Method
string
required
GET

Secondary Source: Dhan Next.js API

URL
string
required
https://dhan.co/_next/data/{BUILD_ID}/{PAGE_KEY}.json
Method
string
required
GET

Tertiary Source: Web Scraping

URL
string
required
https://dhan.co/{PAGE_URL}/
Method
string
required
GET (parse __NEXT_DATA__ script tag)

Configuration

ASM List

Spreadsheet GID
string
290894275
Web URL
string
https://dhan.co/nse-asm-list/
Data Key
string
nse-asm-list
Output File
string
nse_asm_list.json

GSM List

Spreadsheet GID
string
1525483995
Web URL
string
https://dhan.co/nse-gsm-list/
Data Key
string
nse-gsm-list
Output File
string
nse_gsm_list.json

Request Headers

{
  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}

Example Requests

Primary: Google Sheets Gviz

curl -X GET "https://docs.google.com/spreadsheets/d/1zqhM3geRNW_ZzEx62y0W5U2ZlaXxG-NDn0V8sJk5TQ4/gviz/tq?tqx=out:json&gid=290894275" \
  -H "User-Agent: Mozilla/5.0"

Secondary: Next.js API

# First get build ID
curl -X GET https://dhan.co/all-indices/ | grep buildId

# Then fetch data
curl -X GET "https://dhan.co/_next/data/{BUILD_ID}/nse-asm-list.json" \
  -H "User-Agent: Mozilla/5.0"

Tertiary: Web Scraping

curl -X GET https://dhan.co/nse-asm-list/ \
  -H "User-Agent: Mozilla/5.0"

Response Structure

List Object Fields

Symbol
string
Trading symbol
Name
string
Company name
ISIN
string
ISIN code
Stage
string
Surveillance stage (for ASM: “Stage 1”, “Stage 2”, etc.; for GSM: similar staging)

Example Response (Google Sheets Gviz)

{
  "table": {
    "rows": [
      {
        "c": [
          null,
          {"v": "YESBANK"},
          {"v": "Yes Bank Ltd."},
          {"v": "INE528G01035"},
          {"v": "Stage 1"}
        ]
      }
    ]
  }
}

Processed Output

[
  {
    "Symbol": "YESBANK",
    "Name": "Yes Bank Ltd.",
    "ISIN": "INE528G01035",
    "Stage": "Stage 1"
  },
  {
    "Symbol": "SUZLON",
    "Name": "Suzlon Energy Ltd.",
    "ISIN": "INE040H01021",
    "Stage": "Stage 2"
  }
]

Implementation Details

Triple-Fallback Strategy

import requests
import json
import re
from bs4 import BeautifulSoup

def fetch_surveillance_lists():
    spreadsheet_base_url = "https://docs.google.com/spreadsheets/d/1zqhM3geRNW_ZzEx62y0W5U2ZlaXxG-NDn0V8sJk5TQ4/gviz/tq?tqx=out:json&gid="
    
    lists_config = {
        "nse_asm_list.json": {
            "gid": "290894275",
            "web_url": "https://dhan.co/nse-asm-list/",
            "data_key": "nse-asm-list"
        },
        "nse_gsm_list.json": {
            "gid": "1525483995",
            "web_url": "https://dhan.co/nse-gsm-list/",
            "data_key": "nse-gsm-list"
        }
    }
    
    headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"}
    build_id = get_build_id()  # Fetch dynamic build ID
    
    for filename, config in lists_config.items():
        gid = config['gid']
        cleaned_list = []
        success = False
        
        # Attempt 1: Google Sheets Gviz API
        try:
            url = f"{spreadsheet_base_url}{gid}"
            response = requests.get(url, headers=headers, timeout=10)
            text = response.text
            match = re.search(r'setResponse\((.*)\);', text)
            if match:
                data = json.loads(match.group(1))
                rows = data.get('table', {}).get('rows', [])
                
                for row in rows:
                    c = row.get('c', [])
                    if len(c) >= 5:
                        symbol = c[1].get('v') if c[1] else None
                        name = c[2].get('v') if c[2] else None
                        isin = c[3].get('v') if c[3] else None
                        stage = c[4].get('v') if c[4] else None
                        
                        if symbol and symbol != "Symbol":
                            cleaned_list.append({
                                "Symbol": str(symbol),
                                "Name": str(name),
                                "ISIN": str(isin),
                                "Stage": str(stage)
                            })
                success = True
        except Exception as e:
            print(f"Gviz failed: {e}")
        
        # Attempt 2: Next.js API (if Gviz failed)
        if not success and build_id:
            try:
                url = f"https://dhan.co/_next/data/{build_id}/{config['data_key']}.json"
                response = requests.get(url, headers=headers)
                # Parse pageProps for list data
                success = True  # If data found
            except:
                pass
        
        # Attempt 3: Web scraping (if both failed)
        if not success:
            try:
                response = requests.get(config['web_url'], headers=headers)
                soup = BeautifulSoup(response.text, 'html.parser')
                script = soup.find('script', id='__NEXT_DATA__')
                if script:
                    data_json = json.loads(script.string)
                    # Extract list from props
                    success = True
            except:
                pass
        
        if success:
            with open(filename, "w") as f:
                json.dump(cleaned_list, f, indent=4)

Build ID Extraction

def get_build_id():
    """Dynamically fetch Next.js build ID"""
    url = "https://dhan.co/all-indices/"
    headers = {"User-Agent": "Mozilla/5.0"}
    
    try:
        response = requests.get(url, headers=headers, timeout=10)
        match = re.search(r'"buildId":"([^"]+)"', response.text)
        return match.group(1) if match else None
    except:
        return None

Output Files

nse_asm_list.json

[
  {
    "Symbol": "YESBANK",
    "Name": "Yes Bank Ltd.",
    "ISIN": "INE528G01035",
    "Stage": "Stage 1"
  },
  {
    "Symbol": "SUZLON",
    "Name": "Suzlon Energy Ltd.",
    "ISIN": "INE040H01021",
    "Stage": "Stage 2"
  }
]

nse_gsm_list.json

[
  {
    "Symbol": "EXAMPLE",
    "Name": "Example Company Ltd.",
    "ISIN": "INE123A01012",
    "Stage": "Stage 1"
  }
]

Surveillance Stages

ASM (Additional Surveillance Measure)

  • Stage 1: Initial surveillance - price volatility, volume spikes
  • Stage 2: Enhanced surveillance - continued concerns
  • Stage 3: Strictest surveillance - severe concerns
  • Long Term: Long-term ASM list
  • Short Term: Short-term ASM list

GSM (Graded Surveillance Measure)

  • Stage 1: Initial graded surveillance
  • Stage 2: Enhanced graded surveillance
  • Stage 3+: Higher surveillance stages

Use Cases

  1. Risk Screening: Filter out high-risk surveillance stocks
  2. Compliance Checking: Ensure portfolio doesn’t include restricted stocks
  3. Intraday Trading: ASM stocks have stricter intraday limits
  4. Position Limits: GSM stocks have position size restrictions
  5. Circuit Limits: Surveillance stocks often have tighter circuit filters

Performance Metrics

  • ASM List Size: 50-150 stocks (varies)
  • GSM List Size: 20-80 stocks (varies)
  • Fetch Time: 2-5 seconds per list
  • Success Rate: >99% (triple fallback)
  • Update Frequency: NSE updates weekly/as needed

Trading Restrictions

Stocks in surveillance lists face:
  • Reduced Circuit Limits: 5% or 2% instead of 10%/20%
  • Trade-for-Trade (T2T): No intraday allowed
  • 100% Margin: Full upfront payment required
  • Position Limits: Maximum holding restrictions
  • Auction Settlement: Delivery obligations strictly enforced

Error Handling

Primary Source Failure

try:
    # Google Sheets Gviz
    response = requests.get(gviz_url, timeout=10)
    # Process...
except Exception as e:
    print(f"Gviz failed: {e}")
    # Fall back to secondary source

All Sources Failed

if not success:
    print(f"Critical failure: Could not fetch {filename} from any source.")
    # No file written

Data Validation

# Skip header rows
if symbol == "Symbol" or not symbol:
    continue

# Ensure all fields are strings
cleaned_list.append({
    "Symbol": str(symbol),
    "Name": str(name),
    "ISIN": str(isin),
    "Stage": str(stage)
})

Integration Example

# Load surveillance lists
with open("nse_asm_list.json", "r") as f:
    asm_list = json.load(f)

with open("nse_gsm_list.json", "r") as f:
    gsm_list = json.load(f)

# Create lookup sets
asm_symbols = {stock["Symbol"] for stock in asm_list}
gsm_symbols = {stock["Symbol"] for stock in gsm_list}

# Filter master list
safe_stocks = [
    stock for stock in master_list
    if stock["Symbol"] not in asm_symbols and stock["Symbol"] not in gsm_symbols
]

Notes

  • Surveillance lists change frequently - run daily for accuracy
  • Google Sheets is the most reliable source (99%+ uptime)
  • Build ID changes with Next.js deployments - extracted dynamically
  • Web scraping is slowest but most resilient fallback
  • Stage progression: stocks can move up/down or be removed
  • Exit from surveillance: stocks meeting criteria for 6+ months may be removed
  • ASM is more common than GSM
  • Both lists are maintained by NSE and updated on exchange website

Build docs developers (and LLMs) love