Coinbase API: The Complete Trader's Setup and Guide
A practical guide to the Coinbase API — from generating your API key and secret to authenticating requests, reading live market data, and executing trades programmatically.
A practical guide to the Coinbase API — from generating your API key and secret to authenticating requests, reading live market data, and executing trades programmatically.
If you've spent any time manually clicking through Coinbase's interface to place trades, you already know how limiting that gets once your strategy needs speed or scale. The Coinbase API changes that equation entirely. It gives you programmatic access to real-time prices, your account balances, order history, and the ability to fire trades in milliseconds — all from code you control. Whether you're building a personal trading bot, connecting a signal service like VoiceOfChain to auto-execute entries, or simply pulling historical data for backtesting, the Coinbase API is the foundation you'll build on. This guide walks you through every step: from finding the right documentation and generating credentials to writing your first authenticated request and placing a live order.
The Coinbase API portal lives at docs.cdp.coinbase.com — that's your starting point for everything. Coinbase has gone through several API generations, and it's worth knowing which one you're working with before writing a single line of code. The current flagship is the Coinbase Advanced Trade API (v3), which replaced the older Coinbase Pro API. If you've seen tutorials referencing api.pro.coinbase.com, those are outdated — the new base URL is api.coinbase.com/api/v3/brokerage. The Coinbase API documentation covers four main areas: market data endpoints (prices, candles, order books), account management (balances, transaction history), order management (place, cancel, list orders), and portfolio analytics. Unlike Binance or OKX, which offer separate REST and WebSocket layers with very different auth patterns, Coinbase consolidates everything under one JWT-based authentication system, which actually makes it cleaner to work with once you understand it. The coinbase api docs also cover their CDP (Coinbase Developer Platform) APIs for on-chain interaction, but for trading purposes, you'll stay in the brokerage section.
For the vast majority of traders and bot developers, the Advanced Trade API is what you need. It covers BTC, ETH, SOL, and hundreds of other spot pairs. It also supports limit orders, market orders, stop-limit orders, and bracket orders — everything you'd need to implement a serious strategy.
Before you write any code, you need credentials. Coinbase's current authentication system uses CDP API keys, which work differently from the old API key + passphrase + secret pattern. When you create a new key in the Coinbase API portal, you get two things: an API key name (formatted as organizations/{org_id}/apiKeys/{key_id}) and an EC private key (a PEM-encoded elliptic curve key). The private key is your coinbase api secret — Coinbase will show it to you exactly once, at creation time. Copy it immediately and store it securely. Losing it means generating a new key from scratch. Never commit it to git or hardcode it in scripts. Use environment variables or a secrets manager like AWS Secrets Manager or HashiCorp Vault.
Always start with View-only permissions when testing. Only upgrade to Trade permissions once your authentication code is confirmed working and your order logic is thoroughly tested on paper.
The Coinbase API uses JWT (JSON Web Tokens) signed with your EC private key. Every request requires a fresh token that expires in 120 seconds — there's no session to maintain or refresh. This is more secure than the old HMAC-SHA256 pattern used by exchanges like Bybit and OKX, because a stolen token is useless within two minutes. Here's how to set up authentication in Python. Install dependencies first with: pip install requests pyjwt cryptography
import jwt
import time
import secrets
import requests
import os
# Load credentials from environment variables
API_KEY_NAME = os.environ["COINBASE_API_KEY_NAME"]
API_PRIVATE_KEY = os.environ["COINBASE_API_PRIVATE_KEY"] # Full PEM string
def generate_jwt(method: str, path: str) -> str:
"""Generate a short-lived JWT for a specific API endpoint."""
uri = f"{method} api.coinbase.com{path}"
payload = {
"sub": API_KEY_NAME,
"iss": "cdp",
"nbf": int(time.time()),
"exp": int(time.time()) + 120, # 2-minute expiry
"uri": uri,
}
token = jwt.encode(
payload,
API_PRIVATE_KEY,
algorithm="ES256",
headers={
"kid": API_KEY_NAME,
"nonce": secrets.token_hex(16) # prevents replay attacks
}
)
return token
def api_request(method: str, path: str, params: dict = None, body: dict = None):
"""Make an authenticated request to Coinbase API."""
token = generate_jwt(method.upper(), path)
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
url = f"https://api.coinbase.com{path}"
response = requests.request(
method, url, headers=headers, params=params, json=body
)
response.raise_for_status()
return response.json()
# Quick connectivity test
accounts = api_request("GET", "/api/v3/brokerage/accounts")
print(f"Connected. Found {len(accounts.get('accounts', []))} accounts.")
With authentication working, the most common first task is pulling market data. The Coinbase API provides OHLCV candles, current prices (called 'best bid/ask'), 24h statistics, and full order book snapshots. For trading bots and signal systems, candle data is the workhorse — you'll use it for indicator calculations, pattern recognition, and feeding into ML models. Compared to what Binance offers through its klines endpoint, the Coinbase candle response structure is slightly different but equally complete. VoiceOfChain, for example, uses exchange APIs including Coinbase to pull real-time OHLCV data and then overlays proprietary signal logic before surfacing alerts to traders — if you're building something similar, the pattern below is exactly how that data ingestion layer works.
from datetime import datetime
GRANULARITY_MAP = {
"1m": "ONE_MINUTE",
"5m": "FIVE_MINUTE",
"15m": "FIFTEEN_MINUTE",
"1h": "ONE_HOUR",
"6h": "SIX_HOUR",
"1d": "ONE_DAY"
}
def get_candles(product_id: str, interval: str = "1h", lookback_hours: int = 24):
"""Fetch OHLCV candles for a trading pair."""
path = f"/api/v3/brokerage/products/{product_id}/candles"
now = int(time.time())
params = {
"start": str(now - lookback_hours * 3600),
"end": str(now),
"granularity": GRANULARITY_MAP.get(interval, "ONE_HOUR"),
"limit": 300
}
data = api_request("GET", path, params=params)
candles = data.get("candles", [])
result = []
for c in candles:
result.append({
"time": datetime.utcfromtimestamp(int(c["start"])).isoformat(),
"open": float(c["open"]),
"high": float(c["high"]),
"low": float(c["low"]),
"close": float(c["close"]),
"volume": float(c["volume"])
})
return sorted(result, key=lambda x: x["time"])
def get_account_balances():
"""List all non-zero account balances."""
data = api_request("GET", "/api/v3/brokerage/accounts")
balances = [
{"currency": a["currency"], "available": a["available_balance"]["value"]}
for a in data.get("accounts", [])
if float(a["available_balance"]["value"]) > 0
]
return balances
# Fetch BTC 1h candles for the last 24 hours
btc_candles = get_candles("BTC-USD", "1h", 24)
print(f"Latest BTC close: ${btc_candles[-1]['close']:,.2f}")
# Check your balances
for balance in get_account_balances():
print(f"{balance['currency']}: {balance['available']}")
One thing worth noting: Coinbase API candles return data in reverse chronological order (newest first), which is the opposite of what Binance and KuCoin do. The sort in the code above handles that so your arrays are always oldest-to-newest, which is what most indicator libraries expect. Always check the sort order when switching between exchange APIs — it's a common source of subtle bugs in multi-exchange bots.
Order placement is where coinbase api trading gets real. The Advanced Trade API supports market orders, limit orders, stop-limit orders, and bracket (take-profit + stop-loss) orders. Market orders use the market_market_ioc configuration and are filled immediately at the best available price. Limit orders use limit_limit_gtc (good-till-cancelled) or limit_limit_gtd (good-till-date). Platforms like Bybit and Gate.io have similar order type patterns but slightly different parameter names — once you understand Coinbase's structure, adapting to others is straightforward. The critical difference in coinbase api trading is that market buy orders are specified in quote currency (USD), while market sell orders are specified in base currency (BTC, ETH, etc.). Getting this backwards causes order rejection.
import uuid
import json
def place_market_order(product_id: str, side: str, size: float) -> dict:
"""
Place a market order.
For BUY: size is in quote currency (e.g., 100.0 = $100 USD)
For SELL: size is in base currency (e.g., 0.001 = 0.001 BTC)
"""
if side not in ("BUY", "SELL"):
raise ValueError("side must be 'BUY' or 'SELL'")
order_config = {}
if side == "BUY":
order_config["market_market_ioc"] = {"quote_size": str(size)}
else:
order_config["market_market_ioc"] = {"base_size": str(size)}
body = {
"client_order_id": str(uuid.uuid4()), # unique per order
"product_id": product_id,
"side": side,
"order_configuration": order_config
}
result = api_request("POST", "/api/v3/brokerage/orders", body=body)
if result.get("success"):
order_id = result["order_id"]
print(f"Order placed: {order_id}")
return {"success": True, "order_id": order_id}
else:
error = result.get("error_response", {})
print(f"Order failed: {error.get('message', 'Unknown')} — {error.get('preview_failure_reason', '')}")
return {"success": False, "error": error}
def place_limit_order(product_id: str, side: str, base_size: float, limit_price: float) -> dict:
"""Place a GTC limit order."""
body = {
"client_order_id": str(uuid.uuid4()),
"product_id": product_id,
"side": side,
"order_configuration": {
"limit_limit_gtc": {
"base_size": str(base_size),
"limit_price": str(limit_price),
"post_only": False # set True to avoid taker fees
}
}
}
return api_request("POST", "/api/v3/brokerage/orders", body=body)
# Example: Buy $50 of ETH at market
result = place_market_order("ETH-USD", "BUY", 50.0)
# Example: Place a limit buy for 0.01 BTC at $58,000
limit_result = place_limit_order("BTC-USD", "BUY", 0.01, 58000.00)
Always test order logic against the Coinbase sandbox environment (api-sandbox.coinbase.com) before pointing at production. Real money is hard to recover. Sandbox credentials are separate from your live credentials — generate them specifically at portal.sandbox.cdp.coinbase.com.
The Coinbase API itself has no direct subscription fee — access is free. What you pay for is trading: Coinbase charges fees on executed orders based on your 30-day trading volume. That said, coinbase api pricing in the broader sense includes being aware of rate limits, since hitting them can stall your bot at exactly the wrong moment. On the Advanced Trade API, the default rate limit is 10 requests per second for private endpoints and 10 requests per second for public endpoints. This is lower than Binance (which allows 1200 requests per minute on some endpoints) and comparable to what you'd get on KuCoin's basic tier. If you're running a strategy that polls many symbols simultaneously, you'll need to implement request queuing and exponential backoff.
| Endpoint | Method | Rate Limit | Description |
|---|---|---|---|
| /api/v3/brokerage/products | GET | 10 req/s | List all trading pairs |
| /api/v3/brokerage/products/{id}/candles | GET | 10 req/s | OHLCV historical data |
| /api/v3/brokerage/best_bid_ask | GET | 10 req/s | Real-time top of book |
| /api/v3/brokerage/accounts | GET | 10 req/s (private) | Account balances |
| /api/v3/brokerage/orders | POST | 10 req/s (private) | Place new order |
| /api/v3/brokerage/orders/batch_cancel | POST | 10 req/s (private) | Cancel up to 100 orders |
Trading fees on Coinbase Advanced Trade start at 0.60% taker / 0.40% maker for volumes under $1,000/month and drop significantly as volume increases — reaching 0.00% maker fees at the highest tiers. By comparison, Bitget and Binance offer lower base fees but Coinbase compensates with deep USD liquidity on major pairs. If your strategy is market-making or uses post_only limit orders, you'll benefit from the maker fee structure. If you're signal-following with market orders, factor the taker fee into your expected return calculation from the start.
The Coinbase API is one of the cleaner exchange APIs to work with once you get past the JWT authentication curve. The coinbase api documentation is thorough, the error messages are descriptive, and the endpoint structure is logical. Compared to the more complex multi-signature setups you'd encounter on OKX or the layered sub-account systems on Binance, Coinbase keeps things relatively straightforward. From here, the natural next steps are: connecting to the WebSocket feed for real-time order book and trade data, implementing a position manager that tracks open orders and P&L, and adding a signal layer — whether from your own indicators or from a platform like VoiceOfChain — to drive when your bot enters and exits. The code patterns in this guide give you a solid, production-ready foundation to build from.