Skip to content

Coinbase SDK ​

Complete reference for ctx.coinbase — programmatic trading on Coinbase Advanced Trade from worker code.

Coinbase Advanced Trade API

This SDK wraps the Coinbase Advanced Trade v3 API. Live and (optionally) sandbox environments supported. Coinbase International (CFM) futures methods are surfaced when enabled on your account.

Setup ​

  1. Add a Coinbase trading block to your workspace canvas.
  2. Open the inspector → API Keys → pick a key (Settings → API Keys → Coinbase to add new). Coinbase requires api_key (CDP key name) + api_secret (PEM-formatted EC private key).
  3. Connect the block to your Worker block via an edge.
  4. The worker runs in Server mode (Coinbase blocks browser CORS for signed requests).
  5. ctx.coinbase is now available.
python
def tick(ctx):
    t = ctx.coinbase.get_ticker("BTC-USD")
    ctx.log.info(f"BTC-USD: {t.get('price')}")

If no Coinbase block is connected:

RuntimeError: No trading block connected. Connect a Coinbase block to this worker.

Products & Modes ​

Coinbase calls instruments products, identified by BASE-QUOTE strings:

Product typePatternExamples
Spot{COIN}-{COIN}BTC-USD, ETH-USDC, SOL-USDT
Futures (CFM){COIN}-{EXPIRY}BTC-29NOV24-CDE
Perpetual (INTX){COIN}-PERP-INTXBTC-PERP-INTX

Account modes (api_mode on key):

ModeNotes
liveReal funds
sandboxis_sandbox=true flag on key — Coinbase's Exchange sandbox

Coinbase has portfolios — sub-accounts that segregate balances and risk. Default portfolio is automatic; for advanced setups use get_portfolios to discover them.

Market Data ​

get_ticker ​

Latest ticker for a product.

python
ctx.coinbase.get_ticker(product_id: str = "BTC-USD") -> dict

Returns: {"product_id", "price", "volume_24h", "low_24h", "high_24h", "approximate_quote_24h_volume", ...}

WebSocket cache

PRO/MAX plans return cached ticker data at 0ms latency from the live WS stream — no REST call.

get_orderbook ​

Order book depth.

python
ctx.coinbase.get_orderbook(product_id: str = "BTC-USD", limit: int = 50) -> dict

Returns: {"pricebook": {"product_id", "bids": [{"price", "size"}], "asks": [{"price", "size"}], "time"}}

python
ob = ctx.coinbase.get_orderbook("BTC-USD", limit=50)
best_bid = float(ob["pricebook"]["bids"][0]["price"])
best_ask = float(ob["pricebook"]["asks"][0]["price"])
spread_bps = (best_ask - best_bid) / best_bid * 10000

get_best_bid_ask ​

Top-of-book for one or many products in one call.

python
ctx.coinbase.get_best_bid_ask(product_ids: list = None) -> dict
python
top = ctx.coinbase.get_best_bid_ask(["BTC-USD", "ETH-USD", "SOL-USD"])

get_market_trades ​

Recent public trades.

python
ctx.coinbase.get_market_trades(product_id: str, limit: int = 100) -> dict

get_candles ​

Historical OHLCV.

python
ctx.coinbase.get_candles(
    product_id: str,
    granularity: str = "ONE_HOUR",
    start: str = None,
    end: str = None,
) -> dict
granularityValues
Minutes"ONE_MINUTE", "FIVE_MINUTE", "FIFTEEN_MINUTE", "THIRTY_MINUTE"
Hours"ONE_HOUR", "TWO_HOUR", "SIX_HOUR"
Days"ONE_DAY"
python
candles = ctx.coinbase.get_candles("BTC-USD", granularity="ONE_HOUR")
closes = [float(c["close"]) for c in candles["candles"]]
sma = sum(closes[-20:]) / 20

get_products ​

List all tradable products.

python
ctx.coinbase.get_products(product_type: str = None) -> dict
# product_type: "SPOT" | "FUTURE" | None (all)

get_product ​

Detailed info for one product (price, status, base/quote, fees, increments).

python
ctx.coinbase.get_product(product_id: str) -> dict

Account & Portfolios ​

get_accounts ​

All your accounts (one per currency).

python
ctx.coinbase.get_accounts() -> dict

Returns: {"accounts": [{"uuid", "currency", "available_balance": {"value", "currency"}, "hold": {"value", "currency"}, "type", "active", "ready", "default", ...}]}

get_portfolios ​

List portfolios (sub-accounts).

