Back to Coinbase JavaScript SDK
Advanced TradeSpotUS FuturesInternational PerpetualsPublic WebSocketsPrivate WebSocketsSandboxes

Coinbase API JavaScript Tutorial for Node.js

Build Coinbase integrations with Advanced Trade REST API calls, public and private WebSocket streams, safe order previews, reconnect recovery, sandbox guidance, and proxy support.

first-coinbase-api-call.js
public REST API
import { CBAdvancedTradeClient } from 'coinbase-api';

const client = new CBAdvancedTradeClient();

async function main() {
  try {
    const serverTime = await client.getServerTime();
    console.log('Server time:', serverTime.iso);
  } catch (error) {
    console.error('Server time request failed:', error);
  }

  try {
    const product = await client.getPublicProduct({
      product_id: 'BTC-USD',
    });
    console.log('BTC-USD price:', product.price);
    console.log('Base increment:', product.base_increment);
    console.log('Price increment:', product.price_increment);
  } catch (error) {
    console.error('Product request failed:', error);
  }

  try {
    const orderBook = await client.getPublicProductBook({
      product_id: 'BTC-USD',
      limit: 5,
    });
    console.log('Best bid:', orderBook.pricebook.bids[0]);
    console.log('Best ask:', orderBook.pricebook.asks[0]);
  } catch (error) {
    console.error('Order book request failed:', error);
  }

  const end = Math.floor(Date.now() / 1000);
  const start = end - 60 * 60;

  try {
    const candles = await client.getPublicProductCandles({
      product_id: 'BTC-USD',
      start: String(start),
      end: String(end),
      granularity: 'FIVE_MINUTE',
      limit: 12,
    });
    console.log('Latest candle:', candles.candles[0]);
  } catch (error) {
    console.error('Candles request failed:', error);
  }

  try {
    const marketTrades = await client.getPublicMarketTrades({
      product_id: 'BTC-USD',
      limit: 10,
    });
    console.log('Latest trade:', marketTrades.trades[0]);
    console.log('Best bid:', marketTrades.best_bid);
    console.log('Best ask:', marketTrades.best_ask);
  } catch (error) {
    console.error('Market trades request failed:', error);
  }
}

main();

API surface map

Choose the Coinbase client for the job

Start with Advanced Trade for retail trading, then use a specialized client only when the account belongs to another Coinbase API product.

Your app

Bot, dashboard, worker, tool

Any Node.js or JavaScript-compatible service that needs Coinbase market data, account state, order management, payments, or reconciliation.

npm package

npm install coinbase-api

CBAdvancedTradeClient

Advanced Trade products, accounts, orders, portfolios, derivatives, conversions, fees, and payment methods

WebsocketClient

Advanced Trade, Exchange, International, and Prime WebSocket subscriptions

CBAppClient

Coinbase App accounts, addresses, transactions, deposits, withdrawals, and public prices

CBExchangeClient

Institutional Exchange market data, accounts, orders, profiles, reports, loans, and transfers

CBInternationalClient

International instruments, portfolios, orders, positions, loans, and transfers

CBPrimeClient

Prime portfolios, balances, orders, activities, allocations, wallets, and transactions

CBCommerceClient

Legacy Commerce charges, checkouts, and events

Coinbase APIs

Advanced Trade REST API calls

Advanced Trade public and private streams

Coinbase App account and transfer calls

Exchange and International institutional APIs

Prime trading and custody APIs

Legacy Commerce APIs

Request routing

Product, portfolio, connection, and API family are separate choices

Keep these values explicit so each request reaches the intended Coinbase product and account.

Product ID

product_id: 'BTC-USD'

Select an Advanced Trade Spot, futures, or perpetual product.

Product metadata

base_increment and price_increment

Apply current size and price rules before an order request.

Portfolio

portfolio_uuid

Scope balances, positions, and permissions to the intended portfolio.

Client order ID

client_order_id

Give an application-owned order a stable identifier.

WebSocket connection

WS_KEY_MAP.advTradeMarketData

Route a subscription to the correct public or private connection.

API family

CBAdvancedTradeClient

Select the REST API and authentication model used by the account.

What this tutorial covers

What to get right in a Coinbase integration

Start with Advanced Trade public data, then add credentials, private state, streams, safe validation, network configuration, and recovery.

Advanced Trade REST API calls

Read endpoint-specific response objects and let the SDK generate JWTs for private requests.

Public and private WebSocket streams

Select the correct connection with WS_KEY_MAP and distinguish acknowledgements from updates.

Safe order previews

Validate a current order configuration without submitting it to the matching engine.

API families, sandboxes, and proxies

Match the client to the Coinbase account, then configure the intended environment and network route.

Start building

First REST API calls, streams, order preview, recovery, and proxies

Run one focused JavaScript example at a time, then add production controls around the same clients.

import { CBAdvancedTradeClient } from 'coinbase-api'; const client = new CBAdvancedTradeClient(); async function main() {  try {    const serverTime = await client.getServerTime();    console.log('Server time:', serverTime.iso);  } catch (error) {    console.error('Server time request failed:', error);  }   try {    const product = await client.getPublicProduct({      product_id: 'BTC-USD',    });    console.log('BTC-USD price:', product.price);    console.log('Base increment:', product.base_increment);    console.log('Price increment:', product.price_increment);  } catch (error) {    console.error('Product request failed:', error);  }   try {    const orderBook = await client.getPublicProductBook({      product_id: 'BTC-USD',      limit: 5,    });    console.log('Best bid:', orderBook.pricebook.bids[0]);    console.log('Best ask:', orderBook.pricebook.asks[0]);  } catch (error) {    console.error('Order book request failed:', error);  }   const end = Math.floor(Date.now() / 1000);  const start = end - 60 * 60;   try {    const candles = await client.getPublicProductCandles({      product_id: 'BTC-USD',      start: String(start),      end: String(end),      granularity: 'FIVE_MINUTE',      limit: 12,    });    console.log('Latest candle:', candles.candles[0]);  } catch (error) {    console.error('Candles request failed:', error);  }   try {    const marketTrades = await client.getPublicMarketTrades({      product_id: 'BTC-USD',      limit: 10,    });    console.log('Latest trade:', marketTrades.trades[0]);    console.log('Best bid:', marketTrades.best_bid);    console.log('Best ask:', marketTrades.best_ask);  } catch (error) {    console.error('Market trades request failed:', error);  }} main();

