Welcome to API Academy

Module 07 · Real-Time · ~22 min

Websockets.

By the end of this module, your bot reacts the instant a price moves on Limitless, instead of asking the exchange “what’s happening now?” on a timer and missing everything in between.

To get there, you’ll open a socket.io connection to wss://ws.limitless.exchange, subscribe to live price and orderbook feeds, and restore those subscriptions after every reconnect. Replace polling with a push-based stream, the foundation every real-time strategy in this tier builds on.

Real-Time tier · Reference card
Quick answer

How do you connect to the Limitless websocket?

Open a socket.io connection to wss://ws.limitless.exchange on the /markets namespace; the SDK’s WebSocketClient wraps the handshake, and public feeds need no API key. Subscribe with subscribe_market_prices for live data: newPriceData events carry AMM price deltas (marketAddress, updatedPrices, blockNumber, timestamp) and orderbookUpdate events carry CLOB book deltas per market slug. The authenticated subscribe_positions channel requires your API key, which the SDK attaches as a connection header. Two rules keep the stream honest: subscribe_market_prices REPLACES your previous subscription, so send every address and slug in a single call; and the server forgets all subscriptions on every reconnect, so re-issue them from the connect handler (autoReconnect restores the socket, not the channels). Keep one connection per process; one socket covers as many markets as you care to stream.

WS transport verified 2026-06-09 against the backend gateway + SDK.

Section 01

Why streams beat polling.

Polling asks the server for state on a schedule; streaming lets the server push state as it changes. For anything where latency matters, executing on a signal, unwinding into a move, market-making on a shifting fair value, polling is structurally behind. The two cards below compare the tradeoffs honestly; WebSockets aren’t always the right choice. If your strategy is a cron job that snapshots the market once an hour, REST is fine. Everything else in this tier assumes you’ve moved to a stream.

REST polling

  • Best case latency = your poll interval
  • Burns rate limit on quiet markets
  • Misses intra-interval moves entirely
  • Scales linearly with market count
  • Dead simple, fine for cron jobs

WS streams

  • Push-based, deltas arrive in ms
  • One connection covers many markets
  • Lets you see every print, not a snapshot
  • Heartbeats prove the pipe is alive
  • Needs reconnect & resubscribe logic

Section 02

Connecting.

Open a secure WebSocket to wss://ws.limitless.exchange on the /markets namespace (socket.io-based). Pass your API key to the SDK client constructor, the SDK attaches it as a connection header. Public feeds (subscribe_market_prices) work without auth; subscribe_positions requires it. Keep the connection per process, don’t reopen it on every tick.

// Module 07, Connecting to the Limitless WebSocket.
// $ npm install @limitless-exchange/sdk

import { WebSocketClient } from '@limitless-exchange/sdk';

// The SDK wraps socket.io under the hood and targets the
// /markets namespace. Public feeds (prices, orderbook) need
// no auth; authenticated feeds (positions) require an API key.
const ws = new WebSocketClient({
  url:           'wss://ws.limitless.exchange',
  apiKey:        process.env.LIMITLESS_API_KEY, // optional for public data
  autoReconnect: true,
});

ws.on('connect',      ()    => console.log('ws open'));
ws.on('disconnect',   (why) => console.log('ws closed:', why));
ws.on('exception',    (err) => console.error('stream error:', err));

// Fire the connect, subscriptions happen in Section 03.
ws.connect();

How to run this

  1. The client points at wss://ws.limitless.exchange. An API key is only required for authenticated channels like subscribe_positions, if you want to test those too, set LIMITLESS_API_KEY in your environment.
  2. Save the snippet above as ws-connect.ts, then run npx tsx ws-connect.ts.
  3. You see ws open in the terminal and the process keeps running. The socket is alive but silent, no subscriptions yet, that’s Section 03.

Section 03

Subscribing.

After the auth handshake, send a subscribe message with the channel and the slugs you care about. The server replies with a one-shot snapshot, then incremental updates as state changes. Route each message by msg.type.

// Module 07, Subscribing to price + orderbook streams.
// GOTCHA: subscribe_market_prices REPLACES the previous
// subscription. If you want AMM prices AND CLOB orderbooks
// for multiple markets, send ALL addresses + slugs in a
// single call.

ws.on('newPriceData', (data) => {
  // AMM price delta: { marketAddress, updatedPrices: {yes, no}, blockNumber, timestamp }
  console.log('price', data.marketAddress, data.updatedPrices);
});

ws.on('orderbookUpdate', (data) => {
  // CLOB delta: { marketSlug, orderbook, timestamp }
  console.log('book', data.marketSlug);
});

ws.on('positions', (data) => {
  // Authenticated snapshot, AMM or CLOB variant
  console.log('positions', data.type, data);
});

// One call, all markets, both addresses and slugs allowed.
ws.subscribe('subscribe_market_prices', {
  marketAddresses: ['0x76d3e2098Be66Aa7E15138F467390f0Eb7349B9b'],
  marketSlugs:     ['btc-100k-weekly', 'us-cpi-oct-above-3'],
});

// Authenticated, requires apiKey on the client.
ws.subscribe('subscribe_positions');

