Options chain api data issue

I FREQUENTLY GET EMPTY OC DATA FOR ANY INSTRUMENTS OPTIONS CHAIN DATA. IS DATA API HAVING ANY PROBLEM. I USE BELOW PYTHON COMMAND FOR OC DATA.

option_chain_current = dhan.option_chain(
under_security_id=security_id,
under_exchange_segment=exchange_segment,
expiry=current_expiry
)
oc_current = option_chain_current.get(‘data’, {}).get(‘data’, {}).get(‘oc’, {})

IS THE PYTHON SCRIPT CORRECT OR DO YOU SUGGEST ANY CHANGE IN IT. I CALL THIS SCRIPT AT INTERVAL OF 10 SEC FOR DIFFERENT STOCKS. AND THEN WAIT FOR 5 MIN.

PLS CHECK AND VERIFY FROM YOUR SIDE.

@Tradehull_Imran

Hi @Shahid_Shahid ,

Do run the below commands in cmd terminal-

pip install dhanhq==2.2.0
pip install Dhan-Tradehull==3.3.0

Refer this code -

from Dhan_Tradehull import Tradehull

client_code = ""
tsl = Tradehull(client_code, mode="pin_totp", pin="", totp_secret="")

atm,df = tsl.get_option_chain(Underlying='NIFTY', exchange='INDEX', expiry=0, num_strikes=10)
print(atm)
print(df)

Ouptut -

Refer this link for the codebase -

Hi @Tradehull_Imran ,

I am also frequently getting same issue. Below is the screenshot.

Thanks in Advance

Hi @Khush_anand ,

Refer this code -

from Dhan_Tradehull import Tradehull
import time 
client_code = ""
tsl = Tradehull(client_code, mode="pin_totp", pin="", totp_secret="")
watchlist = ['360ONE','ABB','ABCAPITAL','ADANIENSOL','ADANIENT','ADANIGREEN','ADANIPORTS','ADANIPOWER','ALKEM','AMBER','AMBUJACEM','ANGELONE']

for i in watchlist:
    time.sleep(2.5)
    atm,df = tsl.get_option_chain(Underlying=i, exchange='NSE', expiry=0, num_strikes=10)
    print(atm)
    print(df)

Refer this example output photo -

Hi @Tradehull_Imran,

Thanks for looking into this issue

But I need to scan approx 220 stocks of FNO, if I give 2.5 sec sleep time the total time to scan all FNO stocks will be more than 9 mins(220*2.5 sec) which is too much to just scan one round of all FNO stocks.

Can we please make it fast?

Regards

Hi @Khush_anand
Currently option chain has a rate limit of 1 request every 3 seconds.
can you let me know what exactly do you want to scan after we get option chain..
so we can see if any speed optimization is possible

Hi @Tradehull_Imran ,

I belive rate limit for option chain has been modified. Below the link. Please have a look and let me know incase my understanding is wrong.

Thanks

Hi @Tradehull_Imran ,

By any chance did you look into my issue?

Thanks & Regards

Hi @Khush_anand
yes the observation is right, the rate limits have been modified.
so mostly now we not need a sleep of 3 sec

Hi @Tradehull_Imran ,

If I do not give 3 sec sleep, I am getting same error whose screenshot is already attached.

Can you please help me?

Thanks & Regards

Hi @Khush_anand
we are checking on it

1 Like

Hi @Tradehull_Imran ,

Could you please let me know ETA for this issue?

It will help me in planning things at my end.

Thanks & Regards

Hi @Khush_anand
we have checked it again, its like we can send only 1 option chain api call every 3 seconds.

Hi @Tradehull_Imran ,

Thanks a lot for looking into this. Let me re confirm with @Hardik & @Dhan .

My understanding is we can send more then 1 option chain request within 3 sec as long as it is unique.

1 Like

@Tradehull_Imran
i m facing issue in Options Data Pulling

import pkg_resources

print(“dhanhq version:”)

print(pkg_resources.get_distribution(“dhanhq”).version)

print(“\nDhan-Tradehull version:”)

print(pkg_resources.get_distribution(“Dhan-Tradehull”).version)

dhanhq version:
2.2.0

Dhan-Tradehull version:
3.2.2