Workflow diagrams

REST API, WebSocket, and order-preview workflows

REST API results, subscription acknowledgements, stream updates, and order previews carry different information.

REST API request flow

Choose the Advanced Trade method, let the SDK sign private requests, then read the endpoint-specific result.

Choose product and methodYour code
Call the SDK clientYour code
Route and sign if privateSDK handles
Receive endpoint responseCoinbase
Use returned fieldsYour code

WebSocket subscription flow

Choose a topic and wsKey, let the SDK connect and authenticate when needed, then process acknowledgements and updates separately.

Choose topic and wsKeyYour code
Call subscribeYour code
Connect and sign if privateSDK handles
Receive subscriptions responseEvent
Process update eventsYour code

Safe order-preview flow

Read current product rules, preview the order, inspect validation results, and stop without submitting an order.

Read product metadataYour code
Build order configurationYour code
Call previewOrderYour code
Inspect errors and totalsCoinbase
Stop before submissionYour code

Production rollout

Before a Coinbase integration trades unattended

Credentials, product rules, response handling, WebSocket continuity, recovery, and network behavior must all be predictable.

Keep API keys server-side and grant only the permissions each process needs.

Keep product ID, portfolio, API family, and wsKey explicit in code and logs.

Set client_order_id on application-owned orders so retries and restarts can reconcile them.

Read product increments, minimum sizes, status flags, and venue before validating an order.

Check both HTTP errors and operation-level success fields on order-management responses.

Treat an accepted write as pending until a private stream or REST API query confirms state.

Subscribe to heartbeats and reload private state after stream gaps.

Keep the system clock synchronized and respect current Coinbase rate limits.

Monitor proxy reachability, latency, and egress IP when a proxy is enabled.

Choose your path

Jump to the Coinbase workflow you are building

Browse all examples

Start with REST API calls

Make public market calls, add credentials, then inspect accounts and order state.

Open section

Stream live data

Subscribe to public ticker data and private Advanced Trade order updates.

Open section

Understand order management

Preview an order safely, then learn how submission, lookup, cancellation, and confirmation fit together.

Open section

Configure connectivity

Match the client to the Coinbase environment, then add an HTTP or SOCKS proxy when required.

Open section

Build around Coinbase products and account state

This tutorial covers the Coinbase API pieces developers usually need first: Advanced Trade REST API calls, public and private streams, safe order previews, API environments, proxies, and reconnect recovery.

This tutorial uses coinbase-api, Siebly's Node.js and JavaScript SDK for Coinbase. This guide will primarily walk you through using the Coinbase Advanced Trade APIs & WebSockets for public market data, authenticated account access, WebSocket streams, and safe order previews.

The SDK handles REST API authentication, short-lived JWT creation, product-specific WebSocket routing, private subscription signing, reconnects, and resubscriptions. It also provides focused clients for Coinbase App, Exchange, International Exchange, Prime, and legacy Commerce APIs.

Key links

Why use coinbase-api?

Private Advanced Trade REST API requests use short-lived JSON Web Tokens signed with a Coinbase Developer Platform API key. Private WebSocket subscriptions also need a fresh JWT, and each subscription must reach the correct Coinbase connection.

coinbase-api handles those mechanics and provides a focused client for each API family, so you can focus on your integration without excessive plumbing efforts:

ClientUse it for
CBAdvancedTradeClientAdvanced Trade products, accounts, orders, portfolios, derivatives, fees, and converts
WebsocketClientAdvanced Trade, Exchange, International, and Prime WebSocket streams
CBAppClientCoinbase App accounts, addresses, transactions, deposits, withdrawals, and prices
CBExchangeClientInstitutional Exchange market data, trading, profiles, reports, loans, and transfers
CBInternationalClientInternational instruments, portfolios, orders, positions, loans, and transfers
CBPrimeClientPrime portfolios, balances, orders, activities, allocations, wallets, and transactions
CBCommerceClientLegacy Commerce charges, checkouts, and events

The Advanced Trade product group is the right starting point for most developers automating their own Coinbase trading account. The other REST API clients use different account products, credentials, permissions, and sometimes an API passphrase. These are typically catered more towards institutional users.

Install the SDK

npm install coinbase-api

The examples use standard ES module imports. The package also supports CommonJS projects.

Create Coinbase API keys

Public Advanced Trade REST API calls and public WebSocket streams do not need credentials. Private calls and the private user stream need a Coinbase Developer Platform secret API key.

Store the key name and private key in environment variables:

COINBASE_API_KEY_NAME
COINBASE_API_PRIVATE_KEY

The key name is the API key identifier returned by Coinbase. For ECDSA keys it normally has the form organizations/{organization_id}/apiKeys/{key_id}. The private key is the secret signing key, not a Coinbase account password.

coinbase-api accepts the current ECDSA PEM and base64 Ed25519 key formats and detects the signing algorithm automatically. Preserve the private key exactly, including PEM boundaries and line breaks when using an ECDSA key.

When creating a key:

  • Grant view permission for private account reads and order previews.
  • Grant trade only to a process that will place or manage orders.
  • Grant transfer or receive only when the process genuinely needs those actions.
  • Restrict the key to the intended portfolio.
  • Add an IP allowlist when the process has stable outbound addresses.
  • Keep the private key in a server-side secret store and never send it to browser code.

Nothing executable in this tutorial needs transfer or withdrawal access. The order example uses previewOrder() and does not submit an order.

Advanced Trade and Coinbase App keys do not use an API passphrase. Coinbase Exchange, International Exchange, and Prime credentials use a separate API key, secret, and passphrase.

Coinbase products and request vocabulary

Several values determine which market, account, and connection a request uses:

