SAHI DOCS

Python SDK

Authentication

Mint a token in one call or share one across processes, narrow its scopes, and rotate at 06:00 IST.

The SDK signs in the way the API does: it exchanges your API key for an access token, called minting a token, and sends that token on every call. This page follows the API reference's Authentication page step by step and shows how each step looks in Python. Replace pk_... and sahi_sk_... in the samples with your key id and secret.

Creating a key, registering your IP and approving the key are done in the API Dashboard only; there is no API or SDK call for them.

Create an API key#

Create the key in the API keys section of your Sahi account, with the scopes your code needs. Copy the secret when it is shown: it is shown once. The scopes are fixed for the life of the key, and each has a constant, such as SahiAPI.SCOPE_ORDERS_WRITE; see Create an API key for what each grants.

Register your static IP#

Order writes (place, modify, cancel) must come from an IP registered on your account; reads and the streams need none. An order write from any other address raises SahiIpNotAllowedException. See Register your static IP for the rules, including the 7-day lock on a changed address.

Approve the key for the trading day#

A key mints tokens only on a day you have approved it for, in the same API keys section. Creating a key approves it for that day; approval lapses at 06:00 IST. Minting from an unapproved key raises SahiKeyNotApprovedException.

Mint an access token#

One call: from_credentials#

SahiAPI.from_credentials mints a token and returns a client ready to use.

SahiAPI.from_credentials(key_id: str, secret: str, *, scopes: list[str] | None = None, scrip_master_url: str | None = None, timeout: float = 10.0) -> SahiAPI
from sahiapi import SahiAPI

api = SahiAPI.from_credentials(key_id="pk_...", secret="sahi_sk_...")
Parameter Notes
key_id Required. The key id, which begins pk_. Not a secret.
secret Required. The secret shown once when the key was created.
scopes Narrow the token to some of the key's scopes. See Narrow the scopes.
scrip_master_url Another copy of the scrip master. Default: the published file.
timeout Seconds, for the mint and as the client's default. Default 10.0.

Two steps: share one token#

When several processes should share one token for the day, mint it with get_access_token and build each client from the result with SahiAPI(token). get_access_token takes the same arguments as from_credentials, except scrip_master_url, and returns the endpoint's response unchanged: access_token, token_type, expires_in, expires_at and scope.

sahiapi.get_access_token(key_id: str, secret: str, *, scopes: list[str] | None = None, timeout: float = 10.0) -> dict
SahiAPI(token: dict | str, *, scrip_master_url: str | None = None, timeout: float = 10.0)
from sahiapi import SahiAPI, get_access_token

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

Narrow the scopes#

A token carries the key's scopes, or the subset you ask for. A process that only reads quotes should not hold a token that can place orders, even if the key behind it can. Asking for a scope the key does not hold raises SahiBadRequestException.

api = SahiAPI.from_credentials(
    key_id="pk_...",
    secret="sahi_sk_...",
    scopes=[SahiAPI.SCOPE_MARKETDATA_READ],
)
print(api.scopes)  # frozenset({'marketdata.read'})

Call the API#

The client sends its token on every call; there is nothing more to do. It reports what the token says about itself, read from the token without checking it. This is for information: the server checks every call.

Property or function Returns
api.scopes A frozenset of the token's scopes; empty if they cannot be read.
api.token_expires_at When the token expires, as a datetime in UTC, or None if unreadable.
api.access_token The token string in use. Read-only; change it with set_access_token.
sahiapi.token_scopes(token) The same scopes for any token string. Raises SahiConfigurationException if it is not a token.
sahiapi.token_expires_at(token) The same expiry for any token string. Raises SahiConfigurationException if it is not a token.

Token lifetime#

A token expires at 06:00 IST, when the trading day ends, and there are no refresh tokens. The client never refreshes on its own: an expired token comes back as SahiAuthenticationException. At start of day, or when that happens, mint again and give the client the new token:

api.set_access_token(token: dict | str) -> None
from datetime import datetime, timezone

from sahiapi import get_access_token

if api.token_expires_at and api.token_expires_at < datetime.now(timezone.utc):
    api.set_access_token(get_access_token(key_id="pk_...", secret="sahi_sk_..."))

When something goes wrong#

The same failures as the API's When something goes wrong, by the exception the SDK raises.

Minting (from_credentials, get_access_token):

Exception Status, code Usually means
SahiBadRequestException 400 InvalidRequest A field is missing or malformed, or the scopes asked for are not the key's
SahiAuthenticationException 401 Unauthorized Unknown key id, wrong secret, or a revoked key. The server does not say which
SahiAccountLockedException 401 Locked The account is frozen or locked
SahiKeyNotApprovedException 403 Forbidden The key is not approved for today
SahiRateLimitException 429 TooManyRequests Too many mints. Wait for the next minute

Calling the API:

Exception Status, code Usually means
SahiAuthenticationException 401 UNAUTHORIZED The token is missing, expired, or was revoked with its key. Mint a new one
SahiIpNotAllowedException 403 IP_NOT_ALLOWED An order write did not come from a registered IP
SahiAuthorisationException 403 FORBIDDEN The token lacks the scope this call needs
SahiRateLimitException 429 RATE_LIMITED Over the call's rate limit

A SahiIpNotAllowedException on a call that worked yesterday almost always means this machine's public address changed. Errors lists every class.

Checklist#

  1. Create a key with the scopes you need, and keep the secret out of your code, in an environment variable or a secret store.
  2. If the client places orders, register the public IP it calls from.
  3. Approve the key each trading day.
  4. Mint a token at start of day, narrowed to the scopes that process needs, and reuse it until 06:00 IST.
  5. Build a new SahiFeed after each new token.