Tradovate API

Tradovate WebSocket Closes at Exactly 5 Minutes

The socket streams perfectly for 300 seconds, then drops with no error and no warning. Here's what's really closing it, the heartbeat cadence that extends it, and the reconnect pattern that makes the drop a non-event.

Revisado por el equipo de Sistemas de Trading de PickMyTrade Última actualización
· 8 min read
Chrome DevTools Network tab with the WS filter showing a Tradovate WebSocket connection closing at the 5:00 mark

When you're automating fast movers like ES or NQ, few things sting more than a Tradovate WebSocket that dies on a timer. You connect, authorize, subscribe to market data, and everything streams beautifully, right up until 300 seconds, when the socket drops with no error, no close reason, no warning. It's the same story every time: the connection closes at exactly 5 minutes, even when your heartbeats look like they're firing correctly. Two related errors usually tag along, a WebSocket is not open: readyState 0 (CONNECTING) exception when your code writes to a half-open socket, and a bare close code 1006 (abnormal closure) from the remote peer. Here's what the 5-minute close actually means in 2026, the real causes behind it, and the exact heartbeat-plus-reconnect pattern that keeps the connection alive, plus how PickMyTrade runs this plumbing for you so a dropped socket never costs you a fill.

Quick Checklist for the 5-Minute WebSocket Close

  • Send a heartbeat roughly every 2.5 seconds, a frame whose text is an empty JSON array, []. Skip it, and Tradovate closes the socket for inactivity.
  • Don't trust setInterval in a browser tab, background tabs get throttled, your heartbeat slips past 2.5s, and the server hangs up.
  • Send your own heartbeats, once real-time data starts flowing, the server stops sending them, so the client has to keep the rhythm going.
  • Guard every send with a readyState check, only write when the socket is OPEN (1), never while it's still CONNECTING (0).
  • Log the close event, ws.addEventListener('close', e => console.log(e)) reveals the code (often 1006) and any reason.
  • Assume drops will happen and reconnect, wrap the socket so a close automatically re-authorizes and resubscribes.

What "WebSocket Closes at Exactly 5 Minutes" Means

Tradovate's real-time API is a WebSocket connection: you open it, send an authorization frame, then subscribe to the data and events you care about. To keep that connection open, the client is responsible for sending heartbeats, small keep-alive frames, on a fixed cadence. Stop sending them and Tradovate treats the socket as idle, then closes it from the server side.

The reason the failure so often lands on a clean 5-minute mark is that timeouts along the network path, the server, a load balancer, or a proxy in between, tend to be enforced in round numbers. The connection disconnects every 5 minutes exactly (300 seconds) with no message, and tellingly, without heartbeats the drop happens after about 2 minutes instead. So heartbeats extend the life of the socket, but a lagging or missed heartbeat, or an independent idle timer somewhere on the path, can still guillotine it at 300 seconds. There's no published root cause for the exact-5-minute case, so the durable engineering answer isn't to prevent every close. It's to survive it.

The two error strings you'll see are symptoms of the same underlying problem. WebSocket is not open: readyState 0 (CONNECTING) means your code tried to send() before the handshake finished, common inside a heartbeat function that fires while the socket is mid-reconnect. Close code 1006 is the browser or library's way of saying “the connection dropped abnormally,” with no clean close handshake, exactly what you get when a server-side idle timer cuts the line.

Top Causes of the 5-Minute WebSocket Close in 2026

1. Heartbeat frames arriving late or missing entirely

This is the number-one cause. Tradovate expects a heartbeat frame, text content [], roughly every 2.5 seconds to prove the client is alive. Miss that window and the server starts the countdown to close. And here's the catch: once you start streaming real-time quotes, Tradovate's server stops sending its own heartbeats, so you can't lean on the connection's built-in traffic to hold it open. Your client has to send the empty-bracket frame on its own schedule.

2. Browser timer throttling (setInterval / setTimeout)