Field or valueExampleMeaning
Product IDBTC-USDHyphen-separated trading product with BTC as the base and USD as the quote currency
base_incrementProduct metadataSmallest supported change to a base-currency size
quote_incrementProduct metadataSmallest supported change to a quote-currency size
price_incrementProduct metadataSmallest supported change to an order price
base_min_sizeProduct metadataMinimum supported base-currency size
quote_min_sizeProduct metadataMinimum supported quote-currency size
Product status flagsstatus, trading_disabled, limit_onlyCurrent restrictions that affect valid order requests
Product type and venueproduct_type, product_venueDistinguish Spot, US futures, and International perpetual products
Portfolio IDportfolio_uuidPortfolio that owns balances, permissions, orders, or derivative positions
Client order IDclient_order_idApplication-owned custom order ID
Order configurationmarket_market_ioc, limit_limit_gtcOrder type, size, time in force, price, and post-only behavior
WebSocket keyWS_KEY_MAP.advTradeMarketDataWebSocket key selecting the connection

Advanced Trade REST API methods return the endpoint body directly. There is no shared success envelope. Products are objects, accounts and orders are arrays nested inside response objects, and candles are objects inside a candles array.

Start building: first calls and streams

Run each example on its own. Start with public market data, then add credentials, private state, streams, and an order preview.

1. Make public REST API calls

Public calls need no API key.

import { CBAdvancedTradeClient } from 'coinbase-api';

const client = new CBAdvancedTradeClient();

async function main() {
  try {
    const serverTime = await client.getServerTime();
    console.log('Server time:', serverTime.iso);
  } catch (error) {
    console.error('Server time request failed:', error);
  }

  try {
    const product = await client.getPublicProduct({
      product_id: 'BTC-USD',
    });
    console.log('BTC-USD price:', product.price);
    console.log('Base increment:', product.base_increment);
    console.log('Price increment:', product.price_increment);
  } catch (error) {
    console.error('Product request failed:', error);
  }

  try {
    const orderBook = await client.getPublicProductBook({
      product_id: 'BTC-USD',
      limit: 5,
    });
    console.log('Best bid:', orderBook.pricebook.bids[0]);
    console.log('Best ask:', orderBook.pricebook.asks[0]);
  } catch (error) {
    console.error('Order book request failed:', error);
  }

  const end = Math.floor(Date.now() / 1000);
  const start = end - 60 * 60;

  try {
    const candles = await client.getPublicProductCandles({
      product_id: 'BTC-USD',
      start: String(start),
      end: String(end),
      granularity: 'FIVE_MINUTE',
      limit: 12,
    });
    console.log('Latest candle:', candles.candles[0]);
  } catch (error) {
    console.error('Candles request failed:', error);
  }

  try {
    const marketTrades = await client.getPublicMarketTrades({
      product_id: 'BTC-USD',
      limit: 10,
    });
    console.log('Latest trade:', marketTrades.trades[0]);
    console.log('Best bid:', marketTrades.best_bid);
    console.log('Best ask:', marketTrades.best_ask);
  } catch (error) {
    console.error('Market trades request failed:', error);
  }
}

main();

The methods return different object shapes:

MethodSuccessful result
getServerTime()Object containing iso, epochSeconds, and epochMillis
getPublicProduct()One product object
getPublicProductBook()Object containing pricebook, whose bids and asks are arrays of price-level objects
getPublicProductCandles()Object containing a candles array
getPublicMarketTrades()Object containing trades, best_bid, and best_ask

Each candle has named start, low, high, open, close, and volume fields. Prices, quantities, increments, and timestamps are commonly strings. Keep exchange values as strings until a calculation requires a deliberate conversion.

2. Make private REST API calls

The private client uses the same Advanced Trade class with a key name and private key. The SDK creates and signs a new JWT for each private request.

import { CBAdvancedTradeClient } from 'coinbase-api';

async function main() {
  if (
    !process.env.COINBASE_API_KEY_NAME ||
    !process.env.COINBASE_API_PRIVATE_KEY
  ) {
    console.error('Set COINBASE_API_KEY_NAME and COINBASE_API_PRIVATE_KEY.');
    return;
  }

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

  try {
    const permissions = await client.getApiKeyPermissions();
    console.log('Can view:', permissions.can_view);
    console.log('Can trade:', permissions.can_trade);
    console.log('Can transfer:', permissions.can_transfer);
    console.log('Portfolio:', permissions.portfolio_uuid);
  } catch (error) {
    console.error('Permission request failed:', error);
  }

  try {
    const accounts = await client.getAccounts({
      limit: 10,
    });
    console.log('Accounts:', accounts.accounts);
    console.log('More accounts available:', accounts.has_next);
  } catch (error) {
    console.error('Account request failed:', error);
  }

  try {
    const openOrders = await client.getOrders({
      order_status: ['OPEN'],
      limit: 20,
    });
    console.log('Open orders:', openOrders.orders);
    console.log('More orders available:', openOrders.has_next);
  } catch (error) {
    console.error('Open-order request failed:', error);
  }

  try {
    const fills = await client.getFills({
      product_ids: ['BTC-USD'],
      limit: 20,
    });
    console.log('Recent BTC-USD fills:', fills.fills);
    console.log('Next cursor:', fills.cursor);
  } catch (error) {
    console.error('Fill request failed:', error);
  }
}

main();

getAccounts() and getOrders() expose pagination fields alongside their arrays. getFills() returns its fills array and an optional cursor. Follow the returned cursor when the application needs more than one page.

HTTP failures are rejected as parsed errors containing the status, response body, headers, and request context. Credentials are omitted from those parsed error objects. Order-management methods can also return a successful HTTP response whose operation-level success field is false, so both layers must be checked.

3. Subscribe to public Advanced Trade WebSocket streams

Use the Advanced Trade market-data connection for public channels. Subscribe to heartbeats alongside the ticker so the connection remains active when another channel has no updates.

import { WebsocketClient, WS_KEY_MAP } from 'coinbase-api';

