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

AiSpinner Documentation