If you schedule heartbeats with setInterval() or setTimeout() in a browser, you're at the mercy of the tab's priority. When the tab loses focus, Chrome (and most browsers) lower the priority of those timers, so a “2.5-second” interval can quietly stretch to several seconds. The heartbeat slips past the deadline and the socket closes, often right around the 5-minute mark, once enough lag piles up.

3. Server-side idle or session timeout

Even a perfectly paced heartbeat can't override a timeout enforced upstream. A proxy, a gateway, or the Tradovate session itself may cap idle or total connection time, which is why the close lands on that suspiciously round 300-second boundary. Since this is outside your control, the fix is reconnection, not prevention.

4. Sending on a socket that is not OPEN (readyState 0)

A WebSocket has four states: CONNECTING (0), OPEN (1), CLOSING (2), and CLOSED (3). If your heartbeat loop or order code calls send() while the socket is still at readyState 0, you get WebSocket is not open: readyState 0 (CONNECTING) and, often, a “Disconnected from Tradovate Market WebSocket” message right behind it. This one bites hardest in the instant after a drop, when your reconnect has spun up a new socket that hasn't finished its handshake yet.

Browser console log of a Tradovate WebSocket close event object showing code 1006 and the readyState 0 error

5. Token expiry and long-lived connections

A Tradovate access token has a limited lifetime, and these connections aren't built to live forever, hold a single socket open for very long stretches (over a day) and it tends to drop. If your token expires mid-session or the connection ages out, the socket closes and any re-authorization on the old token fails. Token lifetimes vary and change over time, so confirm the current values in Tradovate's API docs and refresh proactively instead of waiting for a failure.

How to Fix the 5-Minute WebSocket Close: Step-by-Step

Fix 1: Send a Heartbeat Frame Roughly Every 2.5 Seconds

The heartbeat is the foundation. On a cadence of about every 2.5 seconds, send a frame whose text is an empty JSON array:

1

Start the keep-alive routine

After the socket opens and you've authorized, start a keep-alive routine.

2

Send the empty-bracket frame

On each tick, call ws.send('[]'), the empty-bracket frame is Tradovate's heartbeat.

3

Keep the interval under 2.5s

Keep the interval comfortably under the limit (2.5 seconds is the target; a touch more often is safer than a touch less).

4

Never assume server pings

Never assume the server will ping you, after data starts streaming, it won't.

Code editor showing a Tradovate WebSocket heartbeat function sending an empty-bracket frame every 2.5 seconds

Fix 2: Replace setInterval with a Timestamp Check or a Web Worker

Because browser timers get throttled, don't lean on setInterval alone in a foreground or background tab. Two proven alternatives:

  • Timestamp-driven heartbeats. Record a timestamp (new Date()) each time you receive a message. Compare it against the last time you sent a heartbeat; if more than 2500ms has elapsed, send [] now. This reacts to real traffic instead of a timer the browser can starve.
  • Move the timer off the main thread. Run the heartbeat inside a Web Worker, or run the whole connection as a Node.js service. Both sidestep browser tab throttling entirely, a Node process isn't subject to the background-tab penalty at all.

Fix 3: Guard Every Send with a readyState Check

Kill the readyState 0 error by never writing to a socket that isn't open:

1

Check readyState before every send