function main() {
  const ws = new WebsocketClient();

  ws.on('open', ({ wsKey }) => {
    console.log('WebSocket opened:', wsKey);
  });

  ws.on('response', (response) => {
    console.log(
      'WebSocket response:',
      response.wsKey,
      response.channel,
      response.events,
    );
  });

  ws.on('update', (update) => {
    console.log(
      'WebSocket update:',
      update.wsKey,
      update.channel,
      update.events,
    );
  });

  ws.on('reconnect', ({ wsKey }) => {
    console.log('Reconnecting:', wsKey);
  });

  ws.on('reconnected', ({ wsKey }) => {
    console.log('Reconnected:', wsKey);
  });

  ws.on('close', ({ wsKey }) => {
    console.log('WebSocket closed:', wsKey);
  });

  ws.on('exception', (error) => {
    console.error('WebSocket exception:', error);
  });

  try {
    ws.subscribe(
      {
        topic: 'ticker',
        payload: {
          product_ids: ['BTC-USD'],
        },
      },
      WS_KEY_MAP.advTradeMarketData,
    );
  } catch (error) {
    console.error('Ticker subscription failed:', error);
  }

  try {
    ws.subscribe('heartbeats', WS_KEY_MAP.advTradeMarketData);
  } catch (error) {
    console.error('Heartbeat subscription failed:', error);
  }

  process.once('SIGINT', () => {
    ws.closeAll();
  });
}

main();

A message whose channel is subscriptions is the subscription acknowledgement. Market snapshots and later changes arrive through update, where each event has a channel-specific shape and a type such as snapshot or update.

The SDK adds wsKey to emitted events so one handler can identify the source connection. After an unexpected disconnect, it reconnects and restores tracked subscriptions.

4. Subscribe to the private Advanced Trade user stream

The private user-data connection provides order and position updates for the authenticated account. The SDK signs each subscription with a fresh JWT.

import { WebsocketClient, WS_KEY_MAP } from 'coinbase-api';

function main() {
  if (
    !process.env.COINBASE_API_KEY_NAME ||
    !process.env.COINBASE_API_PRIVATE_KEY
  ) {
    console.error('Set COINBASE_API_KEY_NAME and COINBASE_API_PRIVATE_KEY.');
    return;
  }

  const ws = new WebsocketClient({
    apiKey: process.env.COINBASE_API_KEY_NAME,
    apiSecret: process.env.COINBASE_API_PRIVATE_KEY,
  });

  ws.on('open', ({ wsKey }) => {
    console.log('Private WebSocket opened:', wsKey);
  });

  ws.on('response', (response) => {
    console.log(
      'Private WebSocket response:',
      response.wsKey,
      response.channel,
      response.events,
    );
  });

  ws.on('update', (update) => {
    console.log('Private update:', update.wsKey, update.channel, update.events);
  });

  ws.on('reconnect', ({ wsKey }) => {
    console.log('Private WebSocket reconnecting:', wsKey);
  });

  ws.on('reconnected', ({ wsKey }) => {
    console.log('Private WebSocket reconnected:', wsKey);
  });

  ws.on('close', ({ wsKey }) => {
    console.log('Private WebSocket closed:', wsKey);
  });

  ws.on('exception', (error) => {
    console.error('Private WebSocket exception:', error);
  });

  try {
    ws.subscribe(
      {
        topic: 'user',
        payload: {
          product_ids: ['BTC-USD'],
        },
      },
      WS_KEY_MAP.advTradeUserData,
    );
  } catch (error) {
    console.error('User subscription failed:', error);
  }

  try {
    ws.subscribe('heartbeats', WS_KEY_MAP.advTradeUserData);
  } catch (error) {
    console.error('Heartbeat subscription failed:', error);
  }

  process.once('SIGINT', () => {
    ws.closeAll();
  });
}

main();

The first user messages contain the account's current open orders, followed by later order changes. Coinbase can split a large initial snapshot across multiple messages. Preserve the event type and sequence information when building local account state.

An eligible derivatives account can also subscribe to futures_balance_summary through WS_KEY_MAP.advTradeUserData. Keep that subscription separate from user because Coinbase requires one channel per subscription request.

Private order updates provide private stream confirmation after a write. A subscription acknowledgement only confirms the stream subscription, not an order state.

5. Preview a Spot order with the REST API

previewOrder() validates an order configuration and returns estimated totals, commission, market prices, warnings, and validation errors. It does not submit an order to the matching engine.

This example reads the current BTC-USD minimum quote size and uses it for a market-buy preview.

import { CBAdvancedTradeClient } from 'coinbase-api';

async function main() {
  if (
    !process.env.COINBASE_API_KEY_NAME ||
    !process.env.COINBASE_API_PRIVATE_KEY
  ) {
    console.error('Set COINBASE_API_KEY_NAME and COINBASE_API_PRIVATE_KEY.');
    return;
  }

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

  let product;

  try {
    product = await client.getPublicProduct({
      product_id: 'BTC-USD',
    });
    console.log('Minimum quote size:', product.quote_min_size);
  } catch (error) {
    console.error('Product request failed:', error);
    return;
  }

  if (
    !product.quote_min_size ||
    !Number.isFinite(Number(product.quote_min_size)) ||
    Number(product.quote_min_size) <= 0
  ) {
    console.error('BTC-USD returned an invalid quote_min_size.');
    return;
  }

  try {
    const preview = await client.previewOrder({
      product_id: 'BTC-USD',
      side: 'BUY',
      order_configuration: {
        market_market_ioc: {
          quote_size: product.quote_min_size,
        },
      },
    });

    console.log('Validation errors:', preview.errs);
    console.log('Warnings:', preview.warning);
    console.log('Order total:', preview.order_total);
    console.log('Commission total:', preview.commission_total);
    console.log('Best bid:', preview.best_bid);
    console.log('Best ask:', preview.best_ask);
    console.log('Preview ID:', preview.preview_id);
  } catch (error) {
    console.error('Order preview failed:', error);
  }
}

main();

The preview endpoint needs view permission. Check errs before using any estimate. Warnings may describe conditions that do not make the request invalid, while an error means the configuration cannot currently be accepted.

preview_id identifies this preview result. Passing it to a later order request does not remove the need to validate current product rules and check the order response. This tutorial stops after the preview and never calls submitOrder().

Work with the Advanced Trade REST API

Understand response shapes

Advanced Trade REST API methods return the endpoint body. The top-level shape depends on the method:

