1. Get an API key
In the app, go to Account β API keys β New key and choose the scopes your client needs:
- Read top picks β the
GET /v2/top-picksand per-pickGET /v2/picks/{id}/researchendpoints. - Read notifications β
GET /v2/events+ the WebSocket stream.
Copy the full secret when it's shown β it starts with fk_live_ and is displayed once. Optionally lock the key to your server's IP(s), and set an expiry. Your plan must include API access.
2. Authenticate
Send the key as a bearer token on every REST call:
curl -H "Authorization: Bearer fk_live_xxxxxxxxxxxx" \
"https://dev.api.qortle.ai/v2/top-picks?universe=NIFTY50&mode=Swing"For the WebSocket, pass it in the query string:
wss://dev.api.qortle.ai/v1/ws/notifications?api_key=fk_live_xxxxxxxxxxxx3. Read today's picks
GET https://dev.api.qortle.ai/v2/top-picks?universe=NIFTY50&mode=Swing&limit=5Each item carries the symbol, recommendation, direction and the trade plan (entry / stop-loss / targets) where available. Use mode=ALL / universe=ALL to aggregate every cell server-side.
3b. Read the research report
Every pick carries an RA research report β the reasoning behind the call β so you (or your end user) can apply your own rules before acting. Fetch the machine-readable view with the same picks.read key:
GET https://dev.api.qortle.ai/v2/picks/{pick_id}/researchReturns the instrument, the recommendation with numeric levels ({entry, stop_loss, targets}), the structured rationale and risk, and the mandatory SEBI disclosures. The full signed document stays available at /v2/picks/{pick_id}/report.html and .pdf.
4. Stream notifications
Connect the WebSocket and read JSON frames:
{"type":"connected","user_id":"β¦"}β stream open.{"type":"notification","notification":{β¦}}β a live trade-signal event.{"type":"pong"}β reply to a keepalive containingping.
One live stream per key β a second concurrent connection is closed with code 4409. Other close codes: 4401 unauthenticated, 4402 plan lacks API access, 4403 missing scope.
5. Never miss an exit (replay)
The WebSocket is low-latency push; the guaranteed source of truth is the cursor-paged replay endpoint:
GET https://dev.api.qortle.ai/v2/events?since=<cursor>&limit=100Start with no since (or your last saved cursor), process each event, then pass the response's next_cursor as the next since. Keep polling while has_more is true. Delivery is at-least-once β dedupe on id. Event types: PICK_NEW, PICK_ENTRY_NEAR, POSITION_SL_HIT, POSITION_TP_HIT, POSITION_CLOSED.
On (re)connect you can also fold replay into the stream with ?since=<cursor>; the stream emits the missed events, then {"type":"replay_complete"}, before going live.
6. Limits & errors
| Status | Meaning |
|---|---|
401 | Missing / malformed / expired / revoked key. |
402 | Your plan doesn't include API access. |
403 | Key lacks the scope this endpoint needs. |
429 | Rate / daily limit β see Retry-After and X-RateLimit-Limit. |
One key per client; keep the secret server-side; rotate before expiry.
7. Minimal Python client
import requests, websocket # pip install requests websocket-client
BASE = "https://dev.api.qortle.ai"
KEY = "fk_live_xxxxxxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}
# pull today's picks
picks = requests.get(f"{BASE}/v2/top-picks",
params={"universe": "NIFTY50", "mode": "Swing"},
headers=H, timeout=10).json()
# catch up on missed events, tracking a cursor
cursor = None
while True:
r = requests.get(f"{BASE}/v2/events",
params={"since": cursor} if cursor else {},
headers=H, timeout=10).json()
for ev in r["events"]:
handle(ev) # dedupe on ev["id"]
cursor = r["next_cursor"]
if not r["has_more"]:
break
# live stream, replaying from the cursor on connect
ws = websocket.create_connection(
f"{BASE.replace('https','wss')}/v1/ws/notifications"
f"?api_key={KEY}" + (f"&since={cursor}" if cursor else ""))8. How to run the code
Save the reference client as qortle_client.py (the snippet above is the core; the full client adds typed errors, cursor persistence, WebSocket reconnect, and the basket command in step 9). Then:
1. Install the two dependencies
pip install requests websocket-client2. Set your API key
export QORTLE_API_KEY=fk_live_xxxxxxxxxxxx3. Read picks and catch up on events (no stream)
python qortle_client.py --universe NIFTY50 --mode Options --no-stream4. Or hold the live stream open
python qortle_client.py --universe NIFTY50 --mode OptionsYou'll see today's picks (empty outside market hours, 09:15β15:30 IST), then events paging by cursor, then live frames. If a call errors, the status code tells you which gate hit β see step 6 (401 key, 402 plan, 403 scope, 429 rate limit).
9. Place picks in your own broker (optional)
Prefer review-and-place over full automation? The reference client can turn today's picks into a pre-filled order basket that you open and confirm in your own broker (Zerodha today). Qortle does not place the order β your broker executes only after you review and tap Place, and no broker credentials are used or stored.
QORTLE_API_KEY=fk_live_β¦ QORTLE_KITE_PUBLISHER_KEY=your_kite_publisher_key \
python qortle_client.py --basket --universe NIFTY50 --mode Options \
--capital-per-trade 20000 --basket-out basket.html
# open basket.html β review in your own Zerodha account β placeSizing is your choice: --qty, --capital-per-trade, or --risk-per-trade. Orders are LIMIT at the recommended entry by default; stop-loss and targets are shown for you to set yourself. A pick that has already hit its stop-loss or target is no longer placeable.
Community, forum & updates
Build with others: real-time chat on Discord, async Q&A and feature requests on GitHub Discussions, and the open-source SDK.
Community channels are for building with the API β please keep trading tips, solicitation and personalized advice out of them.