from Dhan_Tradehull import Tradehull

client_code = “”

token_id = “”

tsl = Tradehull(client_code, token_id, mode=“access_token”)

print(tsl)

data = tsl.get_quote_data(‘SBIN’)

print(data)

CE_symbol_name, PE_symbol_name, strike = tsl.ATM_Strike_Selection(Underlying=‘ITC’, Expiry=0)

print(CE_symbol_name)

option_chain = tsl.get_option_chain(Underlying=“NIFTY”, exchange=“INDEX”, expiry=0, num_strikes=10)

print(option_chain)

option_chain1 = tsl.get_option_chain(Underlying=“ITC”, exchange=“NFO”, expiry=0, num_strikes=10)

print(option_chain1)

Attempting authentication using ACCESS TOKEN.
reading existing file all_instrument 2026-05-29.csv
Already logged in for today, so reusing the token
Instrument file retrieved successfully
-----SUCCESSFULLY LOGGED INTO DHAN-----
<Dhan_Tradehull.Dhan_Tradehull.Tradehull object at 0x000001C20971E650>
Exception at calling Quote as {'status': 'failure', 'remarks': {'error_code': None, 'error_type': None, 'error_message': None}, 'data': ''}
{}
Exception at getting Expiry list as {'status': 'failure', 'remarks': {'error_code': None, 'error_type': None, 'error_message': None}, 'data': ''}
Unable to find the correct Expiry for ITC

please suggest i have tried with Upgrading the pip for Tradehull and dhanhq but still issue doesnt get resolved i had a code running till 25th may where i was pulling all option chain for expiry 0 and 1 for nifty 50 stock For Positional option selling , but now the data is not coming and the above is the error.

Hi @Ram_Trade ,

There are rate limits in place. For LTP, only one request can be made per second, and fetching Option Chain data requires a minimum interval of 2.5 seconds. Therefore, time.sleep() must be used to fetch the data successfully and avoid rate limit errors.

The codebase has been updated. Please run the following command in the Command Prompt (CMD) terminal.

Refer the command -

pip install dhanhq==2.2.0
pip install Dhan-Tradehull==3.3.0

Refer the code -

import time
client_code = ""
token_id = ""
tsl = Tradehull(client_code, token_id, mode="access_token")
print(tsl)

data = tsl.get_quote_data('SBIN')
print(data)

time.sleep(1)
CE_symbol_name, PE_symbol_name, strike = tsl.ATM_Strike_Selection(Underlying='ITC', Expiry=0)
print(CE_symbol_name)

time.sleep(1)
option_chain = tsl.get_option_chain(Underlying="NIFTY", exchange="INDEX", expiry=0, num_strikes=10)
print(option_chain)

time.sleep(2.5)

option_chain1 = tsl.get_option_chain(Underlying="ITC", exchange="NFO", expiry=0, num_strikes=10)
print(option_chain1)

Output -


1 Like

Hey @Tradehull_Imran , thanks for the prompt reply really appreciate it .
however this code is running properly, but my inital code for Pulling option chain data of Nifty 50 symbol is now giving error. Even after having 5 second delay between each symbol being pulled .

STOCK_SYMBOLS = [
“HDFCBANK”, “ICICIBANK”, “KOTAKBANK”, “AXISBANK”,
“RELIANCE”, “ITC”, “INFY”, “TCS”, “HCLTECH”, “BHARTIARTL”,
“ADANIENT”, “ASIANPAINT”, “JSWSTEEL”, “CIPLA”, “MARUTI”,
“BAJFINANCE”, “HINDUNILVR”, “TECHM”, “VEDL”, “SBIN”
]

INDEX_SYMBOLS = [
{“name”: “NIFTY”, “exchange”: “INDEX”},
{“name”: “BANKNIFTY”, “exchange”: “INDEX”},
{“name”: “SENSEX”, “exchange”: “INDEX”},
]

── Config ────────────────────────────────────────────────────────────────────

BASE_FOLDER = r"C:\Users\ravi_\Options Data"
os.makedirs(BASE_FOLDER, exist_ok=True)