OperationMain response fields
List accountsaccounts, has_next, cursor, size
List ordersorders, has_next, optional cursor and sequence
List fillsfills, optional cursor
Product bookpricebook, with bids, asks, and time
Product candlescandles
Market tradestrades, best_bid, best_ask
Submit ordersuccess, success_response, error_response, order_configuration
Cancel ordersresults, with one result for each requested order ID
Preview ordererrs, warning, totals, sizes, market prices, and preview_id

Do not assume that a successful HTTP status means an order-management operation succeeded. For example, submitOrder() can return success: false with details under error_response.

Read account and order state

The private methods used most often for account state are:

MethodPurpose
getApiKeyPermissions()Read the key's current permissions and portfolio scope
getAccounts()List currency accounts, available balances, and holds
getAccount()Read one currency account
getOrders()List orders using product, status, side, type, and time filters
getOrder()Read one order by exchange order ID
getFills()List fills by product, order, trade, time, or cursor
getTransactionSummary()Read current volume and fee-tier information

Use cursors until the required history window is complete. Keep product IDs, order IDs, client order IDs, and cursor values with the data they identify.

Understand Advanced Trade order management

Advanced Trade order commands use CBAdvancedTradeClient. The WebsocketClient provides streams and does not send order commands.

Choose an order configuration

The request's order_configuration contains one order type. Common configurations include:

ConfigurationUse
market_market_iocMarket order using a base or quote size
limit_limit_gtcLimit order that remains active until filled or cancelled
limit_limit_gtdLimit order with an expiry time
limit_limit_fokLimit order that must fill completely and immediately or be cancelled
sor_limit_iocSmart order-router limit order with immediate-or-cancel behavior
Stop and bracket configurationsConditional exits and attached risk controls

A limit_limit_gtc configuration can set post_only: true. A post-only order is rejected or cancelled rather than immediately taking liquidity. It can still fill after resting on the book, so it is not a risk-free live test.

Always apply current base_increment, quote_increment, price_increment, minimum sizes, maximum sizes, and product status flags. Derivative products add contract, margin, session, and eligibility rules.

Use client order IDs

Call client.generateNewOrderId() when an application will submit an order. The SDK generates an identifier with its required cbnode prefix. If a supplied client_order_id does not have that prefix, the SDK adds it and logs a warning.

Persist both the custom order ID and Coinbase order ID. The client order ID lets a process reconcile an uncertain request after a timeout or restart.

Follow the order lifecycle

The main REST API methods are:

MethodOperation
previewOrder()Validate and estimate an order without submitting it
submitOrder()Submit a new order
getOrder()Query one order
getOrders()List matching orders
updateOrderPreview()Preview an order edit
updateOrder()Edit an eligible order
cancelOrders()Request cancellation for one or more order IDs

For a submitted order:

  1. Check the HTTP request outcome.
  2. Check the returned success field.
  3. Read the Coinbase order ID from success_response only when success is true.
  4. Treat the result as pending confirmation.
  5. Confirm the final state through the private user stream or getOrder().

If a write times out without a clear result, query by the known Coinbase order ID or reconcile the client order ID before retrying. Blindly submitting the same intent again can create a second live order.

Know what WebSockets do

coinbase-api does not expose a WebsocketAPIClient for Coinbase because its supported Coinbase WebSocket interfaces are subscription feeds. Use:

  • WebsocketClient for market and account updates.
  • CBAdvancedTradeClient for preview, submit, edit, query, and cancel commands.

A subscription response acknowledges a channel. It is not an order acknowledgement and does not confirm account state.

Explore other Advanced Trade groups

The Advanced Trade client covers more than Spot orders:

GroupRepresentative methods
Authenticated market datagetProducts(), getProduct(), getBestBidAsk(), getProductBook()
PortfoliosgetPortfolios(), getPortfolioBreakdown(), createPortfolio(), updatePortfolio()
FeesgetTransactionSummary()
ConvertssubmitConvertQuote(), getConvertTrade(), commitConvertTrade()
Payment methodsgetPaymentMethods(), getPaymentMethod()
US futuresgetFuturesBalanceSummary(), getFuturesPositions(), getFuturesSweeps()
Futures margingetIntradayMarginSetting(), getCurrentMarginWindow()
International perpetualsgetPerpetualsPortfolioSummary(), getPerpetualsPositions(), getPortfoliosBalances()
International collateralallocatePortfolio(), updateMultiAssetCollateral()

US futures and International perpetual products are available only to eligible accounts and jurisdictions. Read product metadata and account state rather than assuming that a product visible in public data is tradable by the authenticated portfolio.

For the full method-to-endpoint map, use the Coinbase SDK endpoint reference.

Build with Coinbase WebSocket streams

Choose the correct WebSocket key

Each wsKey identifies one Coinbase WebSocket connection:

WebSocket keyAuthenticationUse
WS_KEY_MAP.advTradeMarketDataPublicAdvanced Trade ticker, level 2, trades, candles, and status
WS_KEY_MAP.advTradeUserDataRequiredAdvanced Trade user orders and futures balance summaries
WS_KEY_MAP.exchangeMarketDataPublicCoinbase Exchange market-data feed
WS_KEY_MAP.exchangeDirectMarketDataRequiredCoinbase Exchange direct market-data feed
WS_KEY_MAP.internationalMarketDataRequiredCoinbase International market-data feed
WS_KEY_MAP.primeMarketDataRequiredCoinbase Prime market-data feed

Pass WS_KEY_MAP values explicitly. This keeps connection routing visible and prevents a topic from reaching the wrong API family.

Use the Advanced Trade channels

ChannelAuthenticationMain use
heartbeatsPublicKeep subscriptions open and detect missed heartbeat counts
tickerPublicPrice, volume, and best bid or ask changes
ticker_batchPublicBatched ticker changes
market_tradesPublicRecent trade updates
level2PublicOrder-book snapshots and updates
candlesPublicFive-minute candle updates
statusPublicProduct and currency status
userRequiredCurrent open orders and later order or position changes
futures_balance_summaryRequiredFutures balance changes for eligible accounts

Coinbase requires one channel per Advanced Trade subscription request. Send separate subscribe() calls when a process needs ticker and heartbeat data.

Treat responses and updates differently

