SAHI DOCS

Get started

Quick start

Five steps to your first API order: create a key, register your IP, sign in, and make your first calls with the Python SDK or cURL.

How will you call the API?
  1. Create an API keyRegular or TOTP
  2. Register your IPFor orders only
  3. Sign inPython SDKcURL
  4. First callsLTP → Funds → Order
  5. Every trading dayApprove · Sign in

In your SAHI account#

Part A · SAHI web → Profile → API Dashboard

Step 1: Create an API key#

  1. Open SAHI web, click your profile avatar (bottom left) → API Dashboard → Create API key.
  2. Enter a Name, e.g. Trading bot.
  3. Choose the Access: Full access (orders + market data) or Market data (data only).
  4. Choose the Key type. Regular: you click Approve in the dashboard every trading day. TOTP: a code from an authenticator app approves it, so a scheduled bot runs without you.
  5. Regular: click Create API key.
    TOTP: click Continue, scan the QR with Google Authenticator (or copy the Setup key), enter the 6-digit code, then Create API key. Save the setup key with your other secrets. You have 10 minutes; nothing is created until the code is accepted.
  6. Copy the API key (pk_…) and API secret (sahi_sk_…) into your secret store. Tick I have saved my credentials → Done. The secret is shown only once.

Good to know: you can hold up to 5 keys. Deleting a key stops every token minted from it at once.

Create a Regular key
Create a TOTP key
Delete a key

Step 2: Register your static IP#

For orders only

  1. Skip this step if you will only read market data.
  2. On the machine that will place orders, find its public IP:
    terminal
    curl https://checkip.amazonaws.com
  3. In the API Dashboard, click Add static IP, enter it as the Primary IP address, click Save IP address, check the review screen and Confirm & Lock for 7 days. Secondary is optional: a backup line, not a second bot.

Good to know: use a fixed IP (cloud VM or static broadband); home and mobile IPs change. One IP belongs to one SAHI account. A slot stays locked for 7 days even if you delete the IP.

Add a static IP

On your machine#

Part B · Python SDK sahiapicURL · macOS/Linux shell shown

Step 3: Sign in#

  1. Install from PyPI (Python 3.9 or newer; use pip3 if pip is not found). TOTP keys need sahiapi 0.3.0 or newer. Add pyotp if your bot runs unattended.
    terminal
    pip install sahiapi pyotp
  2. Save this as hello.py, replace the placeholders, run python3 hello.py.
    hello.py · Regular key
    from sahiapi import SahiAPI
    
    api = SahiAPI.from_credentials(
        key_id="pk_...",
        secret="sahi_sk_...",
    )
    
    print(api.get_profile())
    hello.py · TOTP key
    import pyotp
    from sahiapi import SahiAPI
    
    api = SahiAPI.from_credentials(
        key_id="pk_...",
        secret="sahi_sk_...",
        totp=pyotp.TOTP("YOUR_SETUP_KEY").now(),   # or type the 6 digits from your app: totp="042917"
    )
    
    print(api.get_profile())
  3. You should see your profile. The token lasts until 6 AM IST, so sign in once a day. If you see SahiKeyNotApprovedException → approve the key (Step 5). SahiAuthenticationException → wrong key id or secret. SahiInvalidTotpException → wrong code; check the machine's clock.
  1. Exchange your key for an access token (valid until 6 AM IST). Minting uses apis.sahi.com; every other call uses direct.sahi.com.
    terminal · Regular key
    curl -X POST https://apis.sahi.com/api/v1/apikeys/token \
      -H "Content-Type: application/json" \
      -d '{"key_id":"pk_your_key_id","secret":"sahi_sk_your_secret"}'
    # → {"access_token":"eyJ…","expires_at":"…T06:00:00+05:30",…}
    terminal · TOTP key
    curl -X POST https://apis.sahi.com/api/v1/apikeys/token \
      -H "Content-Type: application/json" \
      -d '{"key_id":"pk_your_key_id","secret":"sahi_sk_your_secret","totp":"042917"}'
    # totp = the 6 digits your authenticator app shows right now (as a string)
  2. Put the access_token value in a variable and send it on every call:
    terminal
    export TOKEN=eyJ…
    curl https://direct.sahi.com/v1/profile -H "Authorization: Bearer $TOKEN"
  3. You should see your profile. Mint once a day. If you see 403 Forbidden on mint → approve the key (Step 5). 401 Unauthorized → wrong key id or secret. 401 InvalidTotp → wrong code; check the machine's clock.

Step 4: Make your first calls#

