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#
- Install the SDK and set up your credentials: an API key approved for today, and the IP you will place orders from.
- 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.
- 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.
- Create an API key with the scopes your code needs, such as
orders.writeto place orders ormarketdata.readfor quotes. Copy the secret when it is shown: it is shown only once. - Approve the key for each trading day. Creating a key approves it for that day, and approval lapses at 06:00 IST.
- 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#
- Authentication: sharing a token across processes, narrowing scopes, and the daily token.
- Orders, Portfolio, Funds and Account: the trading methods.
- Market data and Scrip master: quotes, candles, the option chain and instrument lookups.
- Live streams:
SahiFeedfor ticks and order updates. - Errors: every exception and when it is raised.
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:
- Money is integer paise. ₹1 is
100, so ₹1,081.80 is108180. - Quantities are units, not lots. One NIFTY lot of 65 is
qty=65. - Timestamps are epoch milliseconds.
- Identifiers are strings, except
instrument_id, which is an integer. - Order writes must come from a registered IP. Reads and streams are not IP-checked.
- The SDK calls
apis.sahi.comanddirect.sahi.com. If a firewall limits outbound traffic, allow both.
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 |