python
ctx.coinbase.get_portfolios(portfolio_type: str = None) -> dict
# portfolio_type: "DEFAULT" | "CONSUMER" | "INTX" | None (all)
python
ports = ctx.coinbase.get_portfolios()
intx = next((p for p in ports["portfolios"] if p["type"] == "INTX"), None)

get_transactions_summary ​

30-day fee tier, total volume, fees paid. Useful for figuring out which fee bracket you're in.

python
ctx.coinbase.get_transactions_summary() -> dict
python
s = ctx.coinbase.get_transactions_summary()
ctx.log.info(f"Tier: {s['fee_tier']['pricing_tier']}, "
             f"30d vol: ${s['total_volume']:.0f}, "
             f"fees: ${s['total_fees']:.2f}")

get_futures_balance_summary ​

Coinbase International (CFM) futures balance: total equity, available margin, position mode.

python
ctx.coinbase.get_futures_balance_summary() -> dict

CFM only

Returns 404 if your account doesn't have CFM enabled. Check via get_portfolios() for an INTX portfolio.

Orders ​

place_order ​

Place a single order. Auto-builds the order_configuration based on order_type and side.

python
ctx.coinbase.place_order(
    product_id: str,
    side: str,                    # "BUY" | "SELL"
    size: str,                    # for market BUY → quote size (USD); else → base size
    order_type: str = "market",   # "market" | "limit"
    price: str = None,            # required for limit
    client_order_id: str = None,  # auto UUID if omitted
) -> dict
python
# Market buy $50 worth of BTC
r = ctx.coinbase.place_order("BTC-USD", "BUY", "50", "market")

# Limit sell 0.01 BTC @ $70000
r = ctx.coinbase.place_order(
    product_id="BTC-USD", side="SELL", size="0.01",
    order_type="limit", price="70000",
)

Market BUY peculiarity

For a market BUY Coinbase expects a quote size (USD amount). For a market SELL or any limit order, it expects a base size (coin amount). The SDK passes size to the right field automatically.

preview_order ​

Preview an order without placing it. Returns expected totals, fees, slippage estimate, and validation errors. Use for sanity checks.

python
ctx.coinbase.preview_order(
    product_id: str,
    side: str,
    size,
    order_type: str = "market",
    price: str = None,
) -> dict
python
preview = ctx.coinbase.preview_order("BTC-USD", "BUY", "50", "market")
if preview.get("errs"):
    ctx.log.error(f"Validation: {preview['errs']}")
    return
ctx.log.info(f"Will pay {preview['total_fees']} in fees, "
             f"slippage est: {preview.get('best_price') or 'n/a'}")

edit_order ​

Modify an unfilled limit order (size and/or price).

python
ctx.coinbase.edit_order(order_id: str, size: str = None, price: str = None) -> dict

cancel_order ​

Cancel one or more orders.

python
ctx.coinbase.cancel_order(order_ids) -> dict
# order_ids: str | list[str]
python
ctx.coinbase.cancel_order("abc-123-...")        # single
ctx.coinbase.cancel_order(["a", "b", "c"])       # batch

cancel_all_orders ​

Cancel ALL open orders, optionally filtered by product. Kill switch.

python
ctx.coinbase.cancel_all_orders(product_id: str = None) -> dict
python
# Cancel everything across all products
ctx.coinbase.cancel_all_orders()

# Or scoped to one product
ctx.coinbase.cancel_all_orders("BTC-USD")

close_position ​

Close a perpetual futures position immediately at market (CFM/INTX only).

python
ctx.coinbase.close_position(product_id: str, size: str = None) -> dict

get_orders ​

List orders with optional filters.

python
ctx.coinbase.get_orders(product_id: str = None) -> dict

WebSocket cache

PRO/MAX: cached from private WS at 0ms.

get_order_by_id ​

Status of a single order.

python
ctx.coinbase.get_order_by_id(order_id: str) -> dict

get_fills ​

Trade fills (the actual transactions, with fees).

python
ctx.coinbase.get_fills(product_id: str = None, order_id: str = None) -> dict

Common Patterns ​

Sandbox during development ​

Set is_sandbox=true when adding the API key in Settings → API Keys → Coinbase. All worker calls auto-route to Coinbase Exchange's sandbox endpoint.

Error handling ​

python
def tick(ctx):
    try:
        accounts = ctx.coinbase.get_accounts()
    except Exception as e:
        ctx.log.error(f"Coinbase unreachable: {e}")
        return

CoinbaseAPIError (extends ExchangeAPIError) is raised for API errors with code and msg. Network failures raise httpx.HTTPStatusError.

Cloud-Run proxy ​

PRO+ workers route Coinbase traffic through a Cloud Run proxy with rotating exit IPs. Transparent — no code changes.