How to run this

  1. Paste this snippet after the Section 02 connect code so the ws / ws_client client exists. subscribe_positions requires LIMITLESS_API_KEY, drop that line if you only want public price data. Swap the marketSlugs for markets that are currently active.
  2. Save the combined file as subscribe-markets.ts, then run npx tsx subscribe-markets.ts.
  3. You see a stream of price lines for each AMM block and book lines whenever a CLOB level changes. Quiet markets may log one line per minute, that’s normal, not broken.

Section 04

Heartbeats & reconnect.

Connections die. Networks flap. The question is whether your bot notices and recovers. Send a ping every 15 seconds, expect a pong back, and reconnect with exponential backoff on any close or missing pong.

Pipe alive

ping 15s · backoff capped at 30s

// Module 07, Reconnects are automatic. YOU resubscribe.
//
// socket.io handles heartbeats internally, no manual ping/pong.
// autoReconnect: true gives you exponential backoff for free.
// What the SDK does NOT do is re-issue your subscriptions, so
// the server forgets them on every reconnect.

import { WebSocketClient } from '@limitless-exchange/sdk';

const ws = new WebSocketClient({
  url:           'wss://ws.limitless.exchange',
  apiKey:        process.env.LIMITLESS_API_KEY,
  autoReconnect: true,
});

const SLUGS = ['btc-100k-weekly', 'us-cpi-oct-above-3'];

function restoreSubscriptions() {
  // Single call, subscribe_market_prices replaces the previous sub.
  ws.subscribe('subscribe_market_prices', { marketSlugs: SLUGS });
  ws.subscribe('subscribe_positions');
}

ws.on('connect',   () => { console.log('connected');   restoreSubscriptions(); });
ws.on('reconnect', () => { console.log('reconnected'); restoreSubscriptions(); });

ws.on('orderbookUpdate', (d) => { /* apply delta */ });
ws.on('exception',       (e) => console.error('ws:', e));

ws.connect();

How to run this

  1. Set LIMITLESS_API_KEY so subscribe_positions works. Keep the SLUGS array short (one or two active markets) while you test.
  2. Save the snippet above as reconnecting-client.ts, then run npx tsx reconnecting-client.ts.
  3. You see connected / (re)connected logs followed by deltas. Drop your Wi-Fi for a few seconds and the client should re-emit a connect log and resume the stream, proof that restoreSubscriptions() ran.
Common questions

Limitless websockets: what people ask

Each answer also ships invisibly as schema.org FAQ data for search engines and AI assistants. Tap a question to expand.

  1. Do you need an API key for the Limitless websocket?
    Only for authenticated channels. Public feeds, subscribe_market_prices with its newPriceData and orderbookUpdate events, work with no auth at all. subscribe_positions (your own positions) requires an API key, which you pass to the SDK client constructor; the SDK attaches it as a connection header. Set LIMITLESS_API_KEY in the environment if you want both.
  2. What events does the Limitless websocket send?
    Three you’ll handle constantly: newPriceData, the AMM price delta with marketAddress, updatedPrices (yes/no), blockNumber, and timestamp; orderbookUpdate, the CLOB delta with marketSlug, orderbook, and timestamp; and positions, the authenticated snapshot of your own holdings. Route each message by type, and expect quiet markets to log one line a minute, that’s the market, not a broken stream.
  3. Why does a bot stop receiving prices after a websocket reconnect?
    Because subscriptions are per-connection. When the socket drops, a wifi blip, a server restart, a missed heartbeat, the server forgets every channel you subscribed to, and reconnecting opens a fresh, empty session. autoReconnect restores the socket, not the channels, so re-issue every subscribe from the connect handler, which fires on the first connect and every reconnect. Test it by killing your network for 30 seconds and confirming new prices arrive after.
  4. Why must all markets go in one subscribe_market_prices call?
    Because each subscribe_market_prices message REPLACES the previous subscription instead of adding to it. Subscribe to market A, then market B in a second call, and you silently stop receiving A. If you want AMM prices and CLOB orderbooks for multiple markets, send all the marketAddresses and marketSlugs in a single call, and keep that list in one place so the reconnect handler resubscribes correctly.
  5. When is REST polling fine instead of a websocket?
    When latency doesn’t matter: a cron job that snapshots the market once an hour is fine on REST. For anything where reaction time counts, executing on a signal, unwinding into a move, market-making on a shifting fair value, polling is structurally behind: its best-case latency is your poll interval, it burns rate limit on quiet markets, and it misses every move between polls.

Section 05

Module checklist.

Tick each item once you’ve actually done it. The Continue button unlocks at 5/5.

Module 07 complete

Streams flowing.

Your bot can hear the market in real time. When a price ticks or the book shifts, your code knows within milliseconds, fast enough to act on what other traders are seeing at the same moment, instead of after they’ve already moved.

Concretely, you can subscribe to live market updates and parse the message stream. Here’s what you walk away with:

01

A WebSocketClient pointed at wss://ws.limitless.exchange with autoReconnect on, one process, one connection, as many markets as you care to stream.

02

Handlers wired up for newPriceData (AMM touch), orderbookUpdate (CLOB depth), and subscribe_positions (your own fills), the three event streams every downstream module builds on.

03

A restoreSubscriptions() helper called on every connect event, so the server forgetting your subscriptions after a reconnect is no longer your problem.

Next up: turning the raw orderbookUpdate stream into a reliable local book, depth, touch, trades, and the REST reconciliation that catches drift before your bot trades on a stale view.

Complete the checklist above to unlock