Before every send(), heartbeat or order, check if (ws.readyState === WebSocket.OPEN) (that's state 1).

2

Skip and let reconnect logic take over

If it's still CONNECTING (0) or already CLOSING/CLOSED, skip the send and let your reconnect logic take over.

3

Attach listeners for all state transitions

Attach listeners for open, message, error, and close so state transitions are explicit, and log the close event object to capture the code and reason.

Fix 4: Add Reconnect, Re-Authorize, and Resubscribe Logic

Since some 5-minute closes are enforced upstream and can't be prevented, treat every drop as a recoverable error and rebuild automatically:

1

Wrap the socket

Wrap the socket in a class or function that owns its lifecycle, so one place is responsible for creating and tearing it down.

2

Reconnect on close

On close, reconnect, open a fresh socket rather than trying to revive the dead one.

3

Re-authorize the new socket

Re-authorize the new socket with a valid token (regenerate the token before it expires so a fresh one is always ready).

4

Resubscribe to everything

Resubscribe to everything you had, resend your user/syncrequest and re-subscribe to each market-data feed, because a new socket starts with no subscriptions.

5

Wait for OPEN before sending

Wait for OPEN before sending anything on the new socket, and add a short backoff so a flapping connection doesn't hammer the server.

Console output showing a Tradovate WebSocket reconnecting, re-authorizing, and resubscribing after a drop

Troubleshooting Table

Error / signal What it means Fix
Socket closes at exactly 300sAn idle or session timeout on the path cut the connectionSend heartbeats every ~2.5s and auto-reconnect on close
Drops after ~2 minutesNo heartbeats are being sent at allStart a keep-alive that sends the [] frame on a 2.5s cadence
WebSocket is not open: readyState 0 (CONNECTING)Code called send() before the handshake finishedOnly send when readyState === OPEN (1); skip otherwise
Close code 1006Abnormal closure with no clean handshakeReconnect on close, re-authorize, and resubscribe
"Disconnected from Tradovate Market WebSocket"The socket dropped mid-streamRebuild the socket and restore your subscriptions
Heartbeats fine but tab was inactiveBrowser throttled setInterval/setTimeoutUse timestamp-based heartbeats or a Web Worker / Node.js
Drop after very long uptimeToken expired or the connection aged outRefresh the token before expiry and reconnect on a schedule

Prevent This with PickMyTrade

PickMyTrade sits between your TradingView alerts and Tradovate and runs the WebSocket layer as a managed, server-side service, so the 5-minute close is handled before it ever reaches you:

  • Managed Heartbeats, sends the keep-alive frame on a reliable server-side cadence, with no browser tab to throttle the timer.
  • Auto-Reconnect & Re-Auth, detects a dropped socket, opens a fresh one, re-authorizes with a valid token, and resubscribes automatically.
  • Connection-State Guards, only transmits when the socket is genuinely open, eliminating readyState 0 send errors.
  • Multi-Account Sync, keeps every connected account's stream healthy so a single reconnect never leaves one account behind.

Trade Rejection-Free

Start your free 5-day trial, link your alerts today and trade rejection-free.

Start Your Free 5-Day Trial

Frequently Asked Questions

The socket is being closed by an idle or session timeout on the connection path, which tends to trigger on round numbers like 300 seconds. Sending heartbeats extends the connection, but if a heartbeat lags or an upstream timer fires anyway, the socket still drops. The reliable fix is heartbeats plus automatic reconnection.

About every 2.5 seconds. The heartbeat is a frame whose text is an empty JSON array, []. Sending slightly more often than 2.5 seconds is safer than risking a late frame.

Just [], an empty JSON array sent as the WebSocket message text. That empty-bracket frame is what Tradovate reads as a keep-alive.

Your code called send() while the socket was still handshaking (state 0), not yet OPEN (state 1). Check readyState before every send and skip the write until the socket reports OPEN, letting your reconnect logic finish first.

Not once real-time data is streaming. The server stops sending heartbeats after the data flow begins, so the client must send its own on the 2.5-second cadence. You cannot rely on server traffic to hold the connection open.

Browsers throttle setInterval and setTimeout in inactive tabs, stretching your 2.5-second heartbeat past the limit. Use timestamp-based heartbeats keyed off received messages, or run the connection in a Web Worker or a Node.js process to avoid tab throttling entirely.

Code 1006 is an abnormal closure with no clean close handshake, typically a dropped connection or a server-side timeout. Do not try to prevent every 1006; wrap the socket, reconnect on close, re-authorize with a fresh token, and resubscribe to your feeds.

This guide is for educational and informational purposes only and is not financial, investment, or trading advice. Trading futures and other leveraged products carries a substantial risk of loss and is not suitable for every investor. PickMyTrade is an independent third-party automation platform and is not affiliated with, endorsed by, or sponsored by Tradovate, Inc. "Tradovate" and all related names, logos, and trademarks are the property of their respective owners. Platform features and steps change over time, so always confirm the current process in the official Tradovate platform and documentation before acting.