Enable Market Feed API (WebSocket) for My Upstox

I am building an algorithmic trading system and need access to the
LIVE MARKET FEED API (WebSocket) for my Upstox developer app. I have created a python script to fetch nifty 50 call and put data. Here is the code

#!/usr/bin/env python3
import requests
import wcwidth
import time

API_KEY = “”
ACCESS_TOKEN = “”
BASE_URL = “https://api.upstox.com/v2”

UNDERLYING_KEY = “NSE_INDEX|Nifty 50” # Official instrument key for NIFTY index

SLEEP_SEC = 10

def pad(text, width):
text = str(text)
return text + " " * (width - wcwidth.wcswidth(text))

def fetch_option_chain(expiry_date):
url = f"{BASE_URL}/option/chain"
headers = {
“Authorization”: f"Bearer {ACCESS_TOKEN}",
“Accept”: “application/json”
}
params = {
“instrument_key”: UNDERLYING_KEY,
“expiry_date”: expiry_date
}
resp = requests.get(url, headers=headers, params=params)
data = resp.json()

if data.get("status") != "success":
    print("ERROR:", data)
    return []

return data.get("data", [])

def render_dashboard(chain_data):
print(“\n” + “=”*70)
print(f":bar_chart: NIFTY OPTION CHAIN")
print(“=”*70)
header = (
pad(“STRIKE”,10) +
pad(“CE OI”,12) +
pad(“PE OI”,12) +
pad(“PCR”,10)
)
print(header)
print(“-”*70)

for itm in chain_data:
    strike = itm["strike_price"]
    ce_oi = itm["call_options"]["market_data"]["oi"]
    pe_oi = itm["put_options"]["market_data"]["oi"]
    pcr = round(pe_oi/ce_oi,2) if ce_oi > 0 else 0

    print(
        pad(strike,10) +
        pad(ce_oi,12) +
        pad(pe_oi,12) +
        pad(pcr,10)
    )

print("="*70)

def run_engine(expiry_date):
while True:
chain_data = fetch_option_chain(expiry_date)
render_dashboard(chain_data)
time.sleep(SLEEP_SEC)

if name == “main”:
expiry = “2025-02-13” # put your desired expiry
run_engine(expiry)

After running the above script, i can not get any data

Hi @Hitesh_52035976
The v2 WebSocket URL has been discontinued.
Please switch to the v3 WebSocket URL as mentioned in the announcement below:

Thanks!

I am trying to fetch the nifty oi and spot price using the below script but it is not working

#!/usr/bin/env python3

-- coding: utf-8 --

“”"
NIFTY 50 OPTION CHAIN — ADVANCED TERMINAL DASHBOARD
Upstox Market Data Feed V3
Author: Code2DevOps
“”"

import marketdata_pb2

import json, time, uuid, threading, os
import requests, websocket
from collections import defaultdict
from datetime import datetime

from rich.console import Console
from rich.table import Table
from rich.live import Live

from marketdata_pb2 import FeedResponse

MARKET_STATUS_MAP = {
0: “PRE_OPEN_START”,
1: “PRE_OPEN_END”,
2: “NORMAL_OPEN”,
3: “NORMAL_CLOSE”,
4: “CLOSING_START”,
5: “CLOSING_END”,
}

=====================================================

CONFIG

=====================================================

ACCESS_TOKEN = “eyJ0eXAiOiJKV1QiLCJrZXlfaWQiOiJza192MS4wIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiI0U0NNVjMiLCJqdGkiOiI2OTQ0YjhiNjI4ZmNiYTM2NTg4ZDhkZWMiLCJpc011bHRpQ2xpZW50IjpmYWxzZSwiaXNQbHVzUGxhbiI6dHJ1ZSwiaWF0IjoxNzY2MTExNDE0LCJpc3MiOiJ1ZGFwaS1nYXRld2F5LXNlcnZpY2UiLCJleHAiOjE3NjYxODE2MDB9.Y8HVYGsQeC6ZpLheI_0GRuHGG8RXHzuii9LvMFnrV6s”

