1. Getting Started
Prerequisites
Before connecting to our WebSocket API, ensure you have the following:
Paid Direct Subscribers (via our website)
If you've subscribed to a paid plan directly through our website, you can use the same API key from your dashboard for both the REST API and WebSocket API. No extra steps needed — skip the /v2/websocket-key endpoint entirely.
Free Website Plan
Free website API keys are for REST API access only. Upgrade to a paid website plan before connecting to the WebSocket endpoints.
RapidAPI Subscribers Only
If you subscribed via RapidAPI, you must obtain a separate WebSocket API key by calling the /v2/websocket-key endpoint. This key is different from your RapidAPI key and expires after one week. See Section 7: API Key Rotation for details.
- API Key - Your paid dashboard API key (direct subscribers) or a WebSocket key from
/v2/websocket-key(RapidAPI subscribers) - WebSocket Client Library - Choose one that supports automatic reconnection and retry logic
- Connection Settings - Configure appropriate timeouts and implement ping/pong for stability
Connecting to the Server
Our WebSocket API provides two specialized endpoints for different types of financial data.
Market Data
wss://realtime.insightsentry.com/liveReal-time quotes, time series data, and tick data
News Feed
wss://realtime.insightsentry.com/newsfeedLatest financial news and market updates
HTTP/1.1 Required
Our WebSocket endpoints currently only accept connections over HTTP/1.1. HTTP/2 and HTTP/3 are not supported. Most WebSocket client libraries default to HTTP/1.1, but if you use a custom HTTP client, make sure it negotiates HTTP/1.1 for the upgrade request.
Authentication
Both endpoints require an API key. If you subscribed to a paid plan directly via our website, use your dashboard API key. Free website API keys are REST-only. If you subscribed via RapidAPI, use the WebSocket key obtained from /v2/websocket-key. The authentication format varies by endpoint:
Market Data Authentication
Combine authentication with your subscription request (covered in the next section).
News Feed Authentication
Send only your API key in this simplified format:
{
"api_key": "<your_websocket_api_key>"
// No subscriptions needed for news feed
}News Feed Instant Access
When you connect to /newsfeed, the server automatically sends the 10 most recent news items. This gives you immediate access to current news without additional requests.
Connection Lifecycle & Limits
When you connect to /live, the server immediately sends a greeting message before you authenticate:
{"message": "Connecting...", "server": "<server_location>"}Treat this as an informational handshake signal — it is not an error. After receiving it, send your subscription message to authenticate.
- Initial request timeout: You must send a valid subscription message within 5 minutes of connecting. Idle connections that never authenticate are closed automatically.
- Maximum message size: Inbound messages larger than 500,000 characters are rejected. Keep your subscriptions array within reasonable bounds.
- Connection limit:You can keep up to your plan's allowed concurrent connections open per account. If you exceed it, the server will evict your oldest connection (sending a
connection_evictederror) rather than rejecting the new one — but this only happens when your client is clearly misbehaving. See Section 6 for details.
2. Subscribing to Data Feeds
Initial Subscription
After connecting to the market data endpoint, send a JSON message to authenticate and subscribe to data feeds.
This single message handles both authentication and your initial symbol subscriptions.
Required Message Format:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
// Array of subscription objects
]
}Subscription Parameters
Each subscription object in the array can include these parameters:
Required Parameters
code- Symbol identifier (e.g.,"NASDAQ:AAPL","BINANCE:BTCUSDT")
Data Type Selection
type- Data feed type:"series"(default) or"quote"
Tip:Since "series" is default, you can omit this parameter for series data
Series Data Parameters (when type="series")
bar_type- Time interval:"tick","second","minute","hour","day", or"week"bar_interval- Number of units per bar. Allowed values depend onbar_type:"second":1, 5, 10, 15, 30, 45"tick":1, 10, 100, 1000"minute"and above: any positive integer (e.g., 1, 5, 15, 60)
currency- Optional. Convert price values to the given currency (e.g.,"USD","EUR"). Unknown codes are rejected.unit- Optional. Override the instrument unit for display (e.g.,"BTC"). Unknown codes are rejected.extended- Non-futures only. Include extended hours (default: true) - details belowsplit- Non-futures only. Apply split adjustment (default: true) - details belowdadj- Non-futures only. Apply dividend adjustment (default: false). Ignored whensplitisfalse- details belowbadj- Futures only. Apply back-adjustment for continuous futures contracts (default: false) - details belowsettlement- Futures only. Use settlement price as close (default: false) - details belowmax_dp- Number of initial historical data points to receive on connect/reconnect (1-30,000). Default: 1. - details below
Example Subscription Message:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "NASDAQ:AAPL", "type": "series", "bar_type": "minute", "bar_interval": 1, "max_dp": 100},
{"code": "NASDAQ:TSLA", "type": "quote"}
]
}Modifying Subscriptions
You can update your subscriptions without disconnecting from the WebSocket.
Send a new subscription message with your complete desired list. The server replaces all previous subscriptions with the new ones.
Complete Replacement
Each new subscription message completely replaces your current subscriptions. Include all symbols you want to continue receiving data for.
Example - Updating Subscriptions:
To change from the previous example to track both minute bars and quotes for AAPL:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "NASDAQ:AAPL", "type": "series", "bar_type": "minute", "bar_interval": 1, "max_dp": 100},
{"code": "NASDAQ:AAPL", "type": "quote"}
// NASDAQ:TSLA quote subscription is removed as it's not in the new list
]
}Subscription Rules & Limits
Follow these important guidelines when managing your subscriptions:
Required Authentication
Every subscription message must include your valid api_key.
Rate Limiting
Avoid sending more than 120 subscription messages per 3 hours. The server does not drop your messages when you exceed this — instead, it adds small progressive delays to request processing. Rapid reconnections from the same account are throttled the same way to prevent reconnection storms from misbehaving clients.
Note: Throttling only affects new messages and reconnections you initiate. Your existing data feed continues uninterrupted, and data you receive has no rate limit.
Empty Subscriptions
You cannot send an empty subscriptions array. To stop all data, disconnect the WebSocket and reconnect when needed.
Multiple Symbols
Subscribe to multiple symbols in one message by adding multiple objects to the subscriptions array (subject to your plan limits).
3. Response Data Formats
The server sends real-time data updates as JSON messages. The format varies based on your subscription type.
For complete field descriptions, see the corresponding REST API documentation: /symbols/:symbol/series or /symbols/quotes.
Series Data (type: "series")
Series data provides OHLCV (Open, High, Low, Close, Volume) bar information and tick data.
Real-time Updates
Regardless of your chosen bar_type and bar_interval, you receive updates whenever the close price or volume changes within the current bar period.
Example: With bar_type: "hour", you get real-time updates throughout the hour, not just once per hour.
Update Frequency by Bar Type
Data delivery varies based on your bar_type subscription:
bar_type: "tick") - Data pushed for every individual tradebar_type: "second") - Data pushed only when close price or volume changesbar_type: "minute" or above) - Data pushed when price/volume changes AND when new bar periods start{
"code": "NASDAQ:AAPL",
"bar_end": 1733432399.0,
"last_update": 1733432399820,
"bar_type": "1m",
"series": [
{
"time": 1733432340.0,
"open": 242.89,
"high": 243.09,
"low": 242.82,
"close": 243.08,
"volume": 533779.0
}
]
}Quote Data (type: "quote")
Quote data provides real-time market information including current prices, trading volume, and bid/ask spreads.
This data type is ideal for monitoring current market conditions and building trading interfaces.
{
"last_update": 1757061265540,
"total_items": 1,
"data": [
{
"code": "NASDAQ:AAPL",
"status": "PRE",
"lp_time": 1757061117.0,
"volume": 47549429.0,
"last_price": 239.42,
"change_percent": -0.15,
"change": -0.36,
"ask": 239.47,
"bid": 239.42,
"ask_size": 2.0,
"bid_size": 1.0,
"prev_close_price": 238.47,
"open_price": 238.45,
"low_price": 236.74,
"high_price": 239.8999,
"market_cap": 3558428648118.0,
"currency_code": "USD",
"delay_seconds": 0
}
]
}Understanding delay_seconds
The delay_seconds field indicates the data delay in seconds:
0- Real-time with no artificial delay900- Data is delayed by 900 seconds-1- End-of-day (EOD)
4. Additional Parameters
The WebSocket API supports several additional parameters for fine-tuning your data feeds and customizing the response format.
Extended Market Hours (extended)
The extended parameter applies to many US and Global stock markets.
extended: true(default) - Includes pre-market and after-hours trading data where available. For US equities, this covers 4:00 AM - 9:30 AM ET (pre-market) and 4:00 PM - 8:00 PM ET (after-hours).extended: false- Only includes regular trading hours data.- Note: Not all markets support extended hours trading. Setting
extended: truefor markets without extended hours will return only regular session data.
Example:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "NASDAQ:AAPL", "bar_type": "minute", "bar_interval": 1, "extended": false}
]
}Split Adjustment (split)
For non-futures instruments, controls whether price history is adjusted for stock splits. This flag is ignored for futures symbols.
split: true(default) - Historical prices are adjusted for splits.split: false- Returns unadjusted prices. When set tofalse,dadjis forced tofalseas well — dividend adjustment requires split adjustment.
Example:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "NASDAQ:AAPL", "bar_type": "day", "bar_interval": 1, "split": false}
]
}Dividend Adjustment (dadj)
For non-futures instruments, you can request dividend-adjusted price series. This flag is ignored for futures symbols.
dadj: true- Applies dividend adjustments to all price values. Requiressplit: true(the default).dadj: false(default) - Shows prices without dividend adjustment.
Note: If you send split: false together with dadj: true, the server forces dadj back to false.
Example:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "NASDAQ:AAPL", "bar_type": "day", "bar_interval": 1, "dadj": true}
]
}Back-Adjustment (badj)
For continuous futures contracts (e.g., ES1!, NQ1!), you can request back-adjusted price series to remove price gaps between contract rollovers. This flag is ignored for non-futures symbols.
badj: true(default) - Applies back-adjustment to smooth price gaps at contract rollovers.badj: false- Shows unadjusted prices with natural contract rollover gaps.
Example:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "CME_MINI:ES1!", "bar_type": "day", "bar_interval": 1, "badj": false}
]
}Settlement Price (settlement)
For futures contracts, controls whether historical data uses settlement prices for the close value. This flag is ignored for non-futures symbols.
settlement: true- Uses settlement prices as the close price where applicable.settlement: false(default) - Uses the regular close price (last traded price).
Example:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "CME_MINI:ES1!", "bar_type": "day", "bar_interval": 1, "settlement": false}
]
}Initial Data Points (max_dp)
Control how many historical data points you receive when first connecting or reconnecting to a symbol. The max_dp parameter accepts a number from 1 to 30,000. If not specified, defaults to 1 (only the current bar).
When you connect (or reconnect), the first message will contain the most recent max_dp bars in the series property, followed by real-time updates with single bars.
- Mega Plan (10+ symbol subscriptions): up to 30,000 data points.
- Other Plans: up to 1,000 data points.
- If you specify a value higher than your plan allows, it will be automatically clamped to your plan's maximum.
Example:
{
"api_key": "<your_websocket_api_key>",
"subscriptions": [
{"code": "NASDAQ:AAPL", "bar_type": "minute", "bar_interval": 1, "max_dp": 5000}
]
}5. Maintaining Connection & Best Practices
Automatic Re-subscription
Critical Implementation Detail
Configure your WebSocket client to automatically send your subscription message in the onOpen event handler. This ensures immediate re-subscription after any disconnection (network issues, server restarts).
Server Heartbeat (Keep-Alive)
Our server automatically sends periodic timestamp messages (approximately every 10 seconds) to maintain connection health and allow connectivity verification.
Heartbeat Message Format:
{"server_time": 1741397070281}Preventing Connection Drops
Some WebSocket libraries drop connections during inactivity periods. Implement a ping-pong mechanism for connection stability.
Ping-Pong Implementation
- Send a
'ping'text message every 20-30 seconds - Server responds with
'pong'to confirm connection - Filter out
'pong'messages in your data handler
Rate Limiting Warning
Don't send pings more than once every 15 seconds to avoid triggering rate limits. This WebSocket rate limit is separate from REST API limits.
import asyncio
import websockets
import json
async def websocket_client():
uri = 'wss://realtime.insightsentry.com/live'
async with websockets.connect(uri) as websocket:
# Start ping task
ping_task = asyncio.create_task(send_ping(websocket))
try:
async for message in websocket:
if message == 'pong':
print('Received pong from server')
continue
# Handle other messages here
except websockets.exceptions.ConnectionClosed:
print('WebSocket connection closed')
finally:
ping_task.cancel()
async def send_ping(websocket):
while True:
try:
await asyncio.sleep(20)
await websocket.send('ping')
except asyncio.CancelledError:
break
# Run the client
asyncio.run(websocket_client())Client-Side Timeout
Configure your WebSocket library with an appropriate timeout value. We recommend a minimum timeout of 12 seconds. This helps handle periods of low market activity (e.g., weekends, holidays) where price updates might be infrequent, preventing premature disconnection by the client.
Reconnection Best Practices
Network issues, server restarts, and transient errors will occasionally close your connection. How your client reacts determines whether your data flow recovers cleanly or whether you trigger server-side throttling.
Use Exponential Backoff
Never reconnect in a tight loop. Start with a short delay (e.g., 500ms), double it on each consecutive failure, and cap it at a reasonable maximum (e.g., 30 seconds). Reset the backoff once a connection has been stable for a while. Clients that reconnect aggressively will hit the server-side reconnection throttle and experience increasing delays.
Always Close the Socket Properly
When you detect a disconnection, error, or you're about to open a new connection, explicitly close the existing socket first (e.g., ws.close(1000)). Do not just abandon the old connection and open a new one — your old connection may still be held open on our side, counting against your per-account connection limit.
Also remove any listeners/timers attached to the old socket before reconnecting to avoid leaks and duplicate handlers processing incoming data.
Recommended Reconnection Flow
- Detect disconnect (error, close, or missed ping response).
- Call
close()on the existing socket and clear any intervals/listeners. - Wait for
delay = min(base * 2^attempt, max)before reconnecting. - Open a new connection and re-send your subscription message in the open handler.
- On successful data flow, reset the
attemptcounter back to 0.
6. Error Handling
When a request cannot be processed, the server sends a JSON-encoded error message on the same connection. Most errors do not close the socket — the connection stays open so you can correct your request and retry without reconnecting.
Error messages have one of two shapes. Simple validation errors use a single message field:
{
"message": "api_key field is missing."
}Typed errors include an error code and additional context fields such as symbol or details:
{
"error": "invalid_bar_interval",
"message": "Invalid bar_interval for second interval. Allowed values: 1, 5, 10, 15, 30, 45",
"symbol": "NASDAQ:AAPL"
}Error Codes
error | Meaning | Connection |
|---|---|---|
invalid_request | Message payload could not be parsed or validated. | Stays open |
invalid_symbol | Unknown or malformed symbol code. Other symbols in the same message are still processed. | Stays open |
invalid_bar_interval | bar_interval not allowed for the requested bar_type. The message includes the valid values. | Stays open |
invalid_currency_code | Unknown currency value. | Stays open |
invalid_unit_code | Unknown unit value. | Stays open |
max_connections_exceeded | Too many concurrent connections for your account. details includes current and allowed counts. | Closed by server |
connection_evicted | A newer connection from your account replaced this one — usually because your client is reconnecting without properly closing old sockets. | Closed by server |
server_busy | Temporary routing/attach failure. Retry after a backoff delay. | Closed by server |
internal_server_error | Unexpected server-side error. Retry after a backoff delay; contact support if it persists. | Closed by server |
- For errors that stay open, correct the invalid fields and send a new subscription message on the same socket.
- For errors that close the connection, reconnect with exponential backoff after making sure to properly close the local socket first.
- Validate symbol codes, currencies, and intervals on the client before sending.
7. API Key Rotation
RapidAPI Users Only
This section applies only to RapidAPI subscribers. Paid website subscribers use the dashboard API key directly, while free website keys are REST-only.
RapidAPI WebSocket API keys can be refreshed before expiration. Understanding key expiration is crucial for maintaining uninterrupted service.
Automatic Expiration
WebSocket API keys expire automatically after one week from issuance for security purposes.
Key Rotation Process
Follow these steps to rotate your WebSocket API key:
- Request a new key from the
/v2/websocket-keyendpoint - Previously issued keys may continue to work until their JWT expiration
- Update your application to use the new key for all connections
- The response includes the key and expiration timestamp (Unix epoch seconds)
API Key Response Format
{
"api_key": "your-websocket-api-key",
"expiration": 1747580931
}Key Management Best Practices
- Refresh your key regularly - you can refresh it daily if desired, or at least once a week before expiration.
- Store the WebSocket API key in your database along with its expiration timestamp.
- Before using a stored key, check if it's expired or close to expiration.
- If the key is expired or close to expiration, automatically issue a new WebSocket API key and update your database.
import time
import requests
from typing import Optional, Dict, Any
from dataclasses import dataclass
BASE_URL = 'https://insightsentry.p.rapidapi.com'
@dataclass
class WebSocketKey:
key: str
expires_at: int
async def get_valid_websocket_key() -> str:
stored_key = await database.get_websocket_key()
now = int(time.time())
buffer_time = 24 * 60 * 60 # 24 hours
if stored_key and stored_key.expires_at > (now + buffer_time):
return stored_key.key
response = requests.get(
f'{BASE_URL}/v2/websocket-key',
headers={
'x-rapidapi-key': 'YOUR_RAPID_API_KEY'
}
)
response.raise_for_status()
data = response.json()
await database.save_websocket_key(WebSocketKey(
key=data['api_key'],
expires_at=data['expiration']
))
return data['api_key']8. Code Examples
The following examples demonstrate how to connect to our WebSocket API and handle real-time data feeds using popular programming languages.
Before You Start
Replace <your apikey> or <your_key> placeholders with your actual WebSocket API key in all examples.
# pip install requests
# pip install websockets
# pip install asyncio
import requests
import websockets
import json
import asyncio
# If you've subscribed directly on our website, you don't need to refresh the API key as your API key can be used for both Rest API and WebSocket API.
###### RapidAPI specific code START ######
# Below API Key management only applies if you've subscribed via RapidAPI
url = "https://insightsentry.p.rapidapi.com/v2/websocket-key"
# Replace with your actual API key
apiKey = "YOUR_API_KEY_HERE"
headers = {
"x-rapidapi-key": apiKey,
"x-rapidapi-host": "insightsentry.p.rapidapi.com"
}
response = requests.get(url, headers=headers)
json_response = response.json()
print(json_response)
### Important ###
# Save the api_key somewhere in your database or file and then reuse it later
# Since the api_key's expiration is after one week, please refresh it accordingly
# You can retrieve only up to 10 Websocket API keys per day
# This is just an example to show how to get the api_key
api_key = json_response.get("api_key")
###### RapidAPI specific code END ######
async def handle_messages(websocket):
async for message in websocket:
if message == "pong": # skip pong
continue
# can safely assume that the message is a JSON string
try:
data = json.loads(message)
# Check if json contains 'code'
if 'code' in data:
# Do something with the Quote or Series data
if 'series' in data:
# Handle Series data
print(data)
else:
# Handle Quote data
print(data)
else:
# "server_time" is for checking latency and connection status. If it's not received within 30 seconds, there may be a connection issue
# "message" is for initial connection and error messages
print(data)
except json.JSONDecodeError as e:
print(f"Failed to decode JSON: {e}")
print(f"Raw message: {message}")
async def send_ping(websocket):
"""Send ping messages every 15 seconds to keep connection alive"""
while True:
try:
await asyncio.sleep(15)
await websocket.send('ping')
except Exception as e:
print(f"Ping failed: {e}")
break
async def connect_and_subscribe():
uri = "wss://realtime.insightsentry.com/live"
try:
async with websockets.connect(uri) as websocket:
print("Connected to websocket")
# Send subscription message
subMsg = json.dumps({
"api_key": api_key,
"subscriptions": [
# Subscribe to either "series" or "quote" types
# With 1 Symbol Subscription you can subscribe to 1 "series" or 10 "quote"
{
"code": "NASDAQ:AAPL",
"type": "quote"
},
{
"code": "CME_MINI:ES1!",
"type": "quote"
}
#,
# {
# "code": "NASDAQ:GOOGL",
# "type": "series",
# "bar_type": "second",
# "extended": false, # to receive only regular trading hours data
# "bar_interval": 1,
# "max_dp": 100 # optional: receive 100 most recent bars on connect/reconnect
# },
# {
# "code": "NASDAQ:AAPL",
# "type": "series",
# "bar_type": "tick",
# "bar_interval": 1,
# "max_dp": 500 # optional: request a specific amount of initial series history
# }
]
})
await websocket.send(subMsg)
# Periodically sending 'ping' can help to keep connection sticky
ping_task = asyncio.create_task(send_ping(websocket))
try:
# Handle incoming messages
await handle_messages(websocket)
finally:
# Cancel ping task when done
ping_task.cancel()
try:
await ping_task
except asyncio.CancelledError:
pass
except websockets.exceptions.ConnectionClosed as e:
print(f"Connection closed: {e}")
raise
except Exception as e:
print(f"Connection error: {e}")
raise
async def run_with_retry():
attempt = 0
while True:
try:
attempt += 1
await connect_and_subscribe()
break
except Exception as e:
wait_time = min(0.1 + (attempt - 1) * 0.5, 5) # Start at 100ms, increase by 500ms, cap at 5 seconds
print(f"Reconnecting in {wait_time} seconds...")
await asyncio.sleep(wait_time)
if __name__ == "__main__":
asyncio.run(run_with_retry())
FAQ
Q: WebSocket is disconnected frequently
There could be several causes. First, check if your main thread is blocked due to heavy data processing. If the main thread is blocked, the WebSocket library's internal ping mechanism may fail to respond, which can lead to disconnection. This is one of the most common causes. You can easily identify this by comparing your machine's current time with the server_time field in the messages sent from our servers. If Time.inMilliseconds - server_time results in a negative value, it indicates that your main thread is being blocked.
Q: There are missing messages when using 'second' as bar_type and 1 as bar_interval
For the 1-second time frame, a bar message is sent only when there is a change in the close price or volume. This means you may not receive a message every second. To handle missing bars, you can normalize your data by filling in with the previous bar's values when no update is received.