I haven’t explored Indian market broker but Indian broker in Crypto. They already have IP security in place and good thing is they allow to replace IP whenever needed.
I believe IP restrictions doesn’t apply on Sandbox, right?
I haven’t explored Indian market broker but Indian broker in Crypto. They already have IP security in place and good thing is they allow to replace IP whenever needed.
I believe IP restrictions doesn’t apply on Sandbox, right?
What I understood is, API Key authentication through TOTP (Mail/Mobile) is once generated Access token is remain same (no need to generate everyday). So we no need to stop the program everyday to replace Access token.
Also provide Video how to do this Dhan Tradehull framework .
What is this Redirection URL ?
Can family members use same static IP? I have seen some other brokers mentioning about it.
Hello @Hardik
If we are using the same EC2 instance for multiple scripts, then assigning the same EC2 IP address to two different Dhan accounts does not work. The system shows an error stating that the IP is already in use.
This means that for running two different scripts on separate Dhan accounts, we would need to use two different EC2 instances. Managing multiple EC2 instances is quite inconvenient.
Additionally, the token generation Step 2 cannot be automated via code, which is also disappointing.
SEBI should call the circular ‘Guidelines on how to babysit retail investors’ instead of ‘Safer participation of retail investors in Algorithmic trading’
Yes, IP restrictions don’t apply on sandbox.
@Faisal_E Welcome to Dhan Community!
API key authentication needs to be automated, using which new access token can be generated every day. Will be adding this on Dhan Tradehull framework as well.
@stanly_thomas yes, while this has been in discussion and the SEBI circular outlines it as well, there are doubts on it’s implementation. We will be bringing this soon.
@Galbabhai_Kuva If you are using same EC2 instance for multiple scrips, then it should be fine. However, if you are using same EC2 for multiple accounts, that will create an issue.
On the Step 2 automation, we are working on allowing the same.
@Hardik What i understand from this: While step 1 and step 3 are API based, we will have to login everyday manually to complete step 2 in order to retrieve the access token for the APIs.
Doesn’t it defeat the purpose of automating the whole trading flow (to some extent)?
Let me know in case i have not understood the article correctly.
Hello @Hardik,
The steps you shared are fine, but please automate Step 2 as soon as possible.
I am also facing a separate issue when trying to associate a second static IP address.
When I attempt to add the new Elastic IP, I receive the error shown in the first screenshot.
This IP was newly generated from the Elastic IP section in EC2, but it still cannot be attached.
Note:
For a true duplicate IP we see a different error, which is shown in the second screenshot.
Please treat this as a separate scenario and investigate accordingly.
The deadline for resolving this is 1 October, so please prioritize it.
Thank you.
I am able to set my secondary IP address, but I’m getting an “Oops! Something went wrong!” error while setting the primary IP.
Kindly fix this issue on priority.
anyone automated the entire authentication using python kindly help with the code.
my view on static api… Imagine a grand marketplace with one main gate where the guard says, “No more than 10 knocks a second!”
Big elephants inside smile — they simply build many small doors to get around it.
Meanwhile, small traders are told: “Bring a special fixed key (Static IP) if you want entry.”
The broadband pipes groan as everyone is pushed to use more and more connections.
Rules meant for fairness start looking like puzzles only giants can solve.
The little cat in the corner whispers: “Strange how gates are set… fairness should feel simple, not like a maze.”
You can use this sample code. It works as this
dhan_token.json) exists and is valid for at least 7 more hours; if yes, it reuses it./generate-consent API to get a consentAppId.https://auth.dhan.co/login/consentApp-login?...) which you must open in your browser and log in with your Dhan account + 2FA.http://localhost:3000) with a tokenId in the query - you copy this tokenId and paste it into the script.tokenId for a valid accessToken + expiry time, saves them in dhan_token.json, and returns them for your API calls.import json
import logging
from datetime import datetime, timedelta, timezone
from pathlib import Path
import requests
from typing import Optional, Tuple
########################################################################
# ----------------------
# Config & Constants
# ----------------------
API_KEY = ""
API_SECRET = ""
DHAN_CLIENT_ID = ''
AUTH_BASE = "https://auth.dhan.co"
REDIRECT_URL = "http://localhost:3000" # whatever you configured when generating the API key/secret
API_BASE = "https://api.dhan.co"
# Save token JSON alongside this script
TOKEN_FILE: Path = Path(__file__).resolve().parent / "dhan_token.json"
# How much validity time (buffer) we require to reuse the token
REUSE_BUFFER = timedelta(hours=7)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
#########################################################################
# ----------------------
# Helpers: Token IO
# ----------------------
def _parse_expiry(expiry_str: str) -> datetime:
"""
Parse expiry returned by API. Dhan usually returns ISO like '2025-09-23T12:37:23'.
Treat it as UTC if tzinfo is missing.
"""
dt = datetime.fromisoformat(expiry_str)
if dt.tzinfo is None:
# Assume UTC if tz not provided
dt = dt.replace(tzinfo=timezone.utc)
return dt
def _load_existing_token() -> Optional[Tuple[str, datetime]]:
"""
Returns (access_token, expiry_dt) if file exists and is valid JSON with one key.
Otherwise returns None.
"""
if not TOKEN_FILE.exists():
logging.info("Token file not found at %s", TOKEN_FILE)
return None
try:
with TOKEN_FILE.open("r", encoding="utf-8") as f:
data = json.load(f)
if not isinstance(data, dict) or len(data) != 1:
logging.warning("Token file format invalid; expected single {expiry: token} entry.")
return None
expiry_str, access_token = next(iter(data.items()))
if not isinstance(expiry_str, str) or not isinstance(access_token, str):
logging.warning("Token file content types invalid.")
return None
expiry_dt = _parse_expiry(expiry_str)
return access_token, expiry_dt
except Exception as e:
logging.error("Failed to read/parse token file: %s", e)
return None
def _save_token(expiry_str: str, access_token: str) -> None:
"""
Save token as { "expiry": "token" } overwriting any previous content.
"""
try:
with TOKEN_FILE.open("w", encoding="utf-8") as f:
json.dump({expiry_str: access_token}, f, indent=2)
logging.info("Token saved to %s (expires at %s).", TOKEN_FILE, expiry_str)
except Exception as e:
logging.error("Failed to save token: %s", e)
# ----------------------
# Dhan Auth API Calls
# ----------------------
def _generate_consent() -> str:
"""
Step 1: POST /app/generate-consent?client_id={dhanClientId}
Headers: app_id, app_secret
Returns consentAppId
"""
url = f"{AUTH_BASE}/app/generate-consent"
headers = {"app_id": API_KEY, "app_secret": API_SECRET}
params = {"client_id": DHAN_CLIENT_ID}
logging.info("Requesting consent for client_id %s ...", DHAN_CLIENT_ID)
resp = requests.post(url, headers=headers, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
consent_app_id = data["consentAppId"]
logging.info("Consent generated. consentAppId=%s", consent_app_id)
return consent_app_id
def _prompt_user_for_token_id(consent_app_id: str) -> str:
"""
Step 2 (user in browser): Open login URL, complete login/2FA.
Copy tokenId from your redirect URL and paste here.
"""
login_url = f"{AUTH_BASE}/login/consentApp-login?consentAppId={consent_app_id}"
logging.info("Open this URL in your browser and complete login/2FA:")
logging.info(login_url)
token_id = input("Paste tokenId from your redirect URL here: ").strip()
if not token_id:
raise ValueError("Empty tokenId provided.")
logging.info("tokenId received.")
return token_id
def _consume_consent(token_id: str) -> Tuple[str, str]:
"""
Step 3: POST /app/consumeApp-consent?tokenId={tokenId}
Headers: app_id, app_secret
Returns (accessToken, expiryTimeStr)
"""
url = f"{AUTH_BASE}/app/consumeApp-consent"
headers = {"app_id": API_KEY, "app_secret": API_SECRET}
params = {"tokenId": token_id}
logging.info("Consuming consent to obtain access token...")
resp = requests.post(url, headers=headers, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
# Validate presence
if "accessToken" not in data or "expiryTime" not in data:
raise KeyError("Response missing 'accessToken' or 'expiryTime'.")
logging.info("Access token obtained successfully.")
return data["accessToken"], data["expiryTime"]
# ----------------------
# Public: Get Valid Token
# ----------------------
def get_dhan_token():
"""
Returns a valid Dhan access token.
- If a saved token exists and has >= 7 hours remaining, reuse it.
- Otherwise, runs the 3-step flow and saves the new token.
"""
# Try reuse
loaded = _load_existing_token()
now_utc = datetime.now(timezone.utc)
if loaded:
token, expiry_dt = loaded
remaining = expiry_dt - now_utc
if remaining >= REUSE_BUFFER:
logging.info(
"Reusing existing token (remaining: %s >= buffer: %s).",
remaining, REUSE_BUFFER
)
return token, str(expiry_dt)
else:
logging.info(
"Existing token expires soon (remaining: %s < buffer: %s). Generating new token.",
remaining, REUSE_BUFFER
)
# Fresh flow
consent_app_id = _generate_consent()
token_id = _prompt_user_for_token_id(consent_app_id)
access_token, expiry_str = _consume_consent(token_id)
# Persist
_save_token(expiry_str, access_token)
return access_token, expiry_str
def get_access_token():
try:
token, expiry_str = get_dhan_token()
logging.info(f"Access Token: {token} and expiry {expiry_str}...")
return token, expiry_str
except Exception as e:
logging.exception("Failed to obtain Dhan access token: %s", e)
return '', ''
get_access_token()
The static IP and new framework for algo trading to be implemented from 3Jan26.
Can’t post link as a restriction.for new user
fully automatic code required…no manula copy paste any thing ,no
@Hardik Why dont you provide a python script for fully automated access token generation like some users have provided in the comment vinay_rana8727.
Also one user provided Titamazon , encrypted script for the same. pls check and provide something similar so we dont have to struggle for access token daily.
hi this is how i solved it from my end. Mentioned in the different chat as well.
Do try it out - this should work for you.
Hi @Hardik
While generating API Key I found that the redirect URL has a character limit of 104. But when I’m integrating with Google Sheet App Script Web App URL, the URL length is around 121 or more.
Could you please increase the character limit of redirect URL?
Otherwise unnecessarily we have to use some URL shortener service to reduce the URL length and it has to redirect the URL along with tokenId parameter properly.
Thanks and Regards
Suman K Jana
Hey @SumanKJana
Sure, will take a look into increasing this