This guide walks through a complete Node.js/TypeScript integration with the Limitless Exchange API: authentication, market data, EIP-712 order signing with viem, order submission, and WebSocket subscriptions for real-time data.
Never expose your private key. Use environment variables and never commit secrets to version control. For production, consider a dedicated key management solution.
OWNER_ID is your Limitless profile ID (numeric). Obtain it from your account settings or from an authenticated API response. It is required for order submission.
Authenticated REST requests (e.g. submitting orders) are signed with HMAC-SHA256 using your scoped API token. Each request carries three headers — lmts-api-key, lmts-timestamp, and lmts-signature — computed over a canonical message of timestamp, HTTP method, request path (with query string), and body. Public market data (browsing markets, orderbooks) needs no authentication. See Authentication for the full reference.
import { createHmac } from 'crypto';const API_URL = process.env.API_URL ?? 'https://api.limitless.exchange';const TOKEN_ID = process.env.LMTS_TOKEN_ID;const TOKEN_SECRET = process.env.LMTS_TOKEN_SECRET; // base64-encoded// Build HMAC auth headers for a single request (method + path + body).function signRequest(tokenId: string,secret: string,method: string,path: string,body: string = '',): Record<string, string> {const timestamp = new Date().toISOString();const message = `${timestamp}\n${method}\n${path}\n${body}`;const signature = createHmac('sha256', Buffer.from(secret, 'base64')).update(message).digest('base64');return { 'lmts-api-key': tokenId, 'lmts-timestamp': timestamp, 'lmts-signature': signature };}// Public endpoints (market data, orderbooks) require no authentication.export async function apiGet<T>(path: string): Promise<T> {const res = await fetch(`${API_URL}${path}`);if (!res.ok) throw new Error(`API ${res.status}: ${await res.text()}`);return res.json();}// Authenticated POST — signs the exact body bytes that are sent.export async function apiPost<T>(path: string, body: unknown): Promise<T> {if (!TOKEN_ID || !TOKEN_SECRET) { throw new Error('Missing LMTS_TOKEN_ID / LMTS_TOKEN_SECRET. Derive a token at limitless.exchange → API Tokens');}const serialized = JSON.stringify(body);const auth = signRequest(TOKEN_ID, TOKEN_SECRET, 'POST', path, serialized);const res = await fetch(`${API_URL}${path}`, { method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: serialized,});if (!res.ok) throw new Error(`API ${res.status}: ${await res.text()}`);return res.json();}
Generating lmts-signature by hand on the command line is awkward — for shell usage, run the signRequest helper above (or your SDK) to produce the three headers. See Authentication for ready-to-use signing snippets.
Connect to wss://ws.limitless.exchange with namespace /markets using socket.io-client. Emit subscribe_market_prices with marketAddresses and/or marketSlugs.
Subscriptions replace previous ones. To subscribe to both AMM (by address) and CLOB (by slug), send both marketAddresses and marketSlugs in a single subscribe_market_prices call.