Short answer: Crypto exchange API integration means connecting your software to an exchange's REST endpoints for orders, balances and reference data, and to its WebSocket streams for live market and account events. A robust integration signs every private request, keeps its clock synchronized, stays inside the published rate limits, tags each order with a client order ID and treats timeouts as unknown outcomes to be checked, never as failures to retry blindly.
Debug signing with a sample or testnet key and budget your requests: HMAC signature generator and API rate limit calculator.
Every major exchange offers the same basic capabilities, but the details differ, and they matter most where money is at stake: a duplicate order after a retry, an order book that silently drifts out of sync, or an IP ban in the middle of a volatile session. If you are still sorting out the four kinds of crypto API, start there; this guide is about exchange trading APIs.
This guide covers the parts of an integration that decide whether it holds up in production, based on the official Binance Spot, Kraken Spot and Coinbase Advanced Trade documentation as of October 2026. Exchanges are named only to illustrate their published APIs, not as recommendations; which platforms you may use depends on your jurisdiction and each exchange's terms. Automated trading can lose money, however well it is engineered. To commission this work, see the exchange API integration page, which describes it in general terms. Notes by exchange are collected under crypto exchange APIs.
REST vs WebSocket: which does what in trading?
A crypto exchange API connects your software to one venue's market data and, with a key, to your account on that venue, through two channels. REST is for actions and point-in-time queries; WebSocket is for anything that changes continuously. A REST call is a self-contained request and response, so every private call carries its own key and signature. A WebSocket connection stays open and the exchange pushes events as they happen, which is far cheaper than polling; Binance's own rate-limit error tells clients to use WebSocket streams for live updates instead.
| Task | Channel | Example |
|---|---|---|
| Symbol rules, tick and lot sizes | REST | GET /api/v3/exchangeInfo |
| Place, query, cancel orders | REST or WebSocket API | POST /api/v3/order, order.place |
| Order book snapshot | REST | GET /api/v3/depth |
| Trades, book updates | Market streams | btcusdt@trade, btcusdt@depth |
| Fills, order status, balances | User data stream | executionReport |
The split is not absolute: Binance's WebSocket API accepts signed order requests over a persistent connection, and Kraken documents add_order and cancel_order on its WebSocket. Combining a REST snapshot with incremental stream updates is the standard way to maintain a local order book, explained in the local order book guide.
How do exchanges authenticate API requests?
Private endpoints need a key that identifies the account and a signature that proves the request came from the key holder unaltered. The three exchanges use three schemes:
- Binance Spot: the key goes in the
X-MBX-APIKEYheader and asignatureparameter is computed over the query string plus body, with HMAC-SHA256, RSA or Ed25519 keys. Binance highly recommends Ed25519. - Kraken Spot REST:
API-Keyheader plusAPI-Sign, an HMAC-SHA512 of the URI path and SHA-256(nonce + POST data), keyed with the base64-decoded secret. - Coinbase Advanced Trade: a CDP API key signs a JSON Web Token sent as a Bearer header; each token expires after two minutes, each request needs a new one, and ECDSA (ES256) keys are required.
Signing bugs are hard to diagnose from a generic invalid-signature error, so test your signer against the worked examples in the documentation first. The functions below reproduce the published Binance and Kraken examples offline, using only those illustrative values:
import base64, hashlib, hmac, urllib.parse
def binance_hmac(secret, params):
"""Binance Spot: HMAC-SHA256 of the query string, hex-encoded."""
payload = urllib.parse.urlencode(params)
return hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
def kraken_sign(secret_b64, uri_path, data):
"""Kraken Spot REST: HMAC-SHA512 of path + SHA256(nonce + body), base64."""
body = urllib.parse.urlencode(data)
inner = hashlib.sha256((str(data["nonce"]) + body).encode()).digest()
mac = hmac.new(base64.b64decode(secret_b64), uri_path.encode() + inner, hashlib.sha512)
return base64.b64encode(mac.digest()).decode()
# Test vectors published in the official docs (illustrative keys, not real accounts)
b = binance_hmac("NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j",
{"symbol": "LTCBTC", "side": "BUY", "type": "LIMIT", "timeInForce": "GTC",
"quantity": 1, "price": 0.1, "recvWindow": 5000, "timestamp": 1499827319559})
k = kraken_sign("kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg==",
"/0/private/AddOrder",
{"nonce": "1616492376594", "ordertype": "limit", "pair": "XBTUSD",
"price": 37500, "type": "buy", "volume": 1.25})
print(b == "c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71")
print(k == "4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==")
Both lines print True. Where asymmetric keys are offered, prefer them: the private key never leaves your server. Permissions, IP allowlists and secret storage are covered in API key security for trading bots.
Timestamps, recvWindow and clock sync
Signed Binance requests carry a timestamp and are rejected if the client clock disagrees with the server. As of October 2026 a request is processed only if its timestamp is less than one second ahead of server time and no older than recvWindow, which defaults to 5,000 ms and cannot exceed 60,000 ms; violations return -1021. Binance recommends 5,000 or less, and raising it to the maximum only hides clock problems. Run the host under NTP and log the offset against GET /api/v3/time.
Kraken uses an always-increasing nonce instead. Its documentation warns that nonces arriving out of order, typically from one key shared by several processes or from clock recalibration, produce EAPI:Invalid nonce, that repeated invalid nonces can lead to temporary bans, and that a nonce can never be reset lower. Use one key per process.
What does API rate limit exceeded mean? Limits and weights
An API rate limit caps the requests a client may send in a window, and "rate limit exceeded" means you went over it: the exchange refuses requests until the window or counter recovers. Limits are measured differently by exchange, so one generic throttle does not fit all.
| Interface | Limit |
|---|---|
| Binance Spot REST | 6,000 request weight per minute per IP; 100 orders per 10 seconds and 200,000 per day per account (live exchangeInfo, 24 October 2026) |
| Kraken Spot REST | Call counter capped at 15 or 20 by tier, decaying 0.33 to 1 per second; history calls cost 2; order placement and cancellation use a separate limiter |
| Coinbase Advanced Trade WebSocket | Connections and unauthenticated messages each limited to 8 per second per IP |
Binance weights each endpoint, and the header X-MBX-USED-WEIGHT-1M reports consumption. Exceeding a limit returns HTTP 429 with Retry-After; clients that keep sending get HTTP 418 bans scaling from 2 minutes to 3 days. Read limits from exchangeInfo at start-up instead of hard-coding them, and keep a client-side budget below the ceiling. Endpoint weights and futures limits are in the Binance API rate limit guide.
Order lifecycle, user data streams and client order IDs
An order is a state machine that your system must follow to a final state using exchange events, not assumptions. Binance states include NEW, PARTIALLY_FILLED, FILLED, CANCELED, REJECTED and EXPIRED; each change arrives as an executionReport with the client order ID (c), status (X) and cumulative filled quantity (z). As of October 2026 Binance delivers these events through its WebSocket API (userDataStream.subscribe or userDataStream.subscribe.signature); the old listenKey endpoints stopped working on 20 February 2026. Kraken's private channels use a token obtained from GetWebSocketsToken.
Client order IDs make lookups and reconciliation reliable. Generate the ID before sending and store it with the order intent:
- Binance
newClientOrderIdmust be unique among open orders; a duplicate is rejected while the first is open, but the ID can be reused once the first has filled, so a matching ID alone does not stop a double fill. - Kraken checks
cl_ord_id(a UUID or up to 18 characters of text) for uniqueness across open orders. - Coinbase requires
client_order_id; if it is not unique, no new order is created and the existing one is returned.
These rules underpin duplicate-order protection and the order state machine of a crypto order management system.
Error handling and retries with backoff
Decide from the class of error, and never resend an order without first finding out whether the previous attempt executed.
| Response | Meaning | Action |
|---|---|---|
4XX, e.g. -1022, -1013, -2015 | Bad signature, filter failure, invalid key, IP or permission | Fix the request; do not retry unchanged |
| 429 or 418 | Rate limit hit or IP banned | Wait at least Retry-After seconds |
5XX or -1007 | Execution status unknown | Query by client order ID before any resend |
| Network error on a read | Nothing changed | Retry with exponential backoff and jitter |
Binance states that a 5XX must not be treated as a failure because the request may have succeeded, and that a -1007 timeout does not always mean the order failed. Backoff with jitter (1, 2, 4, 8 seconds plus a random fraction, capped) suits reads and reconnects; order submission needs reconciliation.
Hypothetical example. A bot sends a limit buy with client ID grid-0412-b7 and gets HTTP 503 after ten seconds. A naive retry could buy twice. Instead it queries GET /api/v3/order with origClientOrderId=grid-0412-b7. If the order exists, it records the state. If the answer is -2013 (order does not exist) and the user data stream showed nothing, that is still not proof of failure: Binance's documentation says the API stops waiting for the matching engine after 10 seconds and that a timeout does not always mean the request failed. The bot waits, checks again and, if the order is still missing, stops and alerts a person instead of resending. A resend with the same client ID is rejected only while the first order is open; an order that has already filled would not block it.
Reconnect and resubscribe logic
Assume every WebSocket connection will drop, and design the reconnect path as carefully as the normal one.
- Expected disconnects: Binance closes connections at the 24-hour mark, sends a
serverShutdownevent before a server shuts down, pings every 20 seconds and disconnects clients that do not pong within a minute. - Silent failures: Kraken recommends subscribing to its heartbeat channel and reconnecting if nothing arrives within a deadline such as 5 seconds. Coinbase disconnects clients that do not subscribe within 5 seconds.
- Rebuild state: subscriptions are not restored automatically. Replay them with exponential backoff, rebuild order books from a fresh snapshot and reconcile open orders through REST, since fills may have happened while you were offline.
- Gap detection: Coinbase messages carry per-product sequence numbers, and a jump of more than one means a dropped message; Binance depth updates carry update IDs for the same purpose.
For Binance, a reconnect loop in Python and the state to rebuild afterwards are in Binance WebSocket limits and reconnects.
Normalizing multiple exchanges and using testnets
A multi-exchange system needs one internal model and thin adapters, because symbols, quote assets and precision differ: bitcoin against the US dollar is BTC/USD in Kraken's WebSocket v2, XBTUSD in its REST examples and BTC-USD on Coinbase, while Binance's BTCUSDT is quoted in the USDT stablecoin, a different market that an adapter must not silently treat as USD. Store prices and quantities as decimals, round in the direction that keeps orders valid, and refresh the rules regularly.
The Binance Spot Testnet (testnet.binance.vision, GitHub login as of October 2026) uses virtual funds, supports only /api endpoints and is reset roughly monthly, so it tests your code, not your strategy. A testnet walkthrough is in the Binance API Python guide listed below, and the multi-exchange arbitrage monitoring scenario shows a full design.
In Python, CCXT wraps many exchanges behind one interface and an official SDK covers one. On GitHub, start from the exchange's own organization, where Binance, for example, publishes its Spot API documentation (see the sources below), and read third-party code before giving it a key.
An aggregator API does this normalization for market data only: it collects prices from many venues for reading, not for trading from your account. CoinGecko's API documentation (opens in a new tab), for example, describes coverage of more than 1,500 exchanges and a free Demo plan with limited endpoints and rate limits (as of November 2026); the crypto price API guide compares such providers, and orders and fills are still reconciled against each exchange's own data.
For the documented APIs of platforms on the list kept by Turkey's Capital Markets Board (SPK, also CMB), see the Turkish crypto exchange APIs guide. Being on that list does not mean a platform is authorized, and under Article 99/A of Turkey's Capital Markets Law activity by foreign crypto-asset platforms directed at Turkish residents counts as unauthorized.
Frequently asked questions
Should a trading bot use REST or WebSocket?
Both. Use WebSocket streams for market data and order updates, and REST or the exchange's WebSocket API for orders, reference data and reconciliation after reconnects.
What is recvWindow in the Binance API?
It is how many milliseconds after its timestamp a signed request stays valid. As of October 2026 it defaults to 5,000 ms and cannot exceed 60,000 ms.
What is API rate limit exceeded?
The client sent more requests than the exchange allows in a window. Binance returns HTTP 429 with a Retry-After header, and Kraken returns EAPI:Rate limit exceeded until its counter decays.
How do I avoid duplicate orders after a timeout?
Assign a client order ID before sending, then query by that ID or check the user data stream before doing anything else. Reusing the same ID helps only while the first order is open or on exchanges that deduplicate it; on Binance, an order that has already filled would not block a resend.
Does a successful testnet run prove a strategy works?
No. It shows your integration behaves correctly; testnet prices and fills differ from live markets, and a correct integration can still lose money.
Sources
- Binance Spot API documentation: REST API (general information, limits, request security) (opens in a new tab)
- Binance Spot API documentation: WebSocket Streams (opens in a new tab)
- Binance Spot API documentation: WebSocket API (opens in a new tab)
- Binance Spot API documentation: User Data Streams (opens in a new tab)
- Binance Spot API CHANGELOG (listenKey retirement) (opens in a new tab)
- Binance Spot API documentation: Error codes (opens in a new tab)
- Binance Spot Testnet: General info (opens in a new tab)
- Kraken: Spot REST authentication (opens in a new tab)
- Kraken: Spot REST rate limits (opens in a new tab)
- Kraken: Spot client order identifiers (cl_ord_id) (opens in a new tab)
- Kraken: WebSocket reconnection and resilience (opens in a new tab)
- Coinbase Advanced Trade API: Create Order (opens in a new tab)
- Coinbase App API key authentication (JWT) (opens in a new tab)
- Coinbase Advanced Trade WebSocket overview (opens in a new tab)
- Coinbase Advanced Trade WebSocket rate limits (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.