Skip to content
HostEthical
All guides

VPS 14 min read ·

Dhan API in Python: Static IP, Access Token and First Order

Use the Dhan API from Python: get the 24-hour access token, register your static IP, place a first LIMIT order with dhanhq 2.3 and fix DH-905 errors.

To use the Dhan API from Python you need three things: a daily access token, a static IP registered with Dhan, and the official dhanhq library (version 2.3.0 as of October 2026). Every Dhan access token now lasts 24 hours, and Dhan accepts order placement, modification and cancellation only from a static IP you have whitelisted. The Trading APIs are free; live market data is a paid add-on.

This guide covers tokens, IP registration, a first LIMIT order, the live feed and common errors, all on a Linux server. Prices, limits and rules come from Dhan's own documentation and help pages, checked in October 2026.

Before you start. This is a technical guide for people who write their own trading code. HostEthical isn't affiliated with Dhan and provides no strategies, signals or tips. Nothing here is investment advice: test with the smallest quantity you can, and read Dhan's current terms.

What the Dhan API costs and what you need first

Dhan sells DhanHQ as two products:

  • Trading APIs: free. Placing and managing orders, and reading your portfolio and funds. Dhan's pricing page lists them "for FREE".
  • Data APIs: ₹499 a month plus applicable taxes, debited from your trading ledger every 30 days, according to Dhan's help centre. This covers the live market feed over WebSocket, market quotes, and historical and intraday data. The old free-data offer for active traders has ended. A yearly plan exists, but Dhan's help pages don't give its price.

You also need an active Dhan account with your segments switched on, a server with a fixed public IP, and Python 3.10 or newer.

Dhan publishes these rate limits (as of October 2026):

API groupPer secondPer minutePer hourPer day
Order APIs102501,0007,000
Data APIs5––100,000
Quote APIs1UnlimitedUnlimitedUnlimited
Non-trading APIs20UnlimitedUnlimitedUnlimited

Each order can be modified at most 25 times. Ten orders a second is also the exchanges' threshold above which a retail algo must be registered, as our guide to SEBI's algo trading rules explains.

Choose how your bot gets its daily access token

Every request carries an access token. Dhan's help centre says the maximum validity is now 24 hours, in line with exchange and regulator requirements. Its authentication docs describe three ways to get one:

MethodHow it worksValidityHands-free?
Token from Dhan Webweb.dhan.co → My Profile → Access DhanHQ APIs → Generate Access Token, with an app name and an optional postback URL24 hours; the RenewToken call extends a still-active one by 24 hoursNo, you paste it in yourself
API key and secretCreate a key with an app name and redirect URL. Each day you log in once in a browser with 2FA; Dhan sends a tokenId to the redirect URL and your code swaps it for a tokenKey and secret: 12 months. Token: 24 hoursPartly: a person logs in each day
PIN and TOTPTurn on TOTP under Setup TOTP, then send your client ID, PIN and the current TOTP code to the generateAccessToken endpoint24 hoursYes

Some Dhan help pages call the menu "Get Trading & Data APIs". You can hold up to 5 active tokens at once.

The PIN and TOTP route in Python

When you set up TOTP, Dhan shows a QR code and a text code. That text code is the TOTP secret, and pyotp (preinstalled on the Algo VPS) turns it into the current 6-digit code:

import os

import pyotp
from dhanhq import DhanLogin

login = DhanLogin(os.environ["DHAN_CLIENT_ID"])
code = pyotp.TOTP(os.environ["DHAN_TOTP_SECRET"]).now()
result = login.generate_token(os.environ["DHAN_PIN"], code)
token = result["accessToken"]
print("New token, valid until", result["expiryTime"])

Weigh the trade-off: your PIN and TOTP secret together are enough to log in to your Dhan account. A server holding both needs SSH-key logins only, secrets that only root can read, and a fresh TOTP setup if you ever suspect a leak. Pasting a web token each morning is more effort, but leaves nothing long-lived on the server.

Whichever route you choose, your bot needs a fresh token every trading day. Our guide to running a trading bot 24/7 shows how to schedule that step with a systemd timer before the market opens.

Register your server's static IP with Dhan

On an Algo VPS, run algo-ip: it prints the IPv4 address to register and the address Python actually connects from. The "Register your static IP" card in your HostEthical dashboard shows the same address with a link to Dhan's API settings.

In Dhan Web, the steps are: Profile → Get Trading & Data APIs → scroll down → Add IP. Or do it from code: dhanhq wraps Dhan's setIP, modifyIP and getIP endpoints in its DhanLogin class. This check script shows your token's expiry, your data plan and your registered IPs:

import os

from dhanhq import DhanLogin