CE_COLS = [“Strike Price”, “CE LTP”, “CE OI”, “CE Chg in OI”, “CE Volume”, “CE IV”, “CE Vega”, “CE Gamma”, “CE Delta”, “CE Theta”]
PE_COLS = [“Strike Price”, “PE LTP”, “PE OI”, “PE Chg in OI”, “PE Volume”, “PE IV”, “PE Vega”, “PE Gamma”, “PE Delta”, “PE Theta”]

today = pd.Timestamp.now().strftime(“%d-%b-%Y”)

── Core fetch ────────────────────────────────────────────────────────────────

def fetch_and_save(name, exchange, expiry, suffix, num_strikes):
try:
spot, df = tsl.get_option_chain(Underlying=name, exchange=exchange, expiry=expiry, num_strikes=num_strikes)
merged = df[CE_COLS].merge(df[PE_COLS], on=“Strike Price”)
merged[“Spot”] = spot
merged[“Date”] = today

    # For stocks also add spot from get_ltp_data for accuracy
    if exchange == "NSE":
        try:
            merged["Spot"] = tsl.get_ltp_data(names=[name])[name]
        except:
            pass

    filepath = os.path.join(BASE_FOLDER, f"{name}_{today}{suffix}.xlsx")
    # filepath = os.path.join(BASE_FOLDER, f"{name}_{today}{suffix}.csv")
    # merged.to_csv(filepath, index=False)
    merged.to_excel(filepath, index=False)
    print(f"  ✅ {name} (expiry={expiry}) → {filepath}")
except Exception as e:
    print(f"  ❌ {name} (expiry={expiry}) failed: {e}")

── Run ───────────────────────────────────────────────────────────────────────

