WebSocket Connected but Not Receiving NIFTY 50 Live or LTP Data

Hi Team,

I’m working on integrating Upstox WebSocket market data into my Flutter app. I followed the official documentation steps:

1. First, I authorize the feed using:

GET https://api.upstox.com/v2/feed/market-data-feed/authorize

Headers:

Authorization: Bearer <access_token>

2. I receive a WebSocket URL in the response (example: wss://…/feed).

3. I successfully connect to this WebSocket URL with my access token.

4. After connecting, I send the subscription message to get NIFTY 50 index LTP updates, something like:

{

“guid”: “some-guid”,

“method”: “sub”,

“data”: {

"mode": "full",

"instrumentKeys": \["NSE_INDEX|Nifty 50"\]

}

}

Problem:

The WebSocket connection is established successfully, but I am not receiving any live updates for NIFTY 50 (LTP or any other fields).

The onOpen event confirms the socket is connected.

No error messages are returned.

No tick data is received after subscription.

Question:

Am I using the correct instrument key for NIFTY 50?

Is there any additional step required after authorization before subscribing?

Do I need a different subscription payload format?

Any guidance would be appreciated

P.S. I understand V2 market feeder is discontinued, however this we have been try since 15 days.

It is working in V3.

I haven’t tested V3, i will do it today and I want to confirm if the method and instrument is fine?

Yes it is fine

@Anupama Please refer for V3 WebSocket example code.

Please do refer for the V3 Websocket connection and subscription limits as well

Thanks

This is V3. Compiles successfully and connects to WebSocket however i dont see any Live Data nor LTP.

Below is the error i get.

Connected to WebSocket:

wss://wsfeeder-api.upstox.com/market-data-feeder/v3/upstox-developer-api/feeds?requestId=953d09e4-dd7b-4220-8cff-c2dc92385e0f&code=PxU4v-2dd7b12a-8816-43f7-9e1c-5168bb0a18bf

Subscribed to NSE_INDEX|Nifty 50

DartError: Bad state: Stream has already been listened to.

@Anupama Please try during live market hours — you should receive updates. It looks like your previous attempt was on Sunday.

Sorry, I took so long to respond back to you. Yes, you’re right. I did check on Sunday. However, I did check on Monday after 9.30 AM to see if I get the live data. I am not. So what’s happening is that I get successfully authorized using the token that I generate. I see the message. And second thing, it’s successfully connecting to WSS, the WebSocket. And lastly, you know, even the subscription is fine. However, I’m not getting a live data. So live feed is not coming into my application nor the web interface that I use for testing. So my question to you is, do we need an additional plan, like a paid plan in order to have a live data? Or the basic free plan that we have with Upstox should work. And if you want to look at the code, I’ll be more than happy to send you the code. And yes, we are using the proto, the proto buff and all that. I think you will see that in the code if you’re interested.

I to stuck with the same issue since last few days.

@Anupama The normal connection and subscription limits are included under the Basic/Free plan. To increase these limits and D30 subscription mode, a Plus plan is required. If you’re still facing issues, please feel free to share your code for further investigation.

Here is the code

// lib/index_websocket_service.dart
import ‘dart:convert’;
import ‘dart:io’;
import ‘dart:typed_data’;

import ‘proto/marketdatafeed.pb.dart’;

typedef PriceCallback = void Function(String instrumentKey, double ltp);

class IndexWebSocketService {
final String accessToken;
WebSocket? _socket;

/// Called when we extract an LTP tick: (instrumentKey, ltp)
PriceCallback? onPriceUpdate;

IndexWebSocketService({required this.accessToken});

/// Full flow: authorize → connect → subscribe → decode
Future connect({required String instrumentKey}) async {
// 1) Authorize: GET to obtain WSS URL
final client = HttpClient();
final authUri = Uri.parse(
https://api.upstox.com/v3/feed/market-data-feed/authorize’,
);

HttpClientRequest req;
try {
  req = await client.getUrl(authUri);
} catch (e) {
  throw Exception('Network error creating auth request: $e');
}

req.headers.set('Authorization', 'Bearer $accessToken');

final authResp = await req.close();
final authBody = await utf8.decoder.bind(authResp).join();

print('Authorize status: ${authResp.statusCode}');
// debug print small portion for visibility
print('Authorize body (truncated): ${authBody.length > 300 ? authBody.substring(0, 300) + "..." : authBody}');

if (authResp.statusCode != 200) {
  throw Exception('Authorize failed: ${authResp.statusCode} - $authBody');
}

dynamic authJson;
try {
  authJson = jsonDecode(authBody);
} catch (e) {
  throw Exception('Authorize returned non-JSON: $e');
}

// Try common fields for WSS URL
final feedUrl = authJson['data']?['authorizedRedirectUri'] ??
    authJson['data']?['wss_url'] ??
    authJson['data']?['wssUrl'] ??
    authJson['authorizedRedirectUri'] ??
    authJson['wss_url'];

if (feedUrl == null) {
  throw Exception('No WSS URL found in authorize response: $authJson');
}

print('Connecting to WSS URL: $feedUrl');

// 2) Connect to WSS with header
try {
  _socket = await WebSocket.connect(
    feedUrl,
    headers: {
      'Authorization': 'Bearer $accessToken',
      'apiVersion': '3.0',
    },
  );
} catch (e) {
  throw Exception('WebSocket.connect failed: $e (feedUrl: $feedUrl)');
}

print('WebSocket connected; sending subscription for $instrumentKey');

// 3) Subscribe
final subMsg = {
  'guid': 'sub-${DateTime.now().millisecondsSinceEpoch}',
  'method': 'sub',
  'data': {
    'mode': 'full',
    'instrumentKeys': [instrumentKey],
  },
};
_socket!.add(jsonEncode(subMsg));

// 4) Listen and decode (binary or JSON-with-base64)
_socket!.listen((dynamic event) {
  try {
    if (event is String) {
      _handleTextFrame(event, instrumentKey);
    } else if (event is List<int>) {
      _handleBinaryFrame(Uint8List.fromList(event));
    } else if (event is ByteBuffer) {
      _handleBinaryFrame(event.asUint8List());
    } else {
      print('Unknown WS event type: ${event.runtimeType}');
    }
  } catch (e, st) {
    print('Error processing WS event: $e\n$st');
  }
}, onError: (err) {
  print('WebSocket error: $err');
}, onDone: () {
  print('WebSocket closed by server');
});

}

void _handleTextFrame(String text, String instrumentKey) {
// Some servers send JSON with base64-encoded proto in data.feeds[INST].marketFF
try {
final jsonMsg = jsonDecode(text);
final data = jsonMsg[‘data’];
if (data == null) return;

  final feeds = data['feeds'];
  if (feeds != null) {
    var feedEntry = feeds[instrumentKey];
    if (feedEntry == null) {
      // sometimes instrument key uses underscores or slightly different format
      feedEntry = feeds[instrumentKey.replaceAll('|', '_')];
    }
    if (feedEntry != null) {
      // If base64 market payload found
      final base64Str = feedEntry['marketFF'] ??
          feedEntry['marketFullFeed'] ??
          feedEntry['market_full_feed'] ??
          feedEntry['market'];
      if (base64Str is String && base64Str.isNotEmpty) {
        try {
          final bytes = base64Decode(base64Str);
          _handleBinaryFrame(Uint8List.fromList(bytes));
          return;
        } catch (_) {
          // not base64 -> continue
        }
      }

      // If JSON already contains ltpc object:
      final ltpc = feedEntry['ltpc'];
      if (ltpc != null && ltpc['ltp'] != null) {
        final ltpVal = (ltpc['ltp'] as num).toDouble();
        onPriceUpdate?.call(instrumentKey, ltpVal);
        return;
      }
    }
  }
} catch (_) {
  // ignore non-json or unexpected
}

}

void _handleBinaryFrame(Uint8List bytes) {
// PRIMARY: FeedResponse.fromBuffer (map of instrumentKey->Feed)
try {
final feedResp = FeedResponse.fromBuffer(bytes);
// feedResp.feeds is PbMap<String, Feed> — iterate
feedResp.feeds.forEach((instrKey, feed) {
// Use dynamic access to avoid compile-time mismatch on field names
final dynFeed = feed as dynamic;
double? ltp;

    // 1) Try marketFF -> ltpc -> ltp
    try {
      final mff = dynFeed.marketFF;
      if (mff != null) {
        final ltpc = mff.ltpc;
        if (ltpc != null && ltpc.ltp != null) {
          final v = ltpc.ltp;
          ltp = (v is num) ? v.toDouble() : double.tryParse(v.toString());
        }
      }
    } catch (_) {}

    // 2) Try indexFF / marketFullFeed variants
    if (ltp == null) {
      try {
        final alt = dynFeed.indexFF ?? dynFeed.marketFullFeed ?? dynFeed.market_ff;
        if (alt != null) {
          final ltpc = alt.ltpc;
          if (ltpc != null && ltpc.ltp != null) {
            final v = ltpc.ltp;
            ltp = (v is num) ? v.toDouble() : double.tryParse(v.toString());
          }
        }
      } catch (_) {}
    }

    // 3) Try feed.ltpc directly
    if (ltp == null) {
      try {
        final ltpc = dynFeed.ltpc;
        if (ltpc != null && ltpc.ltp != null) {
          final v = ltpc.ltp;
          ltp = (v is num) ? v.toDouble() : double.tryParse(v.toString());
        }
      } catch (_) {}
    }

    if (ltp != null) {
      onPriceUpdate?.call(instrKey, ltp);
    } else {
      // helpful debug output for you — paste this if no ticks appear
      try {
        final j = dynFeed.writeToJson();
        print('Decoded Feed (no ltp found) for $instrKey: $j');
      } catch (_) {
        print('Decoded Feed for $instrKey (could not JSONize)');
      }
    }
  });
  return;
} catch (_) {
  // Not FeedResponse — try MarketFullFeed directly
}

// FALLBACK: try MarketFullFeed directly
try {
  final mff = MarketFullFeed.fromBuffer(bytes);
  final ltp = mff.ltpc?.ltp;
  if (ltp != null) {
    onPriceUpdate?.call('', (ltp is num) ? ltp.toDouble() : double.tryParse(ltp.toString())!);
  } else {
    try {
      print('MarketFullFeed decoded but no ltp: ${mff.writeToJson()}');
    } catch (_) {}
  }
  return;
} catch (_) {}

// Nothing matched
print('Binary decode unknown message (len=${bytes.length})');

}

Future disconnect({String? instrumentKey}) async {
if (_socket != null && instrumentKey != null) {
final unsub = {
‘guid’: ‘unsub-${DateTime.now().millisecondsSinceEpoch}’,
‘method’: ‘unsub’,
‘data’: {
‘mode’: ‘full’,
‘instrumentKeys’: [instrumentKey],
}
};
try {
socket!.add(jsonEncode(unsub));
} catch (
) {}
}
try {
await socket?.close();
} catch (
) {}
_socket = null;
}
}

In .Net I was facing same issue for V3 socket and at last found solution.

Previously I was sending message as WebSocketMessageType.Text at that time I was getting market info only, after that no data receiving so after lots of research I tried Binary instead of Text and started getting live tick data

1 Like

Thank you for your response. I checked and made the changes to Binary and I still dont the Live Tick.

Authorize status: 200
Authorize body (truncated): {“status”:“success”,“data”:{“authorizedRedirectUri”:“wss://wsfeeder-api.upstox.com/market-data-feeder/v3/upstox-developer-api/fe
eds?requestId=36fc1942-45ff-4534-bbab-fd8b4d6f5519&code=t95yO-cc922d2e-6daf-48a0-945a-ee2949e2b28e”,“authorized_redirect_uri”:"wss://wsfeeder-api.upstox.com/market-data-fee…
Connecting to WSS URL: wss://wsfeeder-api.upstox.com/market-data-feeder/v3/upstox-developer-api/feeds?requestId=36fc1942-45ff-4534-bbab-fd8b4d6f5519&code=t95yO-cc922d2e-6daf-48a0-945a-ee2949e2b28e
WebSocket connected; sending subscription for NSE_INDEX|Nifty 50
Syncing files to device Windows… 338ms

Can someone please respond? It’s been over a month since I reached out to the community upstox, and I still don’t have any proper response. I understand that this is something that we need to look at closely, but quite honestly, I’ve done everything I can, referring to the sample code that you’ve given us, all the documentation, everything.

It’s successfully making a WebSocket connection, and the instrument keys are fine, but somehow we’re not seeing the tick. We’re not seeing the live data at all. So I’m really, really not sure what to do next because I’m quite honestly exhausted. So could someone please look at the code that I pasted above and tell me what is wrong that I’m doing? Or, you know, I have updated code yesterday, and we tried today as well, made some changes with regards to binary and all that, but we still don’t see the live data. Could someone please help?

I am sure it will be a simple explanation. Appreciate your help and time.

Hi Anand

For real-time tick data, you need to use full or full_d30. Is this statement true? Do i have to use a Plus plan? I am looking just for LTP but i am looking for Live Data maybe for 2 instruments. Nifty50 and BANKNIFTY.

Hi @Anupama, If you only need LTP (Last Traded Price) , you can simply subscribe in ltpc mode , which is available in the basic plan.

The Plus plan is only required if you want to increase connection and subscription limits, or use full_d30 mode .

Refer for more details on plus plan Websocket Plus Features | Upstox Developer API

Thanks

Hey @Anupama - I am also facing the same issue. How did you managed to fix it?

Managed to get it worked based on sample code