login = DhanLogin(os.environ["DHAN_CLIENT_ID"])
token = os.environ["DHAN_ACCESS_TOKEN"]

profile = login.user_profile(token)
print("Token valid until:", profile.get("tokenValidity"))
print("Data plan:", profile.get("dataPlan"))
print("Registered IPs:", login.get_ip(token))
# First time only, with the IPv4 address that algo-ip prints:
# print(login.set_ip(token, "203.0.113.10", "PRIMARY"))

The rules, from Dhan's documentation:

  • A primary and an optional secondary IP, each IPv4 or IPv6.
  • A 7-day lock. Once an IP is whitelisted, it can't be edited for the next 7 days. getIP returns modifyDatePrimary and modifyDateSecondary, the dates from which each can be changed.
  • Only order calls are checked. Placing, modifying and cancelling orders, including Super Orders and Forever Orders, need the whitelisted IP. Fetching orders, trades and market data does not.
  • A new IP needs a new token. If you added the IP after generating your token, generate the token again.

The IPv6 trap and how Dhan compares with other brokers are covered in our static IP for algo trading guide.

Dhan's family-IP rule

Dhan's API docs say "each individual needs to have a unique static IP". Its help centre adds one exception: eligible family member accounts can be mapped to a single static IP, subject to verification and approval. To request it, email apihelp@dhan.co from your registered email address, with the registered address of every family member's account in CC. Those pages don't define "family". The exchange rules use SEBI's definition: spouse, dependent children and dependent parents. Never share the IP with friends or clients.

A dedicated Mumbai IPv4 with dhanhq already installed. Algo VPS comes with a Python environment, broker SDKs, IPv4-first networking and a ready systemd service for your bot. See Algo VPS →

Install dhanhq and connect with DhanContext

On an Algo VPS, dhanhq is already in the virtual environment at /opt/algo/venv. Check which version you have, and upgrade if a newer one is out:

/opt/algo/venv/bin/pip show dhanhq
/opt/algo/venv/bin/pip install --upgrade dhanhq

On another server, create a virtual environment and run pip install dhanhq. Version 2.3.0 came out on 8 October 2026; Ubuntu 24.04 ships Python 3.12, which meets its 3.10 minimum.

The constructor has changed. Tutorials written for version 2.0 and earlier create the client with dhanhq("client_id", "access_token") and stream prices with marketfeed.DhanFeed. From version 2.1 onwards you build a DhanContext and pass it in, and the old two-argument call fails with a TypeError:

from dhanhq import DhanContext, dhanhq

ctx = DhanContext(client_id, access_token)
dhan = dhanhq(ctx)

Keep your credentials out of the code. On an Algo VPS they belong in /opt/algo/.env, which is created with chmod 600 so only root can read it:

DHAN_CLIENT_ID=your-client-id
DHAN_ACCESS_TOKEN=paste-todays-token-here
# Only if you use the PIN and TOTP login:
DHAN_PIN=
DHAN_TOTP_SECRET=

The algo@ service loads this file automatically. When you run a script by hand, load it into your shell first:

cd /opt/algo
set -a; . ./.env; set +a
venv/bin/python dhan_check.py

Run the check script this way before your first order: it catches an expired token or a missing IP while nothing is at stake.

Place a LIMIT order and check its status

This script places one LIMIT buy on NSE and checks its status every few seconds until it is filled, rejected, cancelled or expired. It refuses to run until you set your own limit price.

"""Place one LIMIT order on Dhan and follow its status. Run it by hand."""
import os
import sys
import time

from dhanhq import DhanContext, dhanhq

SECURITY_ID = "1333"      # HDFC Bank, NSE equity (from Dhan's instrument list)
QUANTITY = 1
LIMIT_PRICE = 0.0         # set your own limit price before running
TAG = "first-order-001"   # your correlation ID, up to 30 characters

if LIMIT_PRICE <= 0:
    sys.exit("Set LIMIT_PRICE first.")

ctx = DhanContext(os.environ["DHAN_CLIENT_ID"], os.environ["DHAN_ACCESS_TOKEN"])
dhan = dhanhq(ctx)

resp = dhan.place_order(
    security_id=SECURITY_ID,
    exchange_segment=dhan.NSE,   # NSE_EQ
    transaction_type=dhan.BUY,
    quantity=QUANTITY,
    order_type=dhan.LIMIT,
    product_type=dhan.CNC,       # delivery; dhan.INTRA for intraday
    price=LIMIT_PRICE,
    validity=dhan.DAY,
    tag=TAG,
)
if resp["status"] != "success":
    sys.exit(f"Order not accepted: {resp['remarks']}")   # DH-905, DH-906 ...