The SDK emits:

  • open when a connection is ready.
  • response for subscription responses and exchange errors.
  • update for snapshots and later channel data.
  • reconnect when it begins restoring a dropped connection.
  • reconnected after the replacement connection opens.
  • close when a connection closes.
  • exception for connection, parsing, signing, or subscription failures.

Keep each event's wsKey, channel, sequence_num, and event type. A subscriptions response confirms the requested channel is active. It does not replace the initial channel snapshot or later updates.

Recover private state after a reconnect

Automatic reconnection restores the socket and cached subscriptions. It cannot prove that every private event was received before or during the gap. Rebuild the affected account state through the REST API after advTradeUserData reconnects.

This example reloads accounts, open orders, and recent BTC-USD fills sequentially. It replaces local state only when all three reads succeed.

import {
  CBAdvancedTradeClient,
  WebsocketClient,
  WS_KEY_MAP,
} from 'coinbase-api';

async function main() {
  if (
    !process.env.COINBASE_API_KEY_NAME ||
    !process.env.COINBASE_API_PRIVATE_KEY
  ) {
    console.error('Set COINBASE_API_KEY_NAME and COINBASE_API_PRIVATE_KEY.');
    return;
  }

  const credentials = {
    apiKey: process.env.COINBASE_API_KEY_NAME,
    apiSecret: process.env.COINBASE_API_PRIVATE_KEY,
  };

  const rest = new CBAdvancedTradeClient(credentials);
  const ws = new WebsocketClient(credentials);

  let accountState;

  ws.on('update', (update) => {
    console.log('Private update:', update.channel, update.events);
  });

  ws.on('reconnected', async ({ wsKey }) => {
    if (wsKey !== WS_KEY_MAP.advTradeUserData) {
      return;
    }

    let accountsResponse;
    let ordersResponse;
    let fillsResponse;

    try {
      accountsResponse = await rest.getAccounts({
        limit: 250,
      });
    } catch (error) {
      console.error('Account recovery failed:', error);
      return;
    }

    try {
      ordersResponse = await rest.getOrders({
        order_status: ['OPEN'],
        limit: 250,
      });
    } catch (error) {
      console.error('Open-order recovery failed:', error);
      return;
    }

    try {
      fillsResponse = await rest.getFills({
        product_ids: ['BTC-USD'],
        limit: 100,
      });
    } catch (error) {
      console.error('Fill recovery failed:', error);
      return;
    }

    const replacement = {
      accounts: accountsResponse.accounts,
      openOrders: ordersResponse.orders,
      recentFills: fillsResponse.fills,
      recoveredAt: new Date().toISOString(),
    };

    accountState = replacement;
    console.log('Private state recovered:', accountState.recoveredAt);
  });

  ws.on('exception', (error) => {
    console.error('WebSocket exception:', error);
  });

  try {
    ws.subscribe(
      {
        topic: 'user',
        payload: {
          product_ids: ['BTC-USD'],
        },
      },
      WS_KEY_MAP.advTradeUserData,
    );
  } catch (error) {
    console.error('User subscription failed:', error);
  }

  try {
    ws.subscribe('heartbeats', WS_KEY_MAP.advTradeUserData);
  } catch (error) {
    console.error('Heartbeat subscription failed:', error);
  }

  process.once('SIGINT', () => {
    ws.closeAll();
  });
}

main();

For a complete implementation, paginate until the required recovery window is covered. A production state owner should also buffer new private updates while recovery is running, apply the replacement snapshot, and then replay updates newer than the snapshot boundary.

See:

Choose the Coinbase API family

The package covers several Coinbase products. Their credentials and account models are not interchangeable.

ClientRepresentative coverage
CBAdvancedTradeClientRetail trading, portfolios, Spot, US futures, International perpetuals, fees, and converts
CBAppClientCoinbase App accounts, addresses, transactions, deposits, withdrawals, and public prices
CBExchangeClientInstitutional Exchange products, accounts, orders, fills, profiles, reports, loans, and transfers
CBInternationalClientInternational instruments, portfolios, balances, positions, orders, fills, loans, and transfers
CBPrimeClientPrime portfolios, balances, orders, allocations, activities, wallets, and transactions
CBCommerceClientLegacy Commerce charges, checkouts, and events

Use Advanced Trade for a normal Coinbase retail trading account. Coinbase Exchange, International Exchange, and Prime require the matching institutional account and credentials.

CBCommerceClient maps the legacy api.commerce.coinbase.com charge and checkout endpoints. Coinbase now directs new payment integrations toward Coinbase Business Checkouts. Review the official Commerce-to-Checkouts migration guide before starting a new payments integration.

Connect to live APIs and sandboxes

Each Coinbase client has its own default REST API host. The WebSocket client selects a host from the requested wsKey.

API or environmentREST API hostWebSocket host or guidance
Advanced Trade livehttps://api.coinbase.comwss://advanced-trade-ws.coinbase.com for market data
Advanced Trade private streamhttps://api.coinbase.comwss://advanced-trade-ws-user.coinbase.com
Advanced Trade static sandboxhttps://api-sandbox.coinbase.comNo Advanced Trade sandbox WebSocket
Coinbase App livehttps://api.coinbase.comNo Coinbase App stream in this SDK
Coinbase Exchange livehttps://api.exchange.coinbase.comwss://ws-feed.exchange.coinbase.com or the authenticated direct feed
Coinbase Exchange sandboxhttps://api-public.sandbox.exchange.coinbase.comwss://ws-feed-public.sandbox.exchange.coinbase.com or its direct sandbox feed
International Exchange livehttps://api.international.coinbase.comwss://ws-md.international.coinbase.com
International Exchange sandboxhttps://api-n5e1.coinbase.comwss://ws-md.n5e2.coinbase.com
Coinbase Prime livehttps://api.prime.coinbase.comwss://ws-feed.prime.coinbase.com
Legacy Commerce livehttps://api.commerce.coinbase.comNo Commerce stream in this SDK

The Advanced Trade static sandbox returns predefined mock responses for a limited set of account and order endpoints. It does not run a matching engine and is not a funded trading environment.

useSandbox: true is supported by CBExchangeClient and CBInternationalClient, and by the matching Exchange or International WebSocket connections. Sandbox credentials are separate from production credentials.

