Coinbase Advanced Trade API Bot in Python: A Beginner's Guide
Build a Coinbase Python bot preview: validate product limits with Decimal, persist decision IDs, and inspect SDK authentication and sandbox limitations.
Put this into practice with a watchlist
Build a watchlist, then review each signal’s entry, stop, target, and reasoning. Broker access is optional.
What a Coinbase API Trading Bot Needs
A Coinbase API trading bot combines market data, written decision rules, request validation and order-state handling. Use the official coinbase-advanced-py SDK to handle Coinbase Advanced Trade REST authentication and request shapes. Start with product and preview checks, not an order-submitting loop. This guide's runnable example defaults to an invented offline fixture; its optional authenticated mode reads product information and requests an order preview. It never creates or cancels an order. This is educational material, not financial advice, and it does not establish profitability or readiness for funded trading.
Coinbase Pro keys and passphrases belong to the retired Pro API. For Advanced Trade, follow the current CDP key authentication guide. This example supports only BTC-USD spot limit previews; do not assume every product ID is a spot crypto pair.
Create CDP API Keys With Least Privilege
Create the key through the Coinbase Developer Platform portal. Select the relevant Coinbase App/Advanced Trade restrictions, permitted portfolio and optional IP allowlist. Copy the key name and secret securely when issued; downloading the key JSON is optional. Never put a real secret in source code, screenshots or a public message.
For the optional authenticated preview in this guide, grant view only. The Advanced Trade scope table assigns view to Get Product and POST /orders/preview; Create Order and Cancel Orders require trade. This program needs neither trade nor transfer. Public product/candle methods require no key, but this example uses the authenticated product endpoint to request tradability information.
The key name has the form organizations/{org_id}/apiKeys/{key_id}. Preserve the exact secret format and newlines. There is a current documentation distinction: the Coinbase App authentication page specifies ECDSA, while the official SDK README recommends Ed25519 and documents support for both signing algorithms. Signing support does not prove that a particular endpoint accepts that key. Follow the endpoint's current requirements and verify a permitted read request; do not diagnose every 401 as a key-type problem.
The SDK reads COINBASE_API_KEY and COINBASE_API_SECRET when constructing RESTClient() without explicit keys. Use a secrets manager or protected environment configuration. No credentials are needed for the default offline mode.
How JWT Authentication Works
Private REST requests use a signed Bearer JWT scoped to the request method and path. Coinbase documents a two-minute JWT lifetime and a new token for each unique request. The SDK signs requests as it sends them. Public data methods and the static sandbox do not prove private JWT authentication.
Keep the signing machine's clock synchronized. Clock skew, the wrong endpoint, insufficient permissions, mismatched key restrictions or a malformed secret can all cause authentication errors. Inspect the documented response and requested method/path without logging a private key or token.
The Sandbox Is Static
The Advanced Trade sandbox at https://api-sandbox.coinbase.com/api/v3/brokerage/{resource} needs no authentication and returns predefined mocked responses. It documents Accounts and Orders workflows and selected additional endpoints. It does not simulate fills from a live order book.
| Check | What it establishes | What it does not establish |
|---|---|---|
| Offline fixture | Decimal math, request shape and local decision record | Current product rules, authentication or fees |
| Static sandbox | Parsing documented mocked responses | JWT validity, actual liquidity or strategy results |
| Authenticated product read | Product fields visible to that key at that moment | Account buying power or order acceptance |
| Authenticated order preview | Current preview response, estimates and validation messages | A created order, reserved liquidity, a fill or guaranteed costs |
Public products and candles can support research. Cache rules, historical aggregation and product prices differ from an executable bid/ask quote. A local simulator must state its own fill and cost assumptions; it is not a Coinbase paper account. Treat any separately submitted production order as funded execution.
Validate Products and Preview a Limit Request
Use Python 3.11+ and install the tested SDK version:
python -m pip install coinbase-advanced-py==1.8.4
Append the following three blocks in order to coinbase_preview.py. Run python coinbase_preview.py first. It makes no network call: all product values, the $25 budget and $50,000 limit price are invented teaching inputs, not current Coinbase limits, market data or a recommendation.
The Get Product reference supplies base, quote and price increments; base and quote minimum/maximum sizes; and product status/type flags. This example rejects missing/ambiguous flags, unsupported product types and nonfinite numbers. It rounds down to actual increment multiples rather than merely counting decimal places, then checks the result against the sample budget and both size ranges. Its positive input ceiling and precision limit are arbitrary defensive teaching bounds, not Coinbase rules. The budget excludes fees; it is not an account balance or maximum-loss cap.
import argparse
import json
import os
import uuid
from decimal import Decimal, InvalidOperation, ROUND_CEILING
from pathlib import Path
from coinbase.rest import RESTClient
# Invented fixture, not a current product response or market quote.
PRODUCT = {
"product_id": "BTC-USD",
"product_type": "SPOT",
"status": "online",
"base_increment": "0.00001",
"price_increment": "0.05",
"quote_increment": "0.01",
"base_min_size": "0.00001",
"base_max_size": "1",
"quote_min_size": "1",
"quote_max_size": "1000",
"is_disabled": False,
"trading_disabled": False,
"cancel_only": False,
"view_only": False,
"auction_mode": False,
"limit_only": False,
"post_only": False,
}
def positive(value):
if not isinstance(value, (str, Decimal)):
raise ValueError("Use decimal strings, not floats or booleans")
try:
value = Decimal(value)
except InvalidOperation as exc:
raise ValueError("Invalid decimal input") from exc
if not value.is_finite() or not Decimal("0") < value <= Decimal("1000000000"):
raise ValueError("Expected a finite positive teaching input")
if value.as_tuple().exponent < -18:
raise ValueError("Input exceeds this example's decimal precision")
return value
def preview_args(product, budget, desired_price):
if (product.get("product_id"), product.get("product_type"), product.get("status")) != ("BTC-USD", "SPOT", "online"):
raise ValueError("This example supports only an online BTC-USD spot product")
flags = ("is_disabled", "trading_disabled", "cancel_only", "view_only", "auction_mode", "limit_only", "post_only")
if any(type(product.get(flag)) is not bool for flag in flags):
raise ValueError("Missing or ambiguous product flags")
if any(product[flag] for flag in flags[:5]):
raise ValueError("Product is not supported for this preview")
# Always a post-only limit request, including limit-only/post-only products.
base_step = positive(product["base_increment"])
price_step = positive(product["price_increment"])
quote_step = positive(product["quote_increment"])
base_min, base_max = (positive(product[key]) for key in ("base_min_size", "base_max_size"))
quote_min, quote_max = (positive(product[key]) for key in ("quote_min_size", "quote_max_size"))
if base_min > base_max or quote_min > quote_max:
raise ValueError("Inconsistent product bounds")
budget = positive(budget)
price = (positive(desired_price) // price_step) * price_step
if price <= 0:
raise ValueError("Rounded price is zero")
size = ((budget / price) // base_step) * base_step
notional = size * price
rounded_cost = (notional / quote_step).to_integral_value(rounding=ROUND_CEILING) * quote_step
if not base_min <= size <= base_max:
raise ValueError("Rounded size is outside product limits")
if not quote_min <= notional or rounded_cost > min(budget, quote_max):
raise ValueError("Notional is outside the sample budget/product limits")
return {
"product_id": "BTC-USD",
"base_size": format(size, "f"),
"limit_price": format(price, "f"),
"post_only": True,
}
limit_only is compatible with this limit request; post_only is compatible because the request always sets it to true. Auction, disabled, cancel-only and view-only products are rejected by this deliberately narrow example. The estimated notional is rounded up to the quote increment for a conservative budget check, but no quote_size is sent for this base-sized limit preview. Validate actual account constraints separately before any future order.
Persist One ID for Each Intended Decision
The Create Order reference says a reused client_order_id does not create another order: Coinbase returns the order corresponding to that ID. Generate an ID once for an intended decision and retain it across restarts or ambiguous results. Creating a new UUID whenever a request is rerun removes this duplicate safeguard.
The next block stores a UUID and the exact preview arguments in a local file. Reruns with the same file and arguments reuse the ID. Changed arguments, malformed records or an interrupted write stop the example instead of silently replacing the record. It is a persistent local teaching record, not a distributed lock, crash-proof journal or broker reconciliation service. Use one operator/process and preserve the file; deleting or moving it loses the local identity.
def unique_keys(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError("Duplicate decision-record key")
result[key] = value
return result
def decision_record(path, args):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
try:
with path.open("x") as file:
record = {"version": 1, "client_order_id": str(uuid.uuid4()), "preview_args": args}
json.dump(record, file, sort_keys=True)
file.flush()
os.fsync(file.fileno())
except FileExistsError:
record = json.loads(path.read_text(), object_pairs_hook=unique_keys)
if (
set(record) != {"version", "client_order_id", "preview_args"}
or type(record["version"]) is not int
or record["version"] != 1
or record["preview_args"] != args
or str(uuid.UUID(record["client_order_id"])) != record["client_order_id"]
):
raise ValueError("Existing decision changed or is malformed; inspect it instead of replacing it")
return record
The UUID is not sent to the Preview Order endpoint, which does not create an order. It records the identity that a separately reviewed future Create Order request would use. This program implements no order submission or automatic retry. Before treating a future submission as absent, inspect broker order history and reconcile the original ID, request, order status and fills. A timeout is not proof of rejection. Do not issue a replacement under a new ID while the original outcome is unresolved.
Put the setup on a watchlist first
Use the rules in this guide to evaluate a signal’s entry, stop, target, and reasoning before deciding what, if anything, to do.
Run Offline, Then Optionally Request an API Preview
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--preview-api", action="store_true")
parser.add_argument("--budget", default="25")
parser.add_argument("--price", default="50000")
parser.add_argument("--decision-file", default=".coinbase-guide/btc-example.json")
options = parser.parse_args()
client = RESTClient(timeout=5) if options.preview_api else None
product = client.get_product("BTC-USD", get_tradability_status=True).to_dict() if client else PRODUCT
args = preview_args(product, options.budget, options.price)
record = decision_record(options.decision_file, args)
print(json.dumps(record, indent=2))
if client:
result = client.preview_limit_order_gtc_buy(**args)
print(json.dumps(result.to_dict(), indent=2))
else:
print("OFFLINE: fixture/request checks only; no Coinbase API call or order")
if __name__ == "__main__":
main()
Default mode creates or reuses .coinbase-guide/btc-example.json, prints the local record and exits. The repeated default decision is not a second intended trade. If the arguments change, review the existing record and any corresponding broker state; choose another decision file only for a deliberately new decision, never to bypass an unresolved result.
To inspect an authenticated preview only, first configure a restricted view key as described above. Then explicitly run python coinbase_preview.py --preview-api --budget 25 --price 50000. The numeric values remain arbitrary examples that you must choose and review; the program does not infer a trading price from product.price. This mode performs Get Product with get_tradability_status=True, validates that response and calls the SDK's preview_limit_order_gtc_buy. Inspect the full returned errors, warnings, estimates and preview identifier. Do not interpret a preview identifier as an order ID or fill.
The default offline checks prove local behavior only. API preview results can change before a future order, and this program does not inspect balances, outstanding orders, holdings, a current order book or fill history. It does not reserve funds or liquidity. No mode in this program calls Create Order, Cancel Orders or a live execution loop.
Acceptance, Fills, Fees and Cancellation Are Separate
An accepted request is not a completed fill. Coinbase order management documents partial fills and remaining open quantities. Market orders take available liquidity, can encounter protection limits, and do not guarantee a complete fill or the displayed price. Limit orders bound the requested price but may remain unfilled. A cancellation request can race with execution; inspect the actual final status and fills instead of assuming immediate cancellation prevented a purchase.
Coinbase Advanced uses maker/taker pricing. Review the current fee rules, the signed-in schedule for the exact account/product and the actual preview. This guide does not publish a headline rate or subscription rebate as a universal cost. A post-only request is designed to avoid taking liquidity, but its broker response and eventual fill/fee records still need inspection. It is not a zero-fee or execution guarantee.
Rate Limits and Failure Handling
Use the current rate-limit documentation for the exact API rather than importing Coinbase Exchange, CDP Platform or another product's limits. Cache appropriate research data, bound polling, and back off read/preview requests on rate limits. A new key is not evidence of an independent rate-limit allowance. For order creation, distinguish throttling from an ambiguous acceptance and reconcile the persisted decision before retrying; this example submits no orders.
| Failure | Inspect before continuing |
|---|---|
| 401/authentication error | Clock, method/path, secret format, key algorithm and restrictions |
| Missing or ambiguous product fields | Current endpoint/schema and access; do not assume tradability |
| Increment/minimum/maximum violation | Round to product increments, recheck budget and both size ranges |
| Preview errors or warnings | Full preview response; do not turn a rejected preview into an order |
| Existing decision changed | Preserve ID/record and inspect the intended decision; do not overwrite it |
| Ambiguous future submission | Original ID, broker history, actual order state and fills |
| Partial fill or cancel race | Remaining open quantity and actual holdings |
The CCXT Alternative
CCXT provides a common interface for multiple exchanges. If you choose its Coinbase adapter, verify the current adapter's authentication, symbols, precision, capabilities and sandbox behavior in the CCXT documentation. A unified interface does not make every venue support the same order types or a paper environment. CCXT's create_order is an execution method, so this guide does not include a default call that sends a funded order. Use the native Coinbase SDK when its documented endpoint-specific preview and response handling fit your workflow.
How Tradewink Handles Coinbase
Tradewink's current Coinbase adapter is marked as not supporting a paper account. Public-plan users cannot connect Coinbase for funded execution; they can build a watchlist, review signals and inspect simulation without a Coinbase connection. Public subscriptions are paper-only; separately approved private beta accounts may submit live broker orders. This disclosure is not a public live-access application path. Review the broker directory for the currently supported paper/sandbox connections.
This guide's standalone SDK example does not test or certify Tradewink's broker adapter, authorization gates, fills or production controls. No provider or local guard eliminates execution errors or loss.
What to Review Before Adding Any Execution
- Current account eligibility, endpoint permissions and key restrictions; least privilege for the action.
- Actual product flags, increment/minimum/maximum constraints, balances, pending orders and holdings.
- One persisted identity per intended decision, plus reconciliation of ambiguous broker outcomes.
- Accepted versus filled quantities, costs, partial fills, cancellation races and remaining exposure.
- Separate actions for stopping new submissions, canceling outstanding orders and closing positions; verify each result.
The paper trading guide explains simulation limitations; how to build a trading bot covers the broader engineering workflow. No fixed number of days, trades or profitable paper observations establishes live readiness. Automation can repeat flawed decisions and can lose all capital it controls.
Frequently Asked Questions
Which Python library should I use for a Coinbase API trading bot?
Coinbase publishes an official SDK called coinbase-advanced-py on PyPI, installed with pip3 install coinbase-advanced-py. It handles JWT generation, REST calls, and WebSocket streaming. CCXT's coinbase class is the main alternative when you want the same code to work across many exchanges.
How do Coinbase CDP API keys work?
You create a Secret API Key in the Coinbase Developer Platform portal, set permissions (view, trade, transfer) and an optional IP allowlist, and securely copy the key name and private key; a JSON download is optional. Your code signs a JSON Web Token with that private key for each request. Coinbase's docs state the JWT expires after 2 minutes and that a different JWT is needed per request.
Does Coinbase Advanced Trade have a sandbox for paper trading?
Only a static one. The sandbox at api-sandbox.coinbase.com needs no authentication, returns pre-defined mocked workflow responses rather than a matching engine. It is useful for checking response formats, not for simulating fills. Fill simulation must be implemented locally or in a framework, with its assumptions disclosed.
Why does my Coinbase API bot get 401 errors with a valid key?
Inspect clock synchronization, secret format, request method/path, endpoint, key permissions and restrictions. The Coinbase App auth page specifies ECDSA while the SDK README documents both key types; SDK signing support is not endpoint acceptance proof. A successful public or sandbox call does not verify private JWT authentication.
What permissions should a Coinbase trading bot key have?
Use the permissions required by the exact endpoint. This guide's optional product read and order preview require API-key view only; they need neither trade nor transfer. Create and cancel order endpoints require trade. Check the current scope table and restrict portfolio/IP access as applicable.
Should I use CCXT or the native Coinbase SDK?
The native SDK exposes Coinbase-specific requests and preview responses. CCXT provides a common interface across exchanges, but authentication, symbols, precision, order capabilities and sandbox support vary by adapter. Check current documentation and implement your own validation, state reconciliation and monitoring.
Is running a Coinbase trading bot profitable?
No example, preview or fixed test duration establishes profitability. Outcomes depend on strategy, data, implementation, costs and execution. The static sandbox does not simulate market fills. Record rejected and unresolved requests, partial fills and actual costs; automation can repeat flawed decisions and lose capital. This is educational content, not financial advice.
Read next
Keep learning with a related guide before putting an idea on your watchlist.
Best Crypto Trading Bots in 2026: How Automated Crypto Trading Works
Crypto trading bots execute automated strategies around the clock. Learn how they work, what to look for when evaluating one, the risks involved, and how AI-driven systems differ from rule-based bots.
Crypto Trading with AI: How to Trade Bitcoin and Altcoins Smarter
Cryptocurrency markets run 24/7 with extreme volatility. Learn how AI trading tools handle crypto markets, manage risk in volatile conditions, and identify opportunities across Bitcoin and altcoins.
Crypto Day Trading Strategies: A Complete Technical Guide for 2026
Day trading cryptocurrency requires different strategies than equities. Learn momentum, breakout, VWAP, and mean reversion approaches tailored for 24/7 crypto markets with AI signal generation.
How to Build a Trading Bot: Step-by-Step Guide (2026)
Learn how to build a trading bot from scratch — architecture, data feeds, strategy logic, risk management, and broker connectivity. Includes a practical framework for beginner and intermediate algorithmic traders.
How to Build an AI Trading Bot: Python, Data and Paper Tests
Build a research prototype with Alpaca Python bars, per-symbol indicators, risk checks, and paper testing. Compare no-code and LLM workflows.
Ready to evaluate a signal?
Start free with a watchlist and inspect the context before you consider a broker connection.
Try AI signals on your watchlist
Send yourself a signal preview, then add tickers to see ranked entries, exits, and risk notes in Tradewink.
Related Signal Types
Tradewink builds explainable market research for self-directed traders. Build a watchlist, inspect signal reasoning and risk context, and paper-track ideas before you decide. Public subscriptions are paper-only; separately approved private beta accounts may submit live broker orders.
How this guide is reviewed
Tradewink reviews educational content against its documented market-data sources, risk controls, and product methodology. See our data sources and evaluation methodology for the evidence and limitations behind the platform.