order_id = resp["data"]["orderId"]
print("Order", order_id, resp["data"]["orderStatus"])

for _ in range(10):
    time.sleep(3)
    r = dhan.get_order_by_id(order_id)
    if r["status"] != "success":
        print("Lookup failed:", r["remarks"])
        continue
    order = r["data"][0] if isinstance(r["data"], list) else r["data"]
    status = order["orderStatus"]
    print(status, "filled", order.get("filledQty"), "of", order.get("quantity"))
    if status == "REJECTED":
        print("Reason:", order.get("omsErrorDescription"))
    if status in ("TRADED", "REJECTED", "CANCELLED", "EXPIRED"):
        break

What to know about it:

  • Security IDs come from Dhan's instrument list. Look them up in the compact CSV (column SEM_SMST_SECURITY_ID) or with dhan.fetch_security_list("compact"). The same number can exist in several segments (1333 is also a currency contract), so always pair it with the right exchange_segment.
  • Every SDK call returns a dictionary with status, remarks and data. When a call fails, remarks holds Dhan's error_code, error_type and error_message.
  • Order statuses are TRANSIT, PENDING, PART_TRADED, TRADED, REJECTED, CANCELLED and EXPIRED. A rejected order carries the reason in omsErrorDescription. To cancel an open order, call dhan.cancel_order(order_id).
  • The tag becomes the order's correlation ID. dhan.get_order_by_correlationID(TAG) finds the order even if your script lost the order ID.
  • Polling is fine for a test. A bot can instead listen with the SDK's OrderUpdate class, which receives changes to your orders over Dhan's order update WebSocket.

Stream live prices with Dhan's market feed

The live feed needs the Data APIs subscription; without it, the connection closes with code 806. The static IP isn't checked for market data.

"""Print live prices from Dhan's market feed (needs the Data API plan)."""
import os

from dhanhq import DhanContext, MarketFeed

ctx = DhanContext(os.environ["DHAN_CLIENT_ID"], os.environ["DHAN_ACCESS_TOKEN"])
instruments = [
    (MarketFeed.NSE, "1333", MarketFeed.Ticker),   # (segment, security ID, mode)
]

feed = MarketFeed(ctx, instruments, "v2")
feed.run_forever()            # connects and subscribes, then returns
while True:
    tick = feed.get_data()    # waits for the next packet
    if tick and tick.get("type") == "Ticker Data":
        print(tick["security_id"], tick["LTP"], flush=True)

The limits from Dhan's feed documentation, as of October 2026:

  • Up to 5 WebSocket connections per user. Opening a sixth disconnects your first one with code 805.
  • Up to 5,000 instruments per connection, and at most 100 in a single subscription message.
  • Three modes: Ticker (last price and time), Quote (adds volume, OHLC and buy/sell quantities) and Full (adds market depth).
  • A ping every 10 seconds. The server drops clients that stay silent for more than 40 seconds. The SDK answers for you and decodes Dhan's binary packets.

If the connection drops, get_data() raises an exception and the script stops. Under the service described next, systemd simply starts it again.

Run your Dhan bot 24/7 with the algo@ service

Every Algo VPS has a systemd template called algo@<name>. Save your script as /opt/algo/dhan_feed.py, then run, as root:

systemctl enable --now algo@dhan_feed
journalctl -u algo@dhan_feed -f          # live logs
systemctl restart algo@dhan_feed         # after you put a new token in .env

The template runs the script with the Python in /opt/algo/venv, loads /opt/algo/.env, starts the bot at boot and restarts it 10 seconds after a crash. Three things matter once real orders are involved:

  • A restart can repeat an order. If your script places an order and then crashes, systemd starts it again and the same code may place a second order. Give each order a fixed correlation ID and look it up with get_order_by_correlationID before placing it.
  • A permanent error keeps looping. With an expired token (DH-901), the script fails, restarts 10 seconds later and fails again, all day. Catch errors that a retry can't fix, send yourself an alert and exit cleanly. The 24/7 bot guide covers alerts, crash loops and kill switches.
  • Flush your output. Python buffers print when it isn't writing to a terminal, so use flush=True or logging, or lines reach journalctl late.

Dhan's kill switch API can also block trading for the rest of the day, once your positions are closed and no orders are pending.

You don't need the Algo VPS for any of this. A plain Cloud VPS starts at ₹860 a month, or about ₹774.17 a month when billed yearly. Mumbai adds a monthly location fee, and you install Python, the SDK, the IPv4 preference and the service yourself. The Algo VPS, with all of that done at first boot, costs ₹1,510 a month, or about ₹1,325 a month when billed yearly. Our guide to choosing a VPS for algo trading compares both with cloud and broker-hosted options.