Do not set useSandbox: true on CBAdvancedTradeClient or an Advanced Trade WebSocket connection. Use public live data and previewOrder() for the safe Advanced Trade workflow in this tutorial.

Official environment references:

Coinbase does not expose one generic region switch for every API family. Account type, portfolio access, jurisdiction, and product eligibility determine the available APIs and products. A proxy does not change those rules.

Use a proxy with the REST API and WebSockets

A proxy can provide a stable egress IP, an approved corporate network route, or controlled network failover. It changes the network path to Coinbase, not the selected API family, account permissions, jurisdiction, or product eligibility.

See Using a proxy with Siebly SDKs for the general networking pattern.

HTTP or HTTPS proxy

Install the proxy agent:

npm install coinbase-api https-proxy-agent

Pass the agent through the second REST API client argument and through wsOptions.agent for WebSockets.

import { HttpsProxyAgent } from 'https-proxy-agent';
import {
  CBAdvancedTradeClient,
  WebsocketClient,
  WS_KEY_MAP,
} from 'coinbase-api';

async function main() {
  if (!process.env.COINBASE_PROXY_URL) {
    console.error('Set COINBASE_PROXY_URL.');
    return;
  }

  const proxyAgent = new HttpsProxyAgent(process.env.COINBASE_PROXY_URL);

  const rest = new CBAdvancedTradeClient(
    {},
    {
      httpsAgent: proxyAgent,
      proxy: false,
    },
  );

  const ws = new WebsocketClient({
    wsOptions: {
      agent: proxyAgent,
    },
  });

  try {
    const serverTime = await rest.getServerTime();
    console.log('REST API through proxy:', serverTime.iso);
  } catch (error) {
    console.error('Proxied REST API request failed:', error);
  }

  ws.on('open', ({ wsKey }) => {
    console.log('Proxied WebSocket opened:', wsKey);
  });

  ws.on('response', (response) => {
    console.log(
      'Proxied WebSocket response:',
      response.wsKey,
      response.channel,
    );
  });

  ws.on('update', (update) => {
    console.log(
      'Proxied WebSocket update:',
      update.wsKey,
      update.channel,
      update.events,
    );
  });

  ws.on('exception', (error) => {
    console.error('Proxied WebSocket exception:', error);
  });

  try {
    ws.subscribe(
      {
        topic: 'ticker',
        payload: {
          product_ids: ['BTC-USD'],
        },
      },
      WS_KEY_MAP.advTradeMarketData,
    );
  } catch (error) {
    console.error('Proxied ticker subscription failed:', error);
  }

  try {
    ws.subscribe('heartbeats', WS_KEY_MAP.advTradeMarketData);
  } catch (error) {
    console.error('Proxied heartbeat subscription failed:', error);
  }

  process.once('SIGINT', () => {
    ws.closeAll();
  });
}

main();

httpsAgent carries HTTPS REST API requests through the proxy. proxy: false prevents Axios from adding a second proxy layer. HttpsProxyAgent accepts both HTTP and HTTPS proxy URLs.

For authenticated traffic, add credentials to the same constructors:

import { HttpsProxyAgent } from 'https-proxy-agent';
import { CBAdvancedTradeClient, WebsocketClient } from 'coinbase-api';

function main() {
  if (
    !process.env.COINBASE_PROXY_URL ||
    !process.env.COINBASE_API_KEY_NAME ||
    !process.env.COINBASE_API_PRIVATE_KEY
  ) {
    console.error(
      'Set COINBASE_PROXY_URL, COINBASE_API_KEY_NAME, and COINBASE_API_PRIVATE_KEY.',
    );
    return;
  }

  const proxyAgent = new HttpsProxyAgent(process.env.COINBASE_PROXY_URL);

  const privateRest = new CBAdvancedTradeClient(
    {
      apiKey: process.env.COINBASE_API_KEY_NAME,
      apiSecret: process.env.COINBASE_API_PRIVATE_KEY,
    },
    {
      httpsAgent: proxyAgent,
      proxy: false,
    },
  );

  const privateWs = new WebsocketClient({
    apiKey: process.env.COINBASE_API_KEY_NAME,
    apiSecret: process.env.COINBASE_API_PRIVATE_KEY,
    wsOptions: {
      agent: proxyAgent,
    },
  });

  console.log(
    'Private REST API and WebSocket clients are configured:',
    Boolean(privateRest),
    Boolean(privateWs),
  );
}

main();

This only constructs the authenticated clients. It does not make a private request, open a private stream, or submit an order.

SOCKS5 proxy

Install:

npm install coinbase-api socks-proxy-agent

Use the same constructor positions with SocksProxyAgent.

import { SocksProxyAgent } from 'socks-proxy-agent';
import {
  CBAdvancedTradeClient,
  WebsocketClient,
  WS_KEY_MAP,
} from 'coinbase-api';

async function main() {
  if (!process.env.COINBASE_SOCKS_PROXY_URL) {
    console.error('Set COINBASE_SOCKS_PROXY_URL.');
    return;
  }

  const proxyAgent = new SocksProxyAgent(process.env.COINBASE_SOCKS_PROXY_URL);

  const rest = new CBAdvancedTradeClient(
    {},
    {
      httpsAgent: proxyAgent,
      proxy: false,
    },
  );

  const ws = new WebsocketClient({
    wsOptions: {
      agent: proxyAgent,
    },
  });

  try {
    const serverTime = await rest.getServerTime();
    console.log('REST API through SOCKS5:', serverTime.iso);
  } catch (error) {
    console.error('SOCKS5 REST API request failed:', error);
  }

  ws.on('open', ({ wsKey }) => {
    console.log('SOCKS5 WebSocket opened:', wsKey);
  });

  ws.on('response', (response) => {
    console.log('SOCKS5 WebSocket response:', response.wsKey, response.channel);
  });

  ws.on('update', (update) => {
    console.log(
      'SOCKS5 WebSocket update:',
      update.wsKey,
      update.channel,
      update.events,
    );
  });

  ws.on('exception', (error) => {
    console.error('SOCKS5 WebSocket exception:', error);
  });

  try {
    ws.subscribe(
      {
        topic: 'ticker',
        payload: {
          product_ids: ['BTC-USD'],
        },
      },
      WS_KEY_MAP.advTradeMarketData,
    );
  } catch (error) {
    console.error('SOCKS5 ticker subscription failed:', error);
  }

  try {
    ws.subscribe('heartbeats', WS_KEY_MAP.advTradeMarketData);
  } catch (error) {
    console.error('SOCKS5 heartbeat subscription failed:', error);
  }

  process.once('SIGINT', () => {
    ws.closeAll();
  });
}

