Maintaining a live trading tool means staying on your toes, especially when the upstream data provider changes their backend. Recently, the maxpain.py module on Unofficed broke down. The culprit? The oi_chain_builder function was failing because its core dependency, nse_optionchain_scrapper(symbol), was trying to hit an obsolete NSE API endpoint.
While digging into the backend to fix this, I also discovered (and patched) an unrelated SEO hack stemming from an un-updated plugin backdoor. Here is a technical breakdown of how I resolved the NSE API changes and secured the site.
The Problem: The NSE API Overhaul
Historically, fetching option chain data from the NSE involved checking if a symbol was an index or an equity, and then routing the request accordingly:
The Old Way (Now Broken)
def nse_optionchain_scrapper(symbol):
symbol = nsesymbolpurify(symbol)
if any(x in symbol for x in indices):
payload = nsefetch('https://www.nseindia.com/api/option-chain-indices?symbol='+symbol)
else:
payload = nsefetch('https://www.nseindia.com/api/option-chain-equities?symbol='+symbol)
return payload
NSE has now unified these endpoints under a new NextApi structure. However, the new API endpoint for the option chain (getOptionChainData) requires a specific expiryDate parameter to function. We can’t just pull the whole board at once anymore; we have to know the exact expiry dates first.
Step 1: Fixing expiry_list()
To feed the new option chain scraper, I first had to update the expiry_list() function to hit the new dropdown API endpoint:
nseindia.com/api/NextApi/apiClient/GetQuoteApi?functionName=getOptionChainDropdown&symbol=SYMBOL
This endpoint returns a clean JSON response containing arrays for both expiryDates and strikePrice. Here is the updated function to parse the new structure while maintaining backward compatibility with the old expected output (either a Pandas DataFrame or a Python list):
def expiry_list(symbol, output_format=“list”):
symbol = nsesymbolpurify(symbol)
url = f"https://www.nseindia.com/api/NextApi/apiClient/GetQuoteApi?functionName=getOptionChainDropdown&symbol={symbol}"
# Fetch JSON using the existing nsefetch wrapper
payload = nsefetch(url)
expiries = payload.get("expiryDates", [])
if output_format == "list":
return expiries
else:
import pandas as pd
return pd.DataFrame({"Date": expiries})
Step 2: Refactoring nse_optionchain_scrapper()
With expiry_list() working again, I could refactor the main scraper. The new approach drops the clunky if/else logic for indices vs. equities. Instead, it dynamically fetches the available expiry dates and passes the nearest expiry (or a specified one) directly into the new endpoint.
Python
def nse_optionchain_scrapper(symbol, expiry_index=0):
symbol = nsesymbolpurify(symbol)
# 1. Fetch available expiries using our updated function
expiries = expiry_list(symbol, output_format="list")
if not expiries:
return None # Handle edge case where no data is returned
# 2. Select the target expiry (defaults to the current/nearest expiry)
target_expiry = expiries[expiry_index]
# 3. Hit the new unified API endpoint
url = f"https://www.nseindia.com/api/NextApi/apiClient/GetQuoteApi?functionName=getOptionChainData&symbol={symbol}¶ms=expiryDate={target_expiry}"
payload = nsefetch(url)
return payload
Side Quest: Plugging a Security Hole
While deploying these fixes, I noticed some anomalies in the site traffic and structure. A deep dive revealed that Unofficed had fallen victim to an SEO hack. An outdated plugin had a backdoor vulnerability, allowing malicious actors to inject hidden spam links into the site to siphon our domain authority.
I’ve since purged the malicious files, patched the backdoor, updated all core plugins, and hardened the site’s security headers to prevent a recurrence.