AUTHORIZE_URL = “https://api.upstox.com/v3/feed/market-data-feed/authorize”

HEADERS = {
“Authorization”: f"Bearer {ACCESS_TOKEN}",
“Accept”: “application/json”
}

NIFTY_SPOT_KEY = “NSE_INDEX|NIFTY 50”
INDIA_VIX_KEY = “NSE_INDEX|INDIA VIX”

console = Console()

=====================================================

STATE

=====================================================

state = {
“market_status”: “UNKNOWN”,
“spot”: None,
“vix”: None,

"call_oi": 0,
"put_oi": 0,
"prev_call_oi": 0,
"prev_put_oi": 0,

"strike_data": defaultdict(lambda: {
    "CE": {"oi": 0, "delta": None, "gamma": None},
    "PE": {"oi": 0, "delta": None, "gamma": None},
}),

"last_update": None

}

=====================================================

HELPERS (CRITICAL)

=====================================================

def safe_ltp(ltpc):
if not isinstance(ltpc, dict):
return None
return ltpc.get(“ltp”) or ltpc.get(“cp”)

def find_ltpc(obj):
“”“Recursively find ltpc anywhere in Upstox V3 feed”“”
if isinstance(obj, dict):
if “ltpc” in obj:
return obj[“ltpc”]
for v in obj.values():
found = find_ltpc(v)
if found:
return found
elif isinstance(obj, list):
for i in obj:
found = find_ltpc(i)
if found:
return found
return None

def decode_protobuf(binary):
feed = FeedResponse()
feed.ParseFromString(binary)
return feed # RETURN RAW PROTO OBJECT

=====================================================

ANALYTICS

=====================================================

def detect_regime(pcr, oi_change):
if state[“market_status”] != “NORMAL_OPEN”:
return “CLOSED”
if 0.9 <= pcr <= 1.1 and abs(oi_change) < 1_000_000:
return “SIDEWAYS”
return “TRENDING”

def gamma_wall():
best = None
score = 0
for strike, d in state[“strike_data”].items():
for side in (“CE”, “PE”):
g = d[side][“gamma”]
oi = d[side][“oi”]
if g and abs(g * oi) > score:
score = abs(g * oi)
best = strike
return best

def build_nifty_option_keys(spot):
“”"
Build CURRENT weekly ATM ±5 strikes
“”"
expiry = “23DEC” # :red_circle: MUST be current weekly expiry

    atm = round(spot / 50) * 50
    keys = []

    for strike in range(atm - 250, atm + 251, 50):
            keys.append(f"NSE_FO|NIFTY{expiry}{strike}CE")
            keys.append(f"NSE_FO|NIFTY{expiry}{strike}PE")

    return keys

=====================================================

WEBSOCKET HANDLERS

=====================================================

def on_open(ws):
console.print(“[green]:check_mark: WebSocket connected (V3)[/green]”)

# -------------------------------
# 1️⃣ Subscribe INDEX feeds
# -------------------------------
ws.send(
    json.dumps({
        "guid": str(uuid.uuid4()),
        "method": "sub",
        "data": {
            "mode": "full",
            "instrumentKeys": [
                NIFTY_SPOT_KEY,
                INDIA_VIX_KEY
            ]
        }
    }).encode(),
    websocket.ABNF.OPCODE_BINARY
)

# -------------------------------
# 2️⃣ Subscribe OPTIONS immediately
# -------------------------------
expiry = "23DEC"          # 🔴 MUST be current weekly expiry
atm    = 26000            # initial fallback ATM

option_keys = []

for strike in range(atm - 250, atm + 251, 50):
    option_keys.append(f"NSE_FO|NIFTY{expiry}{strike}CE")
    option_keys.append(f"NSE_FO|NIFTY{expiry}{strike}PE")