main();

Proxy checks

  • Keep proxy URLs and credentials in environment variables or a secret manager.
  • URL-encode proxy usernames and passwords when composing a URL from separate values.
  • Make sure the proxy egress IP matches the API key's IP allowlist.
  • Test a public REST API request and public WebSocket subscription before private authentication.
  • Monitor REST API latency, WebSocket connection time, and reconnect frequency.
  • Treat HTTP 407 responses, TLS failures, and repeated reconnects as network faults.
  • Do not log complete proxy URLs because they may contain credentials.
  • Keep the host clock synchronized because private request JWTs are time-sensitive.
  • The SDK does not rotate proxy endpoints.

Production checklist

Keep request time accurate

Advanced Trade JWTs are short-lived. Keep the host clock synchronized and alert on clock drift before it causes authentication failures.

Respect rate limits and caching

Coinbase applies REST API, WebSocket connection, and message limits. Public Advanced Trade REST API endpoints can also use a short cache. Use WebSockets for continuously changing prices and books, follow current response headers, and back off after rate-limit responses.

Apply current product rules

Read product metadata before validating an order. Apply the current increments, minimum and maximum sizes, trading status, venue, product type, session details, and account eligibility.

Do not assume that the rules for BTC-USD apply to another Spot, futures, or perpetual product.

Use client order IDs

Generate and persist a unique client_order_id for each application-owned order. Store it with the Coinbase order ID, product, side, configuration, requested size, and creation time.

Confirm final state

An accepted write is not proof of a fill, cancellation, or final order status. Confirm through the private user stream or a REST API query.

After an uncertain timeout, reconcile known identifiers before retrying.

Keep WebSocket streams healthy

Subscribe to heartbeats alongside other Advanced Trade channels. Monitor sequence numbers, heartbeat counters, exceptions, reconnects, and time since the last update.

Reload the affected account state after a private stream gap.

Scope credentials and network access

Keep API keys and proxy credentials out of source control, browser bundles, logs, and error reporting. Grant only the required permissions and portfolio access. Use stable allowlisted egress IPs where practical.

API keys in this guide are for a server acting on its own Coinbase account. An application acting for other Coinbase users should use Coinbase's current OAuth flow rather than collecting users' private API keys.

Shut down connections

Call closeAll() during controlled process shutdown. Use unsubscribe() only when a long-running process should stop selected topics while keeping a connection active.

FAQ

Does coinbase-api work with plain JavaScript?

Yes. Every example in this tutorial is plain JavaScript. The package also publishes TypeScript declarations for projects that use them.

Which REST API client should I use?

Use CBAdvancedTradeClient for a normal Coinbase retail trading account. Use the App, Exchange, International, Prime, or Commerce client only when the Coinbase account and workflow belong to that API family.

What are COINBASE_API_KEY_NAME and COINBASE_API_PRIVATE_KEY?

The key name identifies a Coinbase Developer Platform secret API key. The private key signs requests and subscriptions. It is not the Coinbase account password.

Does Advanced Trade use an API passphrase?

No. Advanced Trade and Coinbase App use a key name and private signing key. Exchange, International Exchange, and Prime credentials include a separate API passphrase.

Can I use ECDSA and Ed25519 keys?

coinbase-api supports both current Coinbase key formats and detects the signing algorithm from the private key. Preserve the complete ECDSA PEM or base64 Ed25519 value.

Why do Advanced Trade REST API responses have different shapes?

The SDK returns each endpoint body directly. A product is one object, accounts are under accounts, orders are under orders, candles are under candles, and trades are under trades.

Why can an order response fail with an HTTP success status?

Some order-management endpoints report operation-level success inside the response body. Check success, then read success_response or error_response as appropriate.

What is the difference between Advanced Trade and Coinbase Exchange?

Advanced Trade is the normal Coinbase trading API for retail users. Coinbase Exchange is an institutional API product with separate accounts, credentials, permissions, hosts, and market structure.

Does Advanced Trade have a sandbox?

Coinbase provides a static Advanced Trade sandbox with predefined mock responses for selected account and order endpoints. It does not run a matching engine. useSandbox: true is not supported by CBAdvancedTradeClient.

Does previewOrder() place an order?

No. It validates and estimates the request but does not submit it to the matching engine. Check errs and warning before using its estimates.

Why is there no Coinbase WebsocketAPIClient?

The Coinbase WebSocket interfaces supported by this SDK are subscription feeds. Use WebsocketClient for streams and the matching REST API client for order commands.

What does the first private user update contain?

Coinbase sends current open-order snapshots before later changes. Large snapshots can be split across multiple messages, so process event types and sequence information rather than treating the first message as complete.

Why should I subscribe to heartbeats?

Heartbeats keep quiet Advanced Trade subscriptions open and expose a counter that can help detect missed messages.

What should happen after a private WebSocket reconnect?

Restore the subscription, then reload affected account state through the REST API. Replace local state only after the required reads complete and reconcile updates received during recovery.

What causes timestamp or signature errors?

Common causes include clock drift, an incorrectly stored private key, lost PEM line breaks, a key for the wrong API family, insufficient portfolio permissions, or a proxy sending traffic from an unexpected IP.

Can a proxy change Coinbase product availability?

No. A proxy changes the network route. It does not change the API family, account region, jurisdiction, portfolio permissions, or product eligibility.

Can I use API keys for other Coinbase users?

API key authentication is for server-side access to an account you control. Use Coinbase's current OAuth guidance when an application needs authorization from other users.

Next steps

Subscribe on Substack

Complete the Substack form below to join our newsletter. Substack handles all subscriber data directly.