Guide
Getting started
Create an API key, pick its scopes and make your first Cirrus API request in a few minutes.
The Cirrus API lets your code do what you do in the app: place orders with your broker, protect positions, run strategies and get told when something happens. It is plain HTTPS and JSON.
| What | URL |
|---|---|
| REST API | https://cirrus.pocketful.in |
| Live stream (WebSocket) | wss://cirrus.pocketful.in/v1/stream |
1. Create an API key #
- Sign in and open API access.
- Choose Create key, give it a name and tick only the scopes you need.
- Optionally limit it to your server’s IP addresses.
- Copy the key and secret now: the secret is shown only once.
2. Pick scopes #
| Scope | Lets the key |
|---|---|
read | Read accounts, orders, positions, holdings, funds, triggers and the live stream. |
orders | Place, modify and cancel orders, bracket orders and GTTs; convert positions. |
triggers | Create, change and cancel alerts, stop-losses, targets and loss limits. |
strategies, strategies_live, strategies_signal | Automated strategies: build and backtest, run live, send signals. API keys cannot carry these yet; use the app for now. |
Scopes do not include each other: a key with orders but not read cannot read the order book. A missing scope answers 403 forbidden naming the scope.
3. Make your first request #
Put the key in an environment variable, then send it as Authorization: token <key>:<secret> on every request. This asks who the key belongs to:
export CIRRUS_API_KEY='ck_xxxxxxxx:cs_xxxxxxxx'curl "https://cirrus.pocketful.in/v1/user/profile" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v1/user/profile",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/user/profile', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"username": "user-01M3PGPS465224YW0C5Z5DBNFB",
"auth": {
"kind": "api_key",
"key_id": "ck_sVOtcTpQ3Y5d6VIRAE9Q",
"name": "Reporting",
"scopes": [
"read"
],
"ip_allowlist": [
"203.0.113.7"
]
}
},
"error": null
}Then list your linked broker accounts. You pass an account id like P1 when you place orders.
curl "https://cirrus.pocketful.in/v1/accounts" \
-H "Authorization: token $CIRRUS_API_KEY"import os
import requests
response = requests.get(
"https://cirrus.pocketful.in/v1/accounts",
headers={
"Authorization": f"token {os.environ['CIRRUS_API_KEY']}",
},
timeout=10,
)
print(response.status_code, response.json())const response = await fetch('https://cirrus.pocketful.in/v1/accounts', {
method: 'GET',
headers: {
Authorization: `token ${process.env.CIRRUS_API_KEY}`,
},
});
const body = await response.json();
console.log(response.status, body);{
"status": "success",
"data": {
"items": [
{
"account": "5P1",
"broker": "5paisa",
"broker_name": "5paisa",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "login_required",
"missing": [
{
"field": "static_ip",
"label": "Static IP",
"required": false
}
],
"updates": "stream",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": false,
"cover": false,
"gtt": false,
"gtt_two_leg": false
},
"warnings": []
},
{
"account": "MO1",
"broker": "motilaloswal",
"broker_name": "Motilal Oswal",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "login_required",
"missing": [
{
"field": "static_ip",
"label": "Static IP",
"required": false
}
],
"updates": "stream",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": false,
"cover": false,
"gtt": false,
"gtt_two_leg": false
},
"warnings": []
},
{
"account": "P1",
"broker": "paper",
"broker_name": "Paper",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "ready",
"missing": [],
"updates": "postback",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": true,
"cover": true,
"gtt": true,
"gtt_two_leg": true
},
"warnings": []
},
{
"account": "P2",
"broker": "paper",
"broker_name": "Paper",
"tag": null,
"multiplier": 2,
"supported": true,
"status": "ready",
"missing": [],
"updates": "postback",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": true,
"cover": true,
"gtt": true,
"gtt_two_leg": true
},
"warnings": []
},
{
"account": "PF1",
"broker": "pocketful",
"broker_name": "Pocketful",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "login_required",
"missing": [],
"updates": "stream",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": true,
"cover": true,
"gtt": true,
"gtt_two_leg": false
},
"warnings": []
},
{
"account": "TJ1",
"broker": "tradejini",
"broker_name": "Tradejini",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "ready",
"missing": [
{
"field": "static_ip",
"label": "Static IP",
"required": false
}
],
"updates": "stream",
"static_ip": null,
"last_login_at": null,
"session_expires_at": "2026-10-12T11:31:53Z",
"seat_assigned": null,
"last_update_at": "2026-10-12T10:31:53.299825Z",
"protection": {
"bracket": true,
"cover": true,
"gtt": true,
"gtt_two_leg": true
},
"warnings": []
},
{
"account": "U1",
"broker": "upstox",
"broker_name": "Upstox",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "login_required",
"missing": [],
"updates": "stream",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": false,
"cover": false,
"gtt": false,
"gtt_two_leg": false
},
"warnings": []
},
{
"account": "Z1",
"broker": "zerodha",
"broker_name": "Zerodha",
"tag": null,
"multiplier": 1,
"supported": true,
"status": "setup_incomplete",
"missing": [
{
"field": "api_key",
"label": "API Key",
"required": true
},
{
"field": "static_ip",
"label": "Static IP",
"required": false
}
],
"updates": "stream",
"static_ip": null,
"last_login_at": null,
"session_expires_at": null,
"seat_assigned": null,
"last_update_at": null,
"protection": {
"bracket": false,
"cover": false,
"gtt": true,
"gtt_two_leg": true
},
"warnings": []
}
]
},
"error": null
}Every response looks the same #
Success is {"status": "success", "data": …, "error": null}. An error keeps the shape and fills error: branch on error.code, show error.message to people. See Errors.
{
"status": "error",
"data": null,
"error": {
"code": "invalid_request",
"message": "price must be > 0 for LIMIT orders",
"field": "orders[0].price"
}
}