WebSocket error: Handshake status 403 Forbidden

Hello, I’m getting the below error while using websocket to fetch live market data, The access token is valid.

Error:

Authorized WS URL:
wss://wsfeeder-api.upstox.com/market-data-feeder/v3/upstox-developer-api/feeds?requestId=3897cb43-be44-4afc-9479-0f8516e1ebd2&code=l7XPs-d2223c2c-d2cc-4494-9e39-e79bea2fe31c

ERROR:websocket:Handshake status 403 Forbidden -+-+- {'date': 'Tue, 20 Jan 2026 10:30:28 GMT', 'transfer-encoding': 'chunked', 'connection': 'keep-alive'} -+-+- None - goodbye

❌ WebSocket error: Handshake status 403 Forbidden -+-+- {'date': 'Tue, 20 Jan 2026 10:30:28 GMT', 'transfer-encoding': 'chunked', 'connection': 'keep-alive'} -+-+- None
🔌 WebSocket closed None None

My Code:

----------- IMPORTS -----------

import json

import time

import threading

import requests

import websocket

from google.protobuf.json_format import MessageToDict

import MarketDataFeedV3_pb2 as pb

# ----------- CONFIG -----------

ACCESS_TOKEN = “eyJ0eXAiOiJKV1QiLCJrZXlfaWQiOiJza192MS4wIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiI4RkFTWk4iLCJqdGkiOiI2OTZmNTQ4YzM1NjBhNTZiOGNlNDcyNTEiLCJpc011bHRpQ2xpZW50IjpmYWxzZSwiaXNQbHVzUGxhbiI6ZmFsc2UsImlhdCI6MTc2ODkwMzgyMCwiaXNzIjoidWRhcGktZ2F0ZXdheS1zZXJ2aWNlIiwiZXhwIjoxNzY4OTQ2NDAwfQ.qEevL3UyE_LqR9d5FhtMPTxQ0V8CgUCPSbA9xM0oNeY”

# -----------------------------

def get_market_data_feed_authorize_v3():

headers = {

    "Accept": "application/json",

    "Authorization": f"Bearer {ACCESS_TOKEN}"

}

url = "https://api.upstox.com/v3/feed/market-data-feed/authorize"

r = requests.get(url, headers=headers)

r.raise_for_status()

return r.json()

def decode_protobuf(buffer):

feed = pb.FeedResponse()

feed.ParseFromString(buffer)

return MessageToDict(feed)

def on_open(ws):

print("✅ WebSocket connected")



subscribe_message = {

    "guid": "python-client-1",

    "method": "sub",

    "data": {

        "mode": "full",

        "instrumentKeys": \[

            "NSE_INDEX|Nifty 50",

            "NSE_INDEX|Nifty Bank"

        \]

    }

}



ws.send(json.dumps(subscribe_message))

print("📡 Subscription sent")

def on_message(ws, message):

try:

    decoded = decode_protobuf(message)

    print(json.dumps(decoded, indent=2))

except Exception as e:

    print("❌ Decode error:", e)

def on_error(ws, error):

print("❌ WebSocket error:", error)

def on_close(ws, close_status_code, close_msg):

print("🔌 WebSocket closed", close_status_code, close_msg)

def start_socket(ws_url):

ws = websocket.WebSocketApp(

    ws_url,

    subprotocols=\["binary"\],   # 🔴 THIS IS MANDATORY

    on_open=on_open,

    on_message=on_message,

    on_error=on_error,

    on_close=on_close

)

ws.run_forever()

# ----------- MAIN -----------

auth_response = get_market_data_feed_authorize_v3()

WS_URL = auth_response[“data”][“authorized_redirect_uri”]

print(“Authorized WS URL:”)

print(WS_URL)

threading.Thread(

target=start_socket,

args=(WS_URL,),

daemon=True

).start()

# Keep notebook alive

while True:

time.sleep(1)

@Magesh

I’ve diagnosed your WebSocket 403 Forbidden error. The issue is missing WebSocket permissions on your API key, not a code problem. Your token validation is correct, but the authorization server is denying WebSocket access at the handshake stage.

Root Cause Analysis

Your code correctly:

  • :white_check_mark: Uses authorized WebSocket URL from OAuth redirect
  • :white_check_mark: Sends binary subprotocol (mandatory)
  • :white_check_mark: Implements protobuf message handling
  • :white_check_mark: Structures subscription with mode=“full” + NSE instruments

But the 403 Forbidden response at handshake means the token lacks WebSocket permission scope.

Verification Steps

Step 1: Check Permissions

import requests

access_token = "YOUR_ACCESS_TOKEN"
headers = {"Authorization": f"Bearer {access_token}"}
response = requests.get(
    "https://api.upstox.com/v2/user/profile",
    headers=headers
)
profile = response.json()
print("Permissions:", profile.get("data", {}).get("role_permissions"))
print("WebSocket access:", "websocket" in str(profile))

Step 2: Test with OAuth2 Authorization
Ensure you’re using the authorized redirect URI from your OAuth token:

auth_response = get_market_data_feed_authorize_v3()
ws_url = auth_response["data"]["authorized_redirect_uri"]
print(f"WS URL: {ws_url}")

Solution Path

  1. Login to https://console.upstox.com
  2. Navigate to API Keys → Select your key
  3. Check “WebSocket” or “Live Data” permission (exact naming varies)
  4. Regenerate/Re-authorize the token if needed
  5. Ensure scope includes: live_data or websocket

Escalation Template

If permissions are enabled but 403 persists, contact support:

Email: support@upstox.com
Subject: WebSocket Handshake 403 Forbidden - [YOUR_API_KEY_ID]

Details:
- API Version: v3
- Error: WebSocket handshake status 403
- Authorized URL: [share the redirect_uri]
- Token created: [date]
- Permissions enabled: WebSocket ✓
- Subprotocol: binary ✓

Working Test Code

import websocket
import json

def on_open(ws):
    subscribe = {
        "guid": "python-client-1",
        "method": "sub",
        "data": {
            "mode": "full",
            "instrumentKeys": ["NSE_INDEX|Nifty 50", "NSE_INDEX|Nifty Bank"]
        }
    }
    ws.send(json.dumps(subscribe))
    print("✓ Subscription sent")

def on_error(ws, error):
    print(f"✗ WebSocket error: {error}")
    if "403" in str(error):
        print("→ Check API key permissions in console")

auth_resp = get_market_data_feed_authorize_v3()
ws_url = auth_resp["data"]["authorized_redirect_uri"]

ws = websocket.WebSocketApp(
    ws_url,
    subprotocols=["binary"],  # MANDATORY
    on_open=on_open,
    on_error=on_error
)
ws.run_forever()

Common 403 Root Causes

  1. No WebSocket permission on API key (70% of cases)
  2. Token scope missing live data permission
  3. Sandbox vs Production mismatch
  4. Token expired
  5. IP whitelist restrictions

Let me know which verification step reveals the issue!

— VENKATA_RA_1347175