console.print(f"[cyan]Subscribing {len(option_keys)} option contracts[/cyan]")

ws.send(
    json.dumps({
        "guid": str(uuid.uuid4()),
        "method": "sub",
        "data": {
            "mode": "full",
            "instrumentKeys": option_keys
        }
    }).encode(),
    websocket.ABNF.OPCODE_BINARY
)

def on_message(ws, message):

    feed = FeedResponse()
    feed.ParseFromString(message)


    msg_type = feed.type

    # ENUM VALUES (PROTO)
    INITIAL_FEED = 0
    LIVE_FEED    = 1
    MARKET_INFO  = 2

    # -----------------------------------------
    # IGNORE EMPTY SNAPSHOT
    # -----------------------------------------
    if msg_type == INITIAL_FEED:
            return

    # -----------------------------------------
    # MARKET STATUS
    # -----------------------------------------
    if msg_type == MARKET_INFO:
            try:
                    raw = dict(feed.marketInfo.segmentStatus).get("NSE_FO")
                    state["market_status"] = MARKET_STATUS_MAP.get(raw, "UNKNOWN")

                    
            except Exception:
                    state["market_status"] = "UNKNOWN"
            return

    # -----------------------------------------
    # ONLY PROCESS LIVE FEED
    # -----------------------------------------
    if msg_type != LIVE_FEED:
            return

    # ---- continue with your existing logic ----

=====================================================

DASHBOARD

=====================================================

def render_dashboard():
pcr = round(state[“put_oi”] / state[“call_oi”], 2) if state[“call_oi”] else 0
oi_change = (state[“call_oi”] - state[“prev_call_oi”]) + (state[“put_oi”] - state[“prev_put_oi”])
regime = detect_regime(pcr, oi_change)

atm = round(state["spot"] / 50) * 50 if state["spot"] else None
wall = gamma_wall()

table = Table(title="📊 NIFTY OPTION CHAIN — ADVANCED")
table.add_column("Metric")
table.add_column("Value", justify="right")

table.add_row("Market Status", state["market_status"])
table.add_row("Spot", str(state["spot"]))
table.add_row("ATM", str(atm))
table.add_row("PCR", str(pcr))
table.add_row("OI Change", f"{oi_change:+,}")
table.add_row("Regime", regime)
table.add_row("India VIX", str(state["vix"]))
table.add_row("Gamma Wall", str(wall))
table.add_row("Last Update", state["last_update"] or "")

ladder = Table(title="Strike Ladder (±5 ATM)")
ladder.add_column("CE OI")
ladder.add_column("Strike")
ladder.add_column("PE OI")

if atm:
    for s in range(atm - 250, atm + 251, 50):
        d = state["strike_data"].get(s, {})
        ladder.add_row(
            str(d.get("CE", {}).get("oi", "")),
            str(s),
            str(d.get("PE", {}).get("oi", ""))
        )

return table, ladder

=====================================================

MAIN

=====================================================

def main():
console.print(“[cyan]Authorizing market feed (V3)…[/cyan]”)
data = requests.get(AUTHORIZE_URL, headers=HEADERS).json()

if data.get("status") != "success":
    console.print("[red]Authorize failed[/red]", data)
    return

ws_url = data["data"]["authorized_redirect_uri"]
console.print("[green]✔ Authorized[/green]")

ws = websocket.WebSocketApp(
    ws_url,
    on_open=on_open,
    on_message=on_message,
    on_error=lambda ws, e: console.print(f"[red]WS error:[/red] {e}")
)

threading.Thread(target=ws.run_forever, daemon=True).start()

with Live(console=console, refresh_per_second=1) as live:
    while True:
        grid = Table.grid(expand=True)
        table, ladder = render_dashboard()
        grid.add_row(table)
        grid.add_row(ladder)
        live.update(grid)
        time.sleep(1)

if name == “main”:
main()