Fixing DH-905 and other Dhan API errors

The codes and descriptions below are from Dhan's error annexure, with what to check first.

CodeDhan's descriptionWhat to check
DH-901Client ID or access token invalid or expiredGenerate a new token, check the client ID in .env, restart the service
DH-902No Data API subscription, or no Trading API accessThe Data APIs tab in Dhan Web
DH-903Account error, such as a segment that isn't activatedYour segments and account status in Dhan
DH-904Rate limit breachedSlow down; see the limits table above
DH-905Input exception: missing or bad parameters. IP rejections also arrive as "DH-905 Invalid IP"The IP checklist below, then your order fields
DH-906Incorrect order requestPrice and tick size, quantity and lot size, product type, market hours
805 / 806 / 807Feed: too many connections / Data APIs not subscribed / token expiredClose extra connections, subscribe, or renew the token

For "DH-905 Invalid IP", work through these in order:

  1. Compare addresses on the server. Run algo-ip and the check script. If the address Python uses differs from the IPv4 you registered, your requests are leaving over IPv6. Make the server prefer IPv4 or register the IPv6 address as your secondary IP, as the static IP guide explains.
  2. Send the order from the server, not from your laptop, and with no VPN or proxy in the path.
  3. Generate a new token if you added the IP after creating the old one. This is the fix Dhan suggests.
  4. Use Dhan's own check. In Dhan Web go to Profile → Get Trading & Data APIs → Sandbox (top right) → IP Setup → Get Static IP, and send your latest token. The response shows your primary IP, secondary IP, the detected IP and whether orders are allowed.
  5. Check the 7-day lock if you changed the IP recently. Dhan can't turn off IP whitelisting or allow a dynamic IP, but apihelp@dhan.co can help with the setup.

Dhan's own TradingView webhooks: when you may not need a bot

For a plain "alert fires, order is placed" setup, Dhan has a no-code feature. Its webhook guide gives the steps:

  1. In web.dhan.co, go to Orders → Webhooks, click Manage under TV Webhook URL and generate a URL.
  2. Build the order in Dhan's webhook order window (equity, F&O, currency, commodity, multi-segment or baskets) and click Generate JSON.
  3. In the TradingView alert, turn on Webhook URL, paste Dhan's URL and put only that JSON in the Message box.

You need a TradingView Pro, Pro+ or Premium plan. Each JSON carries a secret tied to your webhook URL, so if you generate a new URL you must update every alert. TradingView only sends the alert: it doesn't know whether Dhan placed the order, so check your order book.

Dhan's webhook guide doesn't mention a static IP. In an October 2026 reply on MadeForTrade, Dhan's community forum, the account t7support said webhook orders need neither API access nor IP whitelisting. We couldn't find that in Dhan's documentation, so confirm it with Dhan before you rely on it.

If you need logic between alert and order, such as position checks or more than one broker, run your own receiver on your static-IP server; our TradingView webhook guide builds one. If Dhan's webhooks or a broker-hosted platform already do the job, you don't need a server at all.

Register one IP and let your Dhan bot run all day. Algo VPS Mumbai gives you a dedicated IPv4 address, dhanhq in a ready virtualenv and a systemd service that restarts your bot. See Algo VPS →

Frequently asked questions

Is the Dhan API free?

The Trading APIs are free, but the Data APIs are not. As of October 2026, Dhan charges ₹499 a month plus applicable taxes for the Data APIs, debited from your trading ledger every 30 days. That subscription covers the live market feed, market quotes, and historical and intraday data. Placing, modifying and cancelling orders, and fetching positions, holdings and funds, costs nothing extra.

How long is a Dhan API access token valid?

A Dhan access token is valid for 24 hours, whichever way you generate it: on Dhan Web, through the API key and secret login, or with your PIN and a TOTP code. Your bot therefore needs a fresh token every trading day. You can hold up to five active tokens at once, and Dhan's RenewToken call can extend a still-active token that was generated on Dhan Web.

Why does Dhan reject my API order with DH-905 Invalid IP?

DH-905 Invalid IP means the address your order came from does not match the static IP you whitelisted with Dhan. Common causes are requests leaving over IPv6 when you registered IPv4, testing from a laptop instead of the server, a VPN, or a token generated before you added the IP. Check the address on the server, generate a new token and try again.

Can family members use the same static IP with Dhan?

Yes, but only with Dhan's approval. Dhan's API docs say each individual needs a unique static IP, while its help centre says eligible family member accounts can be mapped to one IP after verification. To ask for it, email apihelp@dhan.co from your registered address with each family member's registered email in CC. Never share the IP with friends or clients.