print(f"\n{‘=’*60}“)
print(f” OPTIONS DATA FETCHER — {today}“)
print(f” Stocks: {len(STOCK_SYMBOLS)} | Indices: {len(INDEX_SYMBOLS)}“)
print(f”{‘=’*60}\n")

Stocks

print(“[ STOCKS ]”)
for i, name in enumerate(STOCK_SYMBOLS, 1):
print(f"[{i}/{len(STOCK_SYMBOLS)}] {name}")
for expiry, suffix in [(0, “”), (1, “A”)]:
fetch_and_save(name, “NSE”, expiry, suffix, num_strikes=15)
time.sleep(10)
print()

Indices

print(“[ INDICES ]”)
for sym in INDEX_SYMBOLS:
print(f"{sym[‘name’]}")
for expiry, suffix in [(0, “”), (1, “A”)]:
fetch_and_save(sym[“name”], sym[“exchange”], expiry, suffix, num_strikes=10)
time.sleep(2)
print()

total = (len(STOCK_SYMBOLS) + len(INDEX_SYMBOLS)) * 2
print(f"{‘=’*60}“)
print(f” DONE | Total files saved: {total} → {BASE_FOLDER}“)
print(f”{‘=’*60}")

OPTIONS DATA FETCHER — 01-Jun-2026
Stocks: 50 | Indices: 3

[ STOCKS ]
[1/20] HDFCBANK
Exception at calling ltp as {‘status’: ‘failure’, ‘remarks’: {‘error_code’: None, ‘error_type’: None, ‘error_message’: None}, ‘data’: ‘’}
:white_check_mark: HDFCBANK (expiry=0) → C:\Users\ravi_\Options Data\HDFCBANK_01-Jun-2026.xlsx
Exception at calling ltp as {‘status’: ‘failure’, ‘remarks’: {‘error_code’: None, ‘error_type’: None, ‘error_message’: None}, ‘data’: ‘’}
:white_check_mark: HDFCBANK (expiry=1) → C:\Users\ravi_\Options Data\HDFCBANK_01-Jun-2026A.xlsx

Hi @Ram_Trade ,

The rate limit is the issue . Therefore, time.sleep() must be used to fetch the data successfully and avoid rate limit errors. It should be placed before an API request.

Refer the updated code -

from Dhan_Tradehull import Tradehull
import time
import pandas as pd
import os
client_code = ""
token_id = ''
tsl = Tradehull(client_code, token_id, mode="access_token")

STOCK_SYMBOLS = [
    "HDFCBANK", "ICICIBANK", "KOTAKBANK", "AXISBANK",
    "RELIANCE", "ITC", "INFY", "TCS", "HCLTECH", "BHARTIARTL",
    "ADANIENT", "ASIANPAINT", "JSWSTEEL", "CIPLA", "MARUTI",
    "BAJFINANCE", "HINDUNILVR", "TECHM", "VEDL", "SBIN"
]

INDEX_SYMBOLS = [
    {"name": "NIFTY", "exchange": "INDEX"},
    {"name": "BANKNIFTY", "exchange": "INDEX"},
    {"name": "SENSEX", "exchange": "INDEX"},
]

# ── Config ────────────────────────────────────────────────────────────────────
BASE_FOLDER = r":\Users\ravi_\Options Data"
os.makedirs(BASE_FOLDER, exist_ok=True)

CE_COLS = [
    "Strike Price", "CE LTP", "CE OI", "CE Chg in OI",
    "CE Volume", "CE IV", "CE Vega", "CE Gamma",
    "CE Delta", "CE Theta"
]

PE_COLS = [
    "Strike Price", "PE LTP", "PE OI", "PE Chg in OI",
    "PE Volume", "PE IV", "PE Vega", "PE Gamma",
    "PE Delta", "PE Theta"
]

today = pd.Timestamp.now().strftime("%d-%b-%Y")

# ── Core fetch ────────────────────────────────────────────────────────────────
def fetch_and_save(name, exchange, expiry, suffix, num_strikes):
    try:
        time.sleep(2.5)
        spot, df = tsl.get_option_chain(
            Underlying=name,
            exchange=exchange,
            expiry=expiry,
            num_strikes=num_strikes
        )
        merged = df[CE_COLS].merge(df[PE_COLS], on="Strike Price")
        merged["Spot"] = spot
        merged["Date"] = today

        # For stocks also add spot from get_ltp_data for accuracy
        if exchange == "NSE":
            try:
                time.sleep(1)
                merged["Spot"] = tsl.get_ltp_data(names=[name])[name]
            except:
                pass

        filepath = os.path.join(
            BASE_FOLDER,
            f"{name}_{today}{suffix}.xlsx"
        )

        # filepath = os.path.join(BASE_FOLDER, f"{name}_{today}{suffix}.csv")
        # merged.to_csv(filepath, index=False)

        merged.to_excel(filepath, index=False)

        print(f"  ✅ {name} (expiry={expiry}) → {filepath}")

    except Exception as e:
        print(f"  ❌ {name} (expiry={expiry}) failed: {e}")

# ── Run ───────────────────────────────────────────────────────────────────────
print(f"\n{'='*60}")
print(f" OPTIONS DATA FETCHER — {today}")
print(f" Stocks: {len(STOCK_SYMBOLS)} | Indices: {len(INDEX_SYMBOLS)}")
print(f"{'='*60}\n")

# Stocks
print("[ STOCKS ]")

for i, name in enumerate(STOCK_SYMBOLS, 1):
    print(f"[{i}/{len(STOCK_SYMBOLS)}] {name}")

    for expiry, suffix in [(0, ""), (1, "A")]:
        fetch_and_save(
            name,
            "NSE",
            expiry,
            suffix,
            num_strikes=15
        )

        # time.sleep(10)

    print()

# Indices
print("[ INDICES ]")

for sym in INDEX_SYMBOLS:
    print(f"{sym['name']}")

    for expiry, suffix in [(0, ""), (1, "A")]:
        fetch_and_save(
            sym["name"],
            sym["exchange"],
            expiry,
            suffix,
            num_strikes=10
        )

        # time.sleep(2)

    print()

total = (len(STOCK_SYMBOLS) + len(INDEX_SYMBOLS)) * 2

print(f"{'='*60}")
print(f" DONE | Total files saved: {total} → {BASE_FOLDER}")
print(f"{'='*60}")

Output -

Gentle reminder on the below request.

Thanks a lot Tradehull for looking into this. Let me re-confirm with @Hardik and @Dhan

My understanding is that we can send more than one option chain request within a 3-second window, provided that each request is unique. Could you please confirm if this is correct?

Gentle reminder 2 on the below request.

Thanks a lot Tradehull for looking into this. Let me re-confirm with @Hardik and @Dhan

My understanding is that we can send more than one option chain request within a 3-second window, provided that each request is unique. Could you please confirm if this is correct?