GeminiGemini
Demo environmentGet API key
  • Overview
  • Crypto Trading
  • Prediction Markets
  • Perpetuals
  • Stocks
  • API Reference
  • SDKs & Tools
Changelog
Gemini logoGemini logo

© 2026 Gemini Space Station, Inc.

TypeScript SDK — Patterns & Recipes

Common patterns for timeouts, cancellation, pagination, heartbeat, and advanced client configuration.

Timeouts and cancellation

Every SDK method accepts timeoutMs and signal for deadline control:

Code
// Per-request timeout const symbols = await client.marketData.listSymbols({ timeoutMs: 5_000, }); // Cancellation via AbortController const controller = new AbortController(); const events = client.predictions.listEvents( { status: ["active"] }, { signal: controller.signal }, ); // Cancel from elsewhere controller.abort();

Set a default timeout for all operations on the client:

Code
const client = await createClient({ env: "sandbox", auth, timeoutMs: 10_000, // 10s default for all REST and WebSocket waits });

WebSocket stream listeners also support signal-based cleanup:

Code
const trades = client.websocket.public.trades("BTCUSD"); const controller = new AbortController(); trades.on("message", (trade) => { console.log(trade); }, { signal: controller.signal }); // Later: remove the listener without manually calling off() controller.abort();

Pagination

Auto-paginating async iterators (for await)

High-level domain namespaces provide typed async generators that automatically handle page boundaries, buffering, and limit clamping:

Code
// Iterate positions across all pages effortlessly for await (const position of client.predictions.iteratePositions({}, { limit: 100 })) { console.log(`Position: ${position.instrumentId} — ${position.totalQuantity}`); }

Async iterators support bounds and cancellation:

Code
const controller = new AbortController(); for await (const position of client.predictions.iteratePositions({}, { maxItems: 250, // Stop automatically after 250 items signal: controller.signal, })) { if (shouldStop(position)) { controller.abort(); break; } }

Manual offset pagination

If you need low-level control over page retrieval:

Code
let offset = 0; const positions = []; while (positions.length < 500) { const page = await client.predictions.getPositions({ limit: 100, offset, }); const items = page.positions ?? []; positions.push(...items); if (items.length < 100) break; offset += items.length; }

Deduplication

Offset pagination is not snapshot-consistent — records can shift between pages while you're iterating. Track a stable identity when duplicate records must fail loudly:

Code
const seen = new Set<string>(); for (const position of positions) { const key = `${position.instrumentId}:${position.outcome}`; if (seen.has(key)) throw new Error(`Duplicate position: ${key}`); seen.add(key); }

Lossless Decimal Math (decimal)

To prevent IEEE-754 floating-point inaccuracies in financial calculations (e.g. 0.1 + 0.2 !== 0.3), the SDK includes a built-in, zero-dependency decimal utility powered by native BigInt scaling:

Code
import { decimal } from "@gemini-markets/sdk/server"; // Exact arithmetic on price/size strings const total = decimal.add("100.10", "200.20"); // "300.3" const spread = decimal.subtract("100.50", "100.25"); // "0.25" const fee = decimal.multiply("1000.00", "0.0015"); // "1.5" const unitPrice = decimal.divide("100", "3", 6); // "33.333333" // Financial rounding & comparisons const rounded = decimal.round("10.556", 2); // "10.56" const isCheaper = decimal.compare("99.99", "100.00"); // -1 const isPositive = decimal.isPositive("-5.00"); // false // Exponential / scientific notation parsing const btcUnits = decimal.normalize("1e-8"); // "0.00000001"

Explicit Resource Management (using / await using)

The SDK supports TypeScript 5.2+ explicit resource management (Symbol.dispose and Symbol.asyncDispose) for automatic cleanup of sockets, streams, and order books:

Code
// Automatically closes client and active sockets when leaving scope { await using client = await createClient({ env: "sandbox", auth }); const symbols = await client.marketData.listSymbols(); console.log(symbols); // Scoped WebSocket streaming const stream = client.websocket.public.trades("BTCUSD"); try { for await (const trade of stream) { console.log(trade); break; // stream is automatically unsubscribed & closed on break } } finally { await stream.close(); } } // client.close() is automatic; stream cleanup is explicit

Heartbeat

Keep a session alive with an explicit heartbeat. The heartbeat is stopped by default — you control its lifecycle:

Code
const heartbeat = client.createHeartbeat({ intervalMs: 15_000, // default onError: (err) => console.error("Heartbeat failed:", err), }); heartbeat.start(); // Later heartbeat.stop();

The heartbeat sends a POST /v1/heartbeat request at the configured interval. It requires an authenticated client (HMAC or OAuth).

File responses

Some endpoints return binary files (XLSX, CSV) instead of JSON:

Code
const report = await client.marketData.getFundingAmountReportFile({ symbol: "BTCGUSDPERP", }); // report.bytes: Uint8Array — the raw file content // report.contentType: string | undefined (e.g. "text/csv" or XLSX MIME type) // report.contentDisposition: string | undefined (Content-Disposition header) // Write to disk (Node.js) import { writeFileSync } from "node:fs"; const ext = report.contentType?.includes("csv") ? "csv" : "xlsx"; writeFileSync(`funding-report.${ext}`, report.bytes);

Prediction markets — terms acceptance

Prediction market order placement requires accepting the current terms. You can check status proactively for UI, but the order endpoint is authoritative:

Code
// Check terms status const terms = await client.predictions.getPredictionMarketsTermsStatus(); if (!terms.hasAcceptedLatest) { // Show terms to the user, get explicit consent, then: await client.predictions.acceptTerms(); } // Now orders will work await client.predictions.placeOrder({ /* ... */ });

If you skip this, the order endpoint returns AcceptTermsRequired. Handle that response by showing the terms, obtaining consent, accepting them, and retrying.

Advanced client options

The full set of options available on createClient():

Code
const client = await createClient({ // Environment env: "sandbox", // "sandbox" | "production" // Authentication auth: new HmacAuth({ apiKey, apiSecret }), // Timeouts and retries timeoutMs: 30_000, // default: 30s maxRetries: 5, // default: 5 (safe reads only) backoff: { baseMs: 500, // initial backoff (default: 500) capMs: 30_000, // maximum backoff factor: 2, // exponential factor }, // WebSocket skipWsInit: false, // true to skip ws preloading (REST-only) webSocketFactory: customFactory, // custom socket factory webSocketLiveness: { intervalMs: 30_000, // liveness check interval timeoutMs: 5_000, // liveness check timeout }, webSocketMaxMessageSizeBytes: 1_048_576, // 1MB frame limit // Custom fetch-compatible transport (for proxies, instrumentation, or testing) fetch: customFetchImpl, // Observability & Telemetry logger: new ConsoleLogger({ minLevel: "debug" }), onDiagnostic: (event) => telemetry.record(event), onRequest: (req) => console.log(`[HTTP] ${req.method} ${req.endpoint} (attempt ${req.attempt})`), onResponse: (res) => console.log(`[HTTP] ${res.status} in ${res.durationMs}ms`), });

Runtime compatibility

The SDK's REST and authentication layers run on runtimes with Web Crypto and standard fetch. WebSocket support additionally requires a native WebSocket; authenticated WebSockets require a socket factory that can set upgrade headers.

RuntimeBrowser entryServer entryNotes
Node.js 22.4+YesYesFull support; native WebSocket available
BunYesYesFull lossless integer support; authenticated WebSocket requires ws compatibility or a custom factory
DenoYesYesFull lossless integer support; authenticated WebSocket requires ws compatibility or a custom factory
Cloudflare WorkersYesNoNo ws package — browser entry only; full lossless integer support
BrowsersYesNoPublic WebSockets only; OAuth PKCE authenticates REST, while private WebSockets require a server or relay

The server entry requires the ws peer dependency for authenticated WebSocket connections:

TerminalCode
npm install ws

ws is optional — if you only use REST endpoints with skipWsInit: true, you don't need it.

Closing the client

Always close the client when done to release WebSocket connections:

Code
const client = await createClient({ env: "sandbox", auth }); try { // ... use the client } finally { client.close(); }

close() shuts down all active WebSocket streams and the shared session. It does not cancel in-flight REST requests.

What's next

  • API Reference — all 106 operations across 10 REST namespaces, with code examples
  • WebSocket Reference — streams, methods, and wire format
  • Deep dives — order book reconstruction, RFQ, sessions, transport internals
On this page
  • Timeouts and cancellation
  • Pagination
    • Auto-paginating async iterators (for await)
    • Manual offset pagination
    • Deduplication
  • Lossless Decimal Math (decimal)
  • Explicit Resource Management (using / await using)
  • Heartbeat
  • File responses
  • Prediction markets — terms acceptance
  • Advanced client options
  • Runtime compatibility
  • Closing the client
  • What's next
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript