---
title: "Implementing a Reliable Coinbase WebSocket Feed in Node.js"
description: "Build a reliable Coinbase websocket feed Node.js implementation. This guide shows how to handle JWT auth, heartbeats, and reconnection with the coinbase-api SDK."
canonical: "https://siebly.io/blog/implementing-a-reliable-coinbase-websocket-feed-in-nodejs"
---

# Implementing a Reliable Coinbase WebSocket Feed in Node.js

Build a reliable Coinbase websocket feed Node.js implementation. This guide shows how to handle JWT auth, heartbeats, and reconnection with the coinbase-api SDK.

## Technical Disclaimer

These articles are software engineering references for exchange API integrations. They are not financial, investment, legal, tax, compliance, or trading advice. Use public data, demo, testnet, or paper workflows first. Keep API credentials out of frontend code and disable withdrawal permissions for automation keys.

## Overview {#overview}

Relying on legacy Coinbase Pro documentation for modern real-time data requirements is a direct path to integration failure. As engineering teams migrate to the Advanced Trade API, implementing a robust Coinbase websocket feed nodejs integration requires navigating JWT authentication and separate endpoints for market versus user data. You have likely hit the friction of managing 120 second token expirations or handling silent disconnections that leave gaps in your order book.

Infrastructure should stay out of your way so you can focus on system logic instead of connection state. This article shows how to cut integration boilerplate using the [coinbase-api](/sdk/coinbase/javascript) SDK from Siebly.io to build stable, production-ready streams. We cover the architectural split between public market data and private user feeds, credential handling, and heartbeat monitoring for connection health. By the end, you will have a working Coinbase integration in Node.js that ingests low-latency updates with far less maintenance overhead.

## Key Takeaways {#key-takeaways}

- Understand the distinction between public market data and authenticated user streams within the Coinbase Advanced Trade architecture.
- Simplify your Coinbase websocket feed nodejs integration by using the coinbase-api SDK to handle JWT signing for private subscriptions.
- Use reconnection logic and heartbeat monitoring to keep streams available and catch sequence gaps in real-time market data.
- Secure your environment with least-privilege API keys and proper secret management.
- Move from fragmented legacy docs to a unified, TypeScript-first SDK layer.



## Understanding the Coinbase WebSocket Architecture {#understanding-the-coinbase-websocket-architecture}

