Stolo API authentication
Stolo API authentication is a two-step handshake. You exchange your app key and secret for a bearer token once a day, then send that token with every other request. The secret never needs to leave the place you authenticate from.
Getting your key and secret
Your key and secret come from the API app in your Stolo account. Open the API App page (Account > API App in the Stolo app) and:
- If you don't have an app yet, enter a name and press Create app. The key and secret are ready to copy immediately.
- If you already have one, copy the key and the secret from there.
Each account has one app. The same page is where you regenerate the secret if it leaks, or delete the app to cut off access completely.
Get your app key and secret
Create your API app in a few seconds, or copy the credentials of the one you already have.
Open the API App pagePOST /authenticate
Exchanges an app key and secret for a token. This is the only endpoint that doesn't take a token. It isn't rate limited and isn't metered in Stolo Tokens.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Your app key, from the API App page |
secret | string | Yes | Your app secret |
- curl
- Python
- JavaScript
curl -X POST 'https://algoapi.stolo.in/v1/authenticate' \
-H 'Content-Type: application/json' \
-d '{"key":"YOUR_APP_KEY","secret":"YOUR_APP_SECRET"}'
import requests
res = requests.post(
"https://algoapi.stolo.in/v1/authenticate",
json={"key": "YOUR_APP_KEY", "secret": "YOUR_APP_SECRET"},
timeout=10,
)
data = res.json()["data"]
token, expires_in = data["token"], data["expires_in"]
const res = await fetch("https://algoapi.stolo.in/v1/authenticate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: "YOUR_APP_KEY", secret: "YOUR_APP_SECRET" }),
});
const { token, expires_in } = (await res.json()).data;
Response
{
"status": "success",
"message": "",
"response_meta": { "api_version": "v1", "message": "", "elapsed_time": "4.12ms" },
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 37800
}
}
| Field | Type | Description |
|---|---|---|
data.token | string | The bearer token to send on every other call |
data.expires_in | number | Seconds from now until the token expires (at the next 5 AM IST) |
Errors
| HTTP | message | Cause |
|---|---|---|
401 | Invalid App Key or Secret | The key doesn't exist, or the secret doesn't match it |
403 | Active stolo-pro or Stolo Trial 15 days plan required to use this API | The account that owns the app has no active Stolo Pro or Stolo Trial (15 days) plan |
422 | Validation failed | key or secret is missing or empty. data lists which one |
Sending the token
Put the token in the Authorization header of every request except /authenticate:
GET /v1/analysis/option-chain?symbol=NIFTY HTTP/1.1
Host: algoapi.stolo.in
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
The Bearer prefix is optional; the bare token works too.
Token lifetime: always 5 AM IST
A token doesn't last a fixed number of hours. It always expires at the next 5 AM IST, whenever you asked for it:
| Authenticated at | Expires at | Valid for |
|---|---|---|
| 8:30 AM Monday | 5:00 AM Tuesday | 20.5 hours |
| 6:30 PM Monday | 5:00 AM Tuesday | 10.5 hours |
| 4:00 AM Tuesday | 5:00 AM Tuesday | 1 hour |
So expires_in changes with the time of day. Don't hardcode 86,400. The simplest rule:
authenticate once before the market opens and reuse that token for the whole session.
One token per app
Each app has exactly one valid token at a time. Calling /authenticate again issues
a new token and immediately invalidates the old one, even if it hasn't expired. Any
process still holding the old token gets:
{ "status": "error", "message": "Token no longer valid, please re-authenticate", "data": null, "response_meta": { "...": "..." } }
This catches people out in two ways:
- Authenticating on every request. Each call kills the token another request is still using. Cache the token and reuse it.
- Several scripts on one key. Your scanner authenticates at 9:10 AM, your execution
script authenticates at 9:12 AM, and the scanner starts failing with
401. Each account has one app, so authenticate in one place and share the token.
Plan checks on every request
Holding a valid token isn't enough on its own. On every request, Stolo re-checks that the
app's owner still has an active Stolo Pro or Stolo Trial (15 days) plan. If the plan
lapses mid-day, the next call returns 403 with
Active stolo-pro or Stolo Trial 15 days plan required to use this API even though the token is still valid. Renewing the plan
fixes it immediately, no re-authentication needed.
Handling 401s in your code
message | What happened | What to do |
|---|---|---|
No auth token supplied | The Authorization header is missing or empty | Send the header |
Invalid or expired token | The token is malformed, signed for something else, or past 5 AM IST | Authenticate again |
Token no longer valid, please re-authenticate | Someone called /authenticate with this key after you did | Authenticate again, then find the other caller |
A good pattern is to re-authenticate once on a 401 and retry the request. If the retry
also fails with 401, stop and alert. Two processes fighting over the same key will
otherwise keep invalidating each other.
def call(method, path, data):
global token
# GET endpoints (analysis) take query params; POST endpoints take a JSON body.
fields = {"params": data} if method == "GET" else {"json": data}
res = session.request(method, BASE + path, headers={"Authorization": f"Bearer {token}"}, **fields)
if res.status_code == 401:
token = authenticate() # one retry only
res = session.request(method, BASE + path, headers={"Authorization": f"Bearer {token}"}, **fields)
return res
The app secret is effectively your account's API password. Keep it on a server or in a local environment variable, never in front-end code or a public repository. If it leaks, regenerate it on the API App page. The old secret and any token issued with it stop working immediately, and your app key stays the same.