WebSocket caching ​

PRO+ subscribes to public + private WS streams in the background. get_ticker and get_orders auto-prefer the cache when fresh.

Server-only ​

Coinbase Advanced Trade signing requires a PEM private key — not safe to expose to a browser. Workers must run in Server mode.

Recipes ​

Always-validate-before-trade pattern ​

python
def tick(ctx):
    px = float(ctx.coinbase.get_ticker("BTC-USD")["price"])
    target_quote = "100"  # buy $100 worth

    # 1. Preview first
    preview = ctx.coinbase.preview_order("BTC-USD", "BUY", target_quote, "market")
    if preview.get("errs"):
        ctx.log.error(f"Skipping — preview errors: {preview['errs']}")
        return

    fees = float(preview.get("total_fees") or 0)
    if fees > float(target_quote) * 0.005:  # >0.5% fees
        ctx.log.warn(f"Fees too high: ${fees:.2f}, skipping")
        return

    # 2. Now place for real
    r = ctx.coinbase.place_order("BTC-USD", "BUY", target_quote, "market")
    ctx.log.info(f"Bought $100 BTC @ ~${px}, order {r.get('order_id')}")

Grid market making ​

python
def setup(ctx):
    px = float(ctx.coinbase.get_ticker("BTC-USD")["price"])
    step = px * 0.005  # 0.5% spacing

    # Cancel any old grid first
    ctx.coinbase.cancel_all_orders("BTC-USD")

    for i in range(1, 6):
        ctx.coinbase.place_order(
            "BTC-USD", "BUY", "0.001", "limit",
            price=str(round(px - i * step, 2)),
        )
        ctx.coinbase.place_order(
            "BTC-USD", "SELL", "0.001", "limit",
            price=str(round(px + i * step, 2)),
        )
    ctx.state.set("grid_price", px)

def tick(ctx):
    px = float(ctx.coinbase.get_ticker("BTC-USD")["price"])
    base = ctx.state.get("grid_price", px)

    # Re-grid if price has moved >2% from grid centre
    if abs(px - base) / base > 0.02:
        ctx.log.info(f"Re-gridding around {px}")
        setup(ctx)

Spot momentum with VWAP-aware sizing ​

python
def tick(ctx):
    candles = ctx.coinbase.get_candles("BTC-USD", granularity="ONE_HOUR")["candles"]
    closes = [float(c["close"]) for c in candles[:24]]
    sma_24 = sum(closes) / len(closes)
    last = closes[0]

    # Are we already long?
    accs = ctx.coinbase.get_accounts()["accounts"]
    btc = next((a for a in accs if a["currency"] == "BTC"), None)
    has_pos = btc and float(btc["available_balance"]["value"]) >= 0.001

    if last > sma_24 * 1.005 and not has_pos:
        # Validate first, then trade
        preview = ctx.coinbase.preview_order("BTC-USD", "BUY", "100", "market")
        if not preview.get("errs"):
            ctx.coinbase.place_order("BTC-USD", "BUY", "100", "market")
            ctx.log.info(f"Long entry at {last}")
    elif last < sma_24 * 0.995 and has_pos:
        size = btc["available_balance"]["value"]
        ctx.coinbase.place_order("BTC-USD", "SELL", size, "market")
        ctx.log.info(f"Exit at {last}")

Reference ​

Coinbase endpointSDK method
GET /api/v3/brokerage/products/{id}/tickerget_ticker / get_market_trades
GET /api/v3/brokerage/market/product_bookget_orderbook
GET /api/v3/brokerage/best_bid_askget_best_bid_ask
GET /api/v3/brokerage/products/{id}/candlesget_candles
GET /api/v3/brokerage/productsget_products
GET /api/v3/brokerage/products/{id}get_product
GET /api/v3/brokerage/accountsget_accounts
GET /api/v3/brokerage/portfoliosget_portfolios
GET /api/v3/brokerage/transaction_summaryget_transactions_summary
GET /api/v3/brokerage/cfm/balance_summaryget_futures_balance_summary
POST /api/v3/brokerage/ordersplace_order
POST /api/v3/brokerage/orders/previewpreview_order
POST /api/v3/brokerage/orders/editedit_order
POST /api/v3/brokerage/orders/batch_cancelcancel_order / cancel_all_orders
POST /api/v3/brokerage/orders/close_positionclose_position
GET /api/v3/brokerage/orders/historical/batchget_orders
GET /api/v3/brokerage/orders/historical/{id}get_order_by_id
GET /api/v3/brokerage/orders/historical/fillsget_fills

NodeGraph Documentation