Coinbase uses a stateful, bidirectional [WebSocket protocol](https://en.wikipedia.org/wiki/WebSocket) to push market data and account updates. Unlike REST polling, a Coinbase websocket feed nodejs integration receives data the moment an event hits the matching engine. The connection stays open over TCP, and you send JSON control messages to subscribe to channels.

Public channels cover price discovery and market analysis. The ticker channel emits real-time price and 24-hour volume. level2 gives you an order book snapshot plus incremental updates. market_trades streams every trade on the platform. heartbeats sends a periodic pulse you can use to confirm the connection is alive, and it does not require authentication on the public market data endpoint.

Private channels need credentials. Streaming order fills, balance changes, and other account data goes through the user data WebSocket, where each subscribe request must include a signed JWT. That is what keeps local state in sync without constant REST polling.

### Advanced Trade vs Exchange Feeds {#advanced-trade-vs-exchange-feeds}

The right endpoint depends on which Coinbase product you are integrating with. Retail and professional traders use Advanced Trade:

- Public market data: wss://advanced-trade-ws.coinbase.com
- Private user data: wss://advanced-trade-ws-user.coinbase.com

Institutional setups may still use the Exchange API at wss://ws-feed.exchange.coinbase.com, which uses a different subscription format (channels instead of channel). Advanced Trade is the standard for most new Coinbase websocket feed nodejs projects. The [coinbase-api](/sdk/coinbase/javascript) SDK maps these endpoints internally via `WS_KEY_MAP`, so you subscribe by topic and `wsKey` rather than hardcoding URLs.

### Event-Driven Workflows in Node.js {#event-driven-workflows-in-node-js}

Node.js fits high-throughput market data well because of non-blocking I/O and the event loop. When a message arrives, the runtime queues it for processing. Keep CPU-heavy work off the main thread so you do not delay the next ticker update. A modular design where the WebSocket client emits events to dedicated handlers keeps the system responsive during volatile markets. That pattern also lets you scale data processing separately from connection management, which matters in a modern [algorithmic trading system architecture nodejs](/blog/algorithmic-trading-system-architecture-in-nodejs-a-2026-engineering-guide).

## Authenticating Private Streams with JWT in Node.js {#authenticating-private-streams-with-jwt-in-nodejs}

User-specific data over a Coinbase websocket feed nodejs connection needs more than a public ticker feed. Private channels on the `advTradeUserData` endpoint require a signed JSON Web Token built from your API Key Name and API Secret from the Coinbase Developer Platform.

Coinbase supports ECDSA keys signed with ES256 and Ed25519 keys signed with EdDSA. The SDK detects the key type automatically. Tokens expire after 120 seconds by default (`jwtExpiresSeconds`). If the signature is invalid or the token is expired when Coinbase processes your subscribe request, the connection will reject it.

### The Complexity of Manual Signing {#the-complexity-of-manual-signing}

A compliant JWT needs a precise header and payload. The header includes alg, typ, and kid. The payload needs sub, iss, nbf, and exp. Clock drift is a common failure mode in Node.js: if your server time is off by a few seconds, nbf or exp claims trigger Unauthorized errors. Fixing that in a custom module often means NTP sync or manual offset logic.

On Advanced Trade, JWT is attached to each private subscribe message, not just a one-time handshake. That means your signing logic runs on every subscription to the user data socket.

### Streamlining Auth with Siebly coinbase-api {#streamlining-auth-with-siebly-coinbase-api}

The [coinbase-api](/sdk/coinbase/javascript) package handles JWT generation internally. Pass credentials via environment variables or client options, and the SDK signs each private subscribe request on `advTradeUserData` automatically. It supports both ECDSA (PEM format) and Ed25519 (base64) keys.

```js title="Imported example"
import { WebsocketClient } from "coinbase-api";

const client = new WebsocketClient({
  apiKey: process.env.API_KEY_NAME,
  apiSecret: process.env.API_PRIVATE_KEY,
});

client.on("update", (data) => {
  console.log("user update:", data);
});

// Subscriptions on advTradeUserData are signed automatically
client.subscribe("user", "advTradeUserData");
```

For order execution, use the REST client. The SDK's `CBAdvancedTradeClient` covers that path with full TypeScript types:

```js title="Imported example"
import { CBAdvancedTradeClient } from "coinbase-api";

const client = new CBAdvancedTradeClient({
  apiKey: process.env.API_KEY_NAME,
  apiSecret: process.env.API_PRIVATE_KEY,
});

const order = await client.submitOrder({
  product_id: "BTC-USDT",
  order_configuration: {
limit_limit_gtc: {
base_size: "0.001",
limit_price: "50000.00",
},
  },
  side: "BUY",
  client_order_id: client.generateNewOrderId(),
});
```

See the [Coinbase JavaScript SDK guide](/sdk/coinbase/javascript) for key setup and permissions. For teams that want to avoid maintaining crypto code in-house, a maintained SDK like [Siebly.io](/) is the practical choice.

## Subscribing to Market Data and User Feeds {#subscribing-to-market-data-and-user-feeds}

Connecting is only the first step. Your Coinbase websocket feed nodejs client must send subscribe messages to start receiving events.

On Advanced Trade, each subscribe message looks like this:

```json title="Imported example"
{
  "type": "subscribe",
  "channel": "ticker",
  "product_ids": ["BTC-USD", "ETH-USD"]
}
```

The legacy Exchange API uses a different shape with a channels array instead of a single channel field. The SDK normalises both formats through the same subscribe() API.

### Managing Product Subscriptions {#managing-product-subscriptions}

Batch multiple pairs in one product_ids array to keep control traffic low. Coinbase confirms active subscriptions with a subscriptions response event. To scale down at runtime, send an unsubscribe for specific products without tearing down the whole connection.

With the SDK, pass topics as strings or structured objects. This matches `examples/AdvancedTrade/WebSockets/publicWs.ts` in the coinbase-api repo:

```js title="Imported example"
import { WebsocketClient } from "coinbase-api";

const client = new WebsocketClient();

client.on("open", (data) => console.log("connected:", data?.wsKey));
client.on("update", (data) => console.log("data:", data));
client.on("response", (data) => console.log("subscription confirmed:", data));
client.on("reconnect", () => console.log("reconnecting..."));
client.on("reconnected", () => console.log("back online"));
client.on("exception", (data) => console.error("error:", data));

// Simple topic
client.subscribe("heartbeats", "advTradeMarketData");

// Topic with parameters
client.subscribe(
  {
topic: "ticker",
payload: { product_ids: ["ETH-USD", "BTC-USD"] },
  },
  "advTradeMarketData",
);

// Multiple topics in one call
client.subscribe(
  [
{ topic: "market_trades", payload: { product_ids: ["ETH-USD"] } },
{ topic: "level2", payload: { product_ids: ["ETH-USD", "BTC-USD"] } },
  ],
  "advTradeMarketData",
);
```

### Processing Incoming Data Shapes {#processing-incoming-data-shapes}

Messages arrive as JSON. In production, route by channel (Advanced Trade) or type (Exchange API).

For Advanced Trade level2, the feed sends snapshot and update events inside an events array. Track sequence_num on each message. If you detect a gap after a disconnect, discard your local book and wait for a fresh snapshot before applying increments again.

The Exchange API uses snapshot and l2update message types instead. Same idea, different field names.

Monitor the heartbeats channel (or protocol-level ping/pong) so you know the stream is alive even when the market is quiet. The [coinbase-api](/sdk/coinbase/javascript) SDK emits typed `update`, `response`, and `exception` events, which keeps handlers predictable.

## Manual Integration vs SDK Approach {#manual-integration-vs-sdk-approach}

### Challenge: Manual JWT Authentication {#challenge-manual-jwt-authentication}

Implementing JWT for private streams from scratch is complex and error-prone. You handle signing, token lifecycle, and clock drift on every private subscribe.

- ES256 or EdDSA signing required for each private request
- 120 second token lifespan with strict timing
- High risk of Unauthorized from clock drift

### Solution: Automated SDK Authentication {#solution-automated-sdk-authentication}

The coinbase-api SDK handles signing behind the scenes.

- JWTs are generated and attached to each private subscribe request automatically
- On reconnect, cached subscriptions are re-sent with fresh JWTs
- Supports both ECDSA and Ed25519 key formats
- Connect with API keys and start receiving data in minutes

### Challenge: Fragile Connection Management {#challenge-fragile-connection-management}

A raw WebSocket needs boilerplate to survive network jitter, load balancer timeouts, and exchange maintenance.

- Silent disconnections cause data gaps in order books and trades
- You must wire up ping/pong monitoring and reconnection yourself
- Separate endpoints for public vs user data add routing complexity

### Solution: Production-Ready Reliability {#solution-production-ready-reliability}

The SDK ships with connection management built in.

- Automatic reconnection with resubscribe from cached topics
- Configurable ping interval (default 10s) and pong timeout (default 1s)
- One `subscribe()` API routed to the correct endpoint via `WS_KEY_MAP`

### Security Best Practices {#security-best-practices}

- Use least-privilege API keys for automated systems
- Never enable withdrawal permissions on keys used for data streaming
- Store credentials in environment variables or a secrets manager, never in source code

## Managing Reliability and Reconnections {#managing-reliability-and-reconnections}

WebSockets are stateful. A Coinbase websocket feed nodejs connection can look open while the data stream is dead. That happens during network jitter, load balancer timeouts, or exchange maintenance. Treat every connection as fragile and monitor it actively.

If you go too long without a message, assume the connection is dead even if the socket still shows open. On reconnect, space out attempts so you do not hit Coinbase's inbound rate limit (10 requests per second). The SDK reconnects with a configurable `reconnectTimeout` (500ms by default). For your own retry logic on top, exponential backoff is still a good idea.

For L2 data, sequence tracking is mandatory. Each Advanced Trade message carries a sequence_num. A gap after an outage means you should drop local state and resubscribe to get a clean snapshot.

### Heartbeats and Keep-Alive Patterns {#heartbeats-and-keep-alive-patterns}

Subscribe to heartbeats on the public market data socket for a regular pulse during quiet markets. Reset a "last seen" timestamp on each one.

The SDK also sends protocol-level pings on a `pingInterval` (10 seconds by default) and closes the socket if no pong arrives within `pongTimeout` (1 second by default). You can tune both in the client options:

```js title="Imported example"
import { WebsocketClient } from "coinbase-api";

const client = new WebsocketClient({
  pingInterval: 10000,
  pongTimeout: 1000,
  reconnectTimeout: 500,
});
```

### Error Handling Best Practices {#error-handling-best-practices}

Separate fatal errors from recoverable ones. Bad API keys or invalid signatures should stop the retry loop to avoid lockout. Network resets and timeouts are recoverable. Log state transitions (CONNECTING, OPEN, CLOSING, CLOSED) for production debugging.

The SDK emits `reconnect` and `reconnected` events so you can hook logging or alerts without building a state machine from scratch. You still own local state reconciliation (order book snapshots, fill tracking, etc.).

## Implementing with the Siebly Coinbase SDK {#implementing-with-the-siebly-coinbase-sdk}

The coinbase-api SDK wraps Advanced Trade, Exchange, International, Prime, and Commerce REST APIs, plus WebSocket feeds for Advanced Trade, Exchange, International, and Prime. Building a Coinbase websocket feed nodejs integration from raw WebSockets means writing signing, routing, and reconnection code yourself. Install it with `npm install coinbase-api` and you get a unified `WebsocketClient` plus REST clients like `CBAdvancedTradeClient`.

Here is a minimal end-to-end public feed:

```js title="Imported example"
import { WebsocketClient } from "coinbase-api";

const client = new WebsocketClient();

client.on("update", (data) => {
  if (data.channel === "ticker") {
console.log("price update:", data);
  }
});

client.subscribe(
  {
topic: "ticker",
payload: { product_ids: ["BTC-USD"] },
  },
  "advTradeMarketData",
);

client.subscribe("heartbeats", "advTradeMarketData");
```

For private fills and order updates, add credentials and subscribe on `advTradeUserData`. This follows `examples/AdvancedTrade/WebSockets/privateWs.ts`:

```js title="Imported example"
import { WebsocketClient } from "coinbase-api";

const client = new WebsocketClient({
  apiKey: process.env.API_KEY_NAME,
  apiSecret: process.env.API_PRIVATE_KEY,
});

client.on("update", (data) => {
  console.log(data.channel, data);
});

client.subscribe("user", "advTradeUserData");
client.subscribe("futures_balance_summary", "advTradeUserData");
```

The SDK does not handle rate limiting or throttling. You are responsible for staying within Coinbase's inbound message limits. It also does not implement `sendWSAPIRequest()` for Coinbase. That method exists in the shared WebSocket base class but returns immediately for coinbase-api. Other Siebly SDKs such as [binance](/sdk/binance/javascript), [bybit-api](/sdk/bybit/javascript), [okx-api](/sdk/okx/javascript), [kucoin-api](/sdk/kucoin/javascript), `kraken-api` ([@siebly/kraken-api](/sdk/kraken/javascript)), [gateio-api](/sdk/gate/javascript), and [bitget-api](/sdk/bitget/javascript) do support awaitable WebSocket API requests. Use the REST client for Coinbase order placement and account operations.

For comparison, here is how Kraken WebSocket order placement looks with `@siebly/kraken-api`:

```js title="Imported example"
import { WebsocketClient, WS_KEY_MAP } from "@siebly/kraken-api";

const client = new WebsocketClient({
  apiKey: process.env.API_SPOT_KEY,
  apiSecret: process.env.API_SPOT_SECRET,
});

const addOrderResponse = await client.sendWSAPIRequest(
  WS_KEY_MAP.spotPrivateV2,
  "add_order",
  {
order_type: "limit",
side: "buy",
limit_price: 26500.4,
order_qty: 1.2,
symbol: "BTC/USD",
  },
);
```

More Coinbase examples live in the SDK repo under `examples/AdvancedTrade/WebSockets/`.

### Reducing Boilerplate with the SDK {#reducing-boilerplate-with-the-sdk}

A raw Advanced Trade integration easily needs 50+ lines for JWT signing, endpoint selection, and subscribe formatting. The SDK cuts that to a handful of lines. It routes public traffic to `advTradeMarketData` and private traffic to `advTradeUserData`, re-subscribes cached topics after reconnect, and types request/response shapes across Coinbase products.

### Next Steps for Your Trading System {#next-steps-for-your-trading-system}

If you are maintaining custom WebSocket code with manual signing, migrating to coinbase-api reduces ongoing maintenance. The TypeScript types catch bad payloads at compile time. Check the [Siebly.io releases page](/releases) for SDK updates, and [Siebly AI patterns](/ai/patterns) if you are designing larger data pipelines.

## Scaling Your Real-Time Data Infrastructure {#scaling-your-real-time-data-infrastructure}

A production Coinbase websocket feed nodejs setup is about architecture, not just opening a socket. You need the right endpoints, JWT signing for private feeds, heartbeat monitoring, and sequence tracking for order books. Those patterns keep data ingestion reliable when exchange infrastructure hiccups.

Moving from raw WebSockets to the coinbase-api package lets your team focus on trading logic instead of transport code. The SDK covers Advanced Trade, Exchange, International, Prime, and Commerce with TypeScript support and automatic reconnection. That removes the maintenance cost of manual signing and fragmented docs.

[Build faster with the Siebly.io Coinbase SDK](/sdk/coinbase/javascript) and get typed real-time streams running in your Node.js environment.

## Frequently Asked Questions {#frequently-asked-questions}

### What is the difference between Coinbase Advanced Trade and Coinbase Pro WebSockets? {#what-is-the-difference-between-coinbase-advanced-trade-and-coinbase-pro-websockets}

Advanced Trade is the current standard, using wss://advanced-trade-ws.coinbase.com for public data and wss://advanced-trade-ws-user.coinbase.com for private data. Coinbase Pro is legacy with different auth and message shapes. For new Coinbase websocket feed nodejs work, use Advanced Trade. The [coinbase-api](/sdk/coinbase/javascript) SDK targets the current endpoints.

### How do I handle the 'Unauthorized' error when connecting to Coinbase WebSockets? {#how-do-i-handle-the-unauthorized-error-when-connecting-to-coinbase-websockets}

Usually clock drift or a bad signature. Coinbase expects a JWT signed with ES256 or EdDSA, expiring within 120 seconds. If server time is off, nbf or exp checks fail. The coinbase-api SDK generates and attaches JWTs to private subscribe requests so you do not have to debug signing by hand.

### Does the Siebly coinbase-api SDK handle rate limiting automatically? {#does-the-siebly-coinbase-api-sdk-handle-rate-limiting-automatically}

No. Siebly.io SDKs (`coinbase-api`, [binance](/sdk/binance/javascript), [bybit-api](/sdk/bybit/javascript), [okx-api](/sdk/okx/javascript), [kucoin-api](/sdk/kucoin/javascript), [bitget-api](/sdk/bitget/javascript), [gateio-api](/sdk/gate/javascript), [bitmart-api](/sdk/bitmart/javascript), [@siebly/kraken-api](/sdk/kraken/javascript), and the rest) do not throttle requests for you. Some clients can parse exchange rate-limit headers on REST responses, but none will slow your calls automatically. Stay within Coinbase's 10 requests per second inbound limit on WebSocket control messages. Exceeding it can trigger temporary bans.

### Can I use the Coinbase WebSocket feed for high-frequency trading in Node.js? {#can-i-use-the-coinbase-websocket-feed-for-high-frequency-trading-in-node-js}

Yes. Node.js event-driven I/O handles low-latency ticker and L2 ingestion well. For execution, use `CBAdvancedTradeClient` over REST. Coinbase's WebSocket API supports order commands at the protocol level, but coinbase-api does not implement awaitable WebSocket requests for Coinbase yet. SDKs like `binance`, `bybit-api`, `okx-api`, `kucoin-api`, and `@siebly/kraken-api` do support `sendWSAPIRequest()` or a `WebsocketAPIClient` wrapper.

### How many product IDs can I subscribe to in a single Coinbase WebSocket connection? {#how-many-product-ids-can-i-subscribe-to-in-a-single-coinbase-websocket-connection}

Standard accounts are limited to 10 subscriptions per product per channel. For more pairs, open additional connections or upgrade your subscription tier. Multiple connections need careful event loop management so high-throughput streams do not block processing.

### Is it possible to place orders through the Coinbase WebSocket feed? {#is-it-possible-to-place-orders-through-the-coinbase-websocket-feed}

Coinbase Advanced Trade supports WebSocket order commands in its API, but the coinbase-api SDK handles orders through the REST `CBAdvancedTradeClient.submitOrder()` method. That is the supported path today. WebSocket order placement via `sendWSAPIRequest()` is not implemented for Coinbase in this SDK.

### What is the best way to handle WebSocket reconnections without losing data? {#what-is-the-best-way-to-handle-websocket-reconnections-without-losing-data}

Track sequence_num on level2 and watch heartbeats for connection health. After a gap, discard local cache and resubscribe for a fresh snapshot. Use backoff between reconnect attempts to avoid rate limits. The SDK reconnects and resubscribes cached topics automatically, but you still reconcile local state yourself.

### Which Coinbase channels require a JWT for authentication? {#which-coinbase-channels-require-a-jwt-for-authentication}

On Advanced Trade, anything on the private user data endpoint (`advTradeUserData`) requires a signed JWT on each subscribe request: `user`, `futures_balance_summary`, and similar account channels. Public channels on `advTradeMarketData` (`ticker`, `level2`, `market_trades`, `heartbeats`) do not need credentials. Use least-privilege API keys with only the permissions your streams require.

## Related articles

- [Coinbase Advanced Trade Node.js Library: Production-Ready Implementation Guide](/blog/coinbase-advanced-trade-nodejs-library-production-ready-implementation-guide)
- [Building Reliable Crypto WebSocket Data Feeds in Node.js](/blog/building-reliable-crypto-websocket-data-feeds-in-nodejs)
- [Binance vs Bybit vs Coinbase API Integration Patterns](/blog/binance-bybit-coinbase-api-integration-patterns)


## Related Siebly Resources

- [Binance JavaScript SDK](/sdk/binance/javascript)
- [Bybit JavaScript SDK](/sdk/bybit/javascript)
- [OKX JavaScript SDK](/sdk/okx/javascript)
- [Siebly SDK directory](/sdk)
- [Siebly AI Prompt Framework & Skills](/ai)
