GeminiGemini
SandboxGet API key
  • Crypto Trading
  • Prediction Markets
  • SDKs & Tools
Changelog
Gemini Crypto Exchange LogoGemini Crypto Exchange Logo

© 2026 Gemini Space Station, Inc.

Overview
TypeScript SDK
    QuickstartAuthenticationWebSocketError HandlingPatterns & Recipes
    API Reference
    Deep Dives
TypeScript SDK

TypeScript SDK — WebSocket

The SDK provides real-time market data and account updates over WebSocket. Public streams require no authentication. Authenticated streams (orders, balances) require an auth strategy and run only on the server entry point.

Public streams

Subscribe to market data without authentication:

Code
import { createClient } from "gemini-markets/browser"; const client = createClient({ env: "sandbox" }); // Real-time trades const trades = client.websocket.trades("BTCUSD"); trades.on("message", (trade) => { console.log(trade.p, trade.q, trade.m); // price, quantity, is-maker }); await trades.ready; // wait for subscription confirmation // Book ticker (best bid/ask) const ticker = client.websocket.bookTicker("BTCUSD"); ticker.on("message", (tick) => console.log(tick)); // Depth updates (order book diffs) const depthUpdates = client.websocket.depthUpdates("BTCUSD"); depthUpdates.on("message", (update) => console.log(update)); // Depth snapshots (top N levels) const depthSnapshot = client.websocket.depth("BTCUSD", { levels: 10 }); depthSnapshot.on("message", (snapshot) => console.log(snapshot));

Public streams share a single underlying WebSocket connection. Opening multiple streams to different symbols reuses the same session.

Closing a stream

Code
await trades.close(); // sends unsubscribe, waits for acknowledgement

Closing one stream does not affect others on the shared session. Call client.close() to shut down all streams and connections.

Live order book

The SDK maintains a self-healing L2 order book from WebSocket depth data:

Code
const book = client.orderBook("BTCUSD"); book.on("update", (lob, delta) => { console.log("Best bid:", lob.bestBid()); console.log("Best ask:", lob.bestAsk()); console.log("Spread:", lob.spread()); console.log("Mid:", lob.mid()); console.log("Top 5 bids:", lob.topN("bids", 5)); console.log("Changed levels:", delta); }); book.on("resync", () => { // A gap was detected in the update stream. // The book is rebuilding from a fresh snapshot. // Treat current state as stale until the next "update" event. console.warn("Order book resyncing — data may be stale"); }); book.on("error", (err) => console.error(err));

The first "update" event after subscribing (or after a "resync") carries the full book — treat it as a replacement, not an incremental diff. Subsequent updates carry only changed levels; a quantity of "0" means the level was removed.

spread() and mid() return floating-point values for display. Do not use them for exact execution decisions without decimal handling.

Code
book.close(); // stop updates and release the stream

Authenticated streams

Authenticated streams require the server entry point and an auth strategy. They use the ws package to set custom headers on the WebSocket upgrade request.

Code
import { createClient, HmacAuth } from "gemini-markets/server"; const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey, apiSecret }), }); // Order updates for the current session const orders = client.websocket.orders({ scope: "session" }); orders.on("message", (order) => { console.log(order.i, order.X, order.z); // orderId, status, remainingQuantity }); // Account-wide balance updates const balances = client.websocket.balances(); balances.on("message", (update) => { for (const b of update.B) { console.log(b.a, b.f, b.c); // asset, free, locked } }); await orders.ready;

Browser limitation

Browser WebSocket cannot set custom HTTP headers on the upgrade request. Authenticated WebSocket streams (which require HMAC headers) only work from the server entry point. Browser apps can use:

  • Public streams (trades, depth, book tickers) — no auth needed
  • REST endpoints via OAuth for authenticated operations

WebSocket order operations

Place and cancel orders over WebSocket for lower latency:

Code
// Place an order const result = await client.websocket.placeOrder({ symbol: "BTCUSD", side: "BUY", type: "LIMIT", price: "50000.00", quantity: "0.001", timeInForce: "GTC", }); // response: { id (request correlation ID), status, result? } // The exchange order ID is inside the result object: const orderResult = result.result as Record<string, unknown> | undefined; if (!orderResult?.orderId) throw new Error("Place response missing orderId"); // Cancel an order using the exchange order ID await client.websocket.cancelOrder({ orderId: String(orderResult.orderId) });

Cancellation methods that affect multiple orders require explicit confirmation:

Code
await client.websocket.cancelAllOrders({ confirm: true }); await client.websocket.cancelSessionOrders({ confirm: true });

Reconnection

WebSocket connections reconnect automatically on disconnection with exponential backoff:

  • Public stream subscriptions are replayed after reconnect
  • Authenticated streams re-authenticate with fresh credentials (nonces and tokens are regenerated)
  • Mutating requests (order placement/cancellation) are never replayed — they reject with an error if the connection drops mid-request

Monitor reconnection:

Code
const stream = client.websocket.trades("BTCUSD"); stream.on("resubscribed", () => { console.log("Stream reconnected and resubscribed"); }); stream.on("subscriptionError", (err) => { console.error("Resubscription failed:", err); });

Stream state

Code
stream.state; // "active" | "reconnecting" | "failed" | "closed" stream.lastError; // the last error, if any stream.malformedFrameCount; // count of frames that couldn't be parsed

GeminiWebSocket vs WsSession

The SDK exposes two lower-level WebSocket classes if you need more control:

ClassUse when
GeminiWebSocketYou want the full typed stream API (trades, depth, orders) but outside of a GeminiMarkets client
WsSessionYou want raw WebSocket request/response correlation without stream abstractions

Most applications should use client.websocket via createClient(). The lower-level classes are for advanced use cases like custom stream routing or multi-session architectures.

What's next

  • WebSocket Reference — every stream, method, and wire-format field
  • Order Book Reconstruction — snapshot + diff internals and gap recovery
  • Error handling — WebSocket-specific errors and connection failures
  • Patterns & recipes — heartbeat, liveness checks, and advanced configuration
Last modified on August 14, 2026
AuthenticationError Handling
On this page
  • Public streams
    • Closing a stream
  • Live order book
  • Authenticated streams
    • Browser limitation
    • WebSocket order operations
  • Reconnection
    • Stream state
  • GeminiWebSocket vs WsSession
  • What's next
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript