SAHI DOCS

Python SDK

Getting started

Install the official Python client, authenticate, resolve an instrument, read the market, and place and cancel an order.

sahiapi is the official Python client for the Sahi APIs. It gives you one class, SahiAPI, for every REST endpoint, and SahiFeed for the live market-data and order-update streams. The SDK signs in for you, keeps the streams connected, and returns each endpoint's JSON as a Python dict, so the API reference's fields apply unchanged. Any error comes back as an exception you can catch.

How to work through this#

  1. Install the SDK and set up your credentials: an API key approved for today, and the IP you will place orders from.
  2. Run the samples below in order. Each builds on the one before it, from your first call to placing and cancelling an order on your real account.
  3. Look up the methods you need in the reference pages under Where next. New to trading? See Trading terms.

Install#

The SDK needs Python 3.9 or newer. Install it from PyPI:

pip install sahiapi

Credentials#

The SDK signs in with an API key. You set the key up once in the API Dashboard; there is no API call for these steps. Authentication has the details.

  1. Create an API key with the scopes your code needs, such as orders.write to place orders or marketdata.read for quotes. Copy the secret when it is shown: it is shown only once.
  2. Approve the key for each trading day. Creating a key approves it for that day, and approval lapses at 06:00 IST.
  3. Register your static IP if you will place, modify or cancel orders. Order writes from any other address are refused; reads and streams need no registered IP.

Your first call#

SahiAPI.from_credentials exchanges your key id and secret for an access token and returns a client that sends it on every call. Replace pk_... and sahi_sk_... with your own. The token lasts until 06:00 IST, so a long-running process signs in once a day.

from sahiapi import SahiAPI

api = SahiAPI.from_credentials(
    key_id="pk_...",
    secret="sahi_sk_...",
)

print(api.get_profile())

Resolve an instrument#

Orders name an instrument by exchange_segment and symbol; market data and the tick stream name it by instrument_id. Both come from the scrip master, which the SDK downloads on first use and indexes for you. A lookup by symbol returns a list, so take the first match.

instrument_id = api.get_instruments_by_symbol("RELIANCE", SahiAPI.EXCHANGE_SEGMENT_NSE)[0]["id"]

Read the market#

Every price is in paise: divide by 100 for rupees. Market-data results are keyed by the instrument id as a string.

quote = api.get_quotes(instrument_id)["data"][str(instrument_id)]
print(quote["last_trade_price"] / 100, "INR")

For prices you follow continuously, subscribe on the tick stream instead of polling. Ticks reach your callback while feed.consume() runs. It blocks, so run this sample on its own rather than before the order sample below. Outside market hours you get one tick with the last price, then nothing until the next session.

from sahiapi import SahiFeed

def on_tick(data, meta):
    print(data)

feed = SahiFeed(api)
feed.subscribe_ltp([instrument_id], on_data_received=on_tick)
feed.consume()  # blocks; stop with Ctrl+C or feed.stop() from another thread

Place and cancel an order#

This sample places a real order on your account. It sizes the buy to what your delivery funds cover, places a limit at the bottom of the day's price band so it rests on the book, and cancels it if it has not filled. Run it during market hours: outside them a regular order is refused.

import time

price = quote["dpr_lower"]  # paise
available = api.get_funds()["available"]["stocks_delivery"]  # paise
qty = min(available // price, 1)  # what the funds cover, at most 1 share
if qty < 1:
    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=qty,
    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)

if status in (SahiAPI.ORDER_STATUS_OPEN, SahiAPI.ORDER_STATUS_OPEN_PARTIAL):
    api.cancel_order(order_id)  # not filled yet

Raise the 1 to buy as many shares as the funds cover.

An order id means the order was accepted, not that it reached the exchange. Some checks, such as tick size and price band, run after acceptance, so read the order's status before relying on it: a refused order shows Rejected with the reason in rejection_reason. If place_order raises SahiIpNotAllowedException, this machine's IP is not the one you registered.

Where next#

Getting help#

If the SDK or the API does not behave as these pages say, write to developers@sahi.com. Include the request_id from the exception if there is one.

Conventions#

The API's Conventions hold unchanged:

Market hours#

NSE and BSE cash and F&O trade 09:15–15:30 IST on exchange trading days. While the market is closed, a regular order is refused; send an after-market order (variant=SahiAPI.VARIANT_AMO) instead, and it goes out at the next open.

Trading terms#

Term Meaning
Segment NSE, BSE: cash. NFO, BFO: futures and options.
Product C delivery, I intraday, M F&O carry-forward, F MTF.
Order type LMT limit, MKT market, SL-LMT / SL-MKT stop-loss at trigger_price.
LTP Last traded price.
Price band The day's allowed range, dpr_lower to dpr_upper.
Tick size The smallest price step; a price must be a whole number of ticks.
Lot size F&O trade in lots; qty must be a multiple of lot_size.
AMO After-market order, sent at the next session's open.

Constants#

Each code is a class attribute on SahiAPI and a name in sahiapi. A leading _ stands for the group's prefix. MTF product F has no constant.

Group Constants Values
Exchange segment EXCHANGE_SEGMENT_NSE, _NFO, _BSE, _BFO NSE, NFO, BSE, BFO
Transaction type TXN_TYPE_BUY, TXN_TYPE_SELL B, S
Order type ORDER_TYPE_LIMIT, _MARKET, _SL_LIMIT, _SL_MARKET LMT, MKT, SL-LMT, SL-MKT
Product PRODUCT_DELIVERY, PRODUCT_INTRADAY, PRODUCT_NORMAL C, I, M
Validity VALIDITY_DAY, VALIDITY_IOC DAY, IOC
Variant VARIANT_REGULAR, VARIANT_AMO REGULAR, AMO
Order status ORDER_STATUS_OPEN, _OPEN_PARTIAL, _PARTIAL_FILLED, _COMPLETED, _CANCELLED, _REJECTED Open, OpenPartial, PartialFilled, Completed, Cancelled, Rejected
Candle size CANDLE_1M, _3M, _5M, _15M, _30M, _1H, _1D 1m, 3m, 5m, 15m, 30m, 1h, 1d
Tick mode MODE_LTP, MODE_QUOTE, MODE_DEPTH LTP, QUOTE, DEPTH
Scope SCOPE_ORDERS_WRITE, _ORDERS_READ, _POSITIONS_READ, _HOLDINGS_READ, _FUNDS_READ, _PROFILE_READ, _MARKETDATA_READ orders.write, orders.read, positions.read, holdings.read, funds.read, profile.read, marketdata.read
WebSocket close code WS_CLOSE_AUTH_EXPIRED, _FORBIDDEN, _HEARTBEAT, _DROPPED, _TOO_MANY 4401, 4403, 4408, 4409, 4429