Prices are in paise (divide by 100); quantities are in units.

  1. Find your instrument. Market data uses instrument_id; orders use exchange_segment + symbol. Both come from the scrip master, which the SDK downloads for you.
    hello.py · continued
    instrument_id = api.get_instruments_by_symbol("RELIANCE", SahiAPI.EXCHANGE_SEGMENT_NSE)[0]["id"]   # 12027
    terminal
    curl -O https://assets.sahi.com/external/instruments.csv            # rebuilt daily
    grep ',RELIANCE,' instruments.csv | grep ',NSE,NSE,'                 # first column is the id → 12027
  2. Get its last traded price.
    hello.py · continued
    quote = api.get_quotes(instrument_id)["data"][str(instrument_id)]   # keyed by the id as a string
    print(quote["last_trade_price"] / 100, "INR")
    terminal
    curl "https://direct.sahi.com/v1/marketdata/ltp?i=12027" -H "Authorization: Bearer $TOKEN"
    # → {"data":{"12027":{"last_trade_price":251000,…}}}   # paise: ₹2,510.00
  3. Check your funds.
    hello.py · continued
    print(api.get_funds()["available"]["stocks_delivery"] / 100, "INR for delivery buys")
    terminal
    curl https://direct.sahi.com/v1/funds -H "Authorization: Bearer $TOKEN"
    # → {"available":{"stocks_delivery":10500000,…},…}   # paise
  4. Place your first order during market hours (09:15–15:30 IST). This is a real order: a limit buy of 1 share at the bottom of the day's price band, so it rests unfilled. The script cancels it if it has not filled.Cancel it with the last command.
    hello.py · continued
    import time
    
    price = quote["dpr_lower"]                                        # paise; bottom of the day's band
    if api.get_funds()["available"]["stocks_delivery"] < price:
        raise SystemExit("not enough funds for one share")
    
    result = api.place_order(
        exchange_segment=SahiAPI.EXCHANGE_SEGMENT_NSE,
        symbol="RELIANCE",
        txn_type=SahiAPI.TXN_TYPE_BUY,
        qty=1,
        order_type=SahiAPI.ORDER_TYPE_LIMIT,
        product=SahiAPI.PRODUCT_DELIVERY,
        validity=SahiAPI.VALIDITY_DAY,
        price=price,
    )
    order_id = result["order_ids"][0]
    
    time.sleep(1)                                                     # the order takes a moment to reach the book
    status = api.get_order_detail(order_id)["status"]
    print(status)                                                     # Open, or Rejected with rejection_reason
    
    if status in (SahiAPI.ORDER_STATUS_OPEN, SahiAPI.ORDER_STATUS_OPEN_PARTIAL):
        api.cancel_order(order_id)                                    # not filled yet
    terminal
    # a) Day's price band (paise): dpr_lower / dpr_upper in the quote
    curl "https://direct.sahi.com/v1/marketdata/quotes?i=12027" -H "Authorization: Bearer $TOKEN"
    
    # b) Limit buy, 1 share, at dpr_lower (replace the price)
    curl -X POST https://direct.sahi.com/v1/orders \
      -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
      -d '{"exchange_segment":"NSE","symbol":"RELIANCE","txn_type":"B","qty":1,
           "order_type":"LMT","product":"C","validity":"DAY","price":249000}'
    # → {"order_ids":["2610…"]}
    
    # c) Check it (status: Open, or Rejected + rejection_reason), then cancel it
    curl https://direct.sahi.com/v1/orders/ORDER_ID -H "Authorization: Bearer $TOKEN"
    curl -X DELETE https://direct.sahi.com/v1/orders/ORDER_ID -H "Authorization: Bearer $TOKEN"
    An order id means accepted, not placed: check status. SahiIpNotAllowedException403 IP_NOT_ALLOWED → your IP isn't registered (Step 2).
  5. View your positions and orders.
    hello.py · continued
    for p in api.get_positions()["positions"]:
        print(p["instrument_id"], p["net_qty"], p["net_avg_price"] / 100, p["realised_pnl"] / 100)
    
    for o in api.get_order_list()["orders"]:                           # today's order book
        print(o["order_id"], o["symbol"], o["status"], o["filled_qty"])
    terminal
    curl https://direct.sahi.com/v1/positions -H "Authorization: Bearer $TOKEN"   # net_qty, net_avg_price, realised_pnl
    curl https://direct.sahi.com/v1/orders    -H "Authorization: Bearer $TOKEN"   # today's order book
    Also: api.get_holdings(), api.modify_order(), and SahiFeed for live ticks. See the SDK docs.Also: GET /v1/holdings, PUT /v1/orders/{id} to modify, and the WebSocket streams for live ticks. See the API reference.

Step 5: Every trading day#

  1. Regular key: open the API Dashboard after 6 AM IST. The key shows Expired; click Approve and it changes to Resets 6 AM tomorrow. A key created today is already approved.TOTP key: nothing to do here; it shows Active. The code you send when signing in approves it.
  2. Sign in once (Step 3) and reuse the token until 6 AM IST.
  3. If orders fail with 403 IP_NOT_ALLOWED, your public IP has changed. The slot is locked for 7 days, so add the new IP as Secondary, or write to developers@sahi.com.
Approve a Regular key · Expired → Approve → Resets 6 AM tomorrow