Binance API Error Code List: Complete Trader's Guide
Master Binance API error codes with real code examples. Learn what each error means, how to handle them in Python, and keep your trading bots running smoothly.
Master Binance API error codes with real code examples. Learn what each error means, how to handle them in Python, and keep your trading bots running smoothly.
If you've built a trading bot or written any automation against Binance, you've hit error codes. That -1121 that killed your order at 3am. The -2010 that fires right when volatility spikes and your bot tries to place 40 orders in two seconds. These aren't random noise — every code is specific, and once you understand what each one means, debugging goes from guesswork to a five-minute fix.
This guide covers the full Binance REST API error code list, what triggers each one, and how to handle them correctly in production Python code. The same principles apply whether you're on Binance Spot, Futures, or Margin — and many of these patterns translate directly to Bybit, OKX, and other exchange APIs that follow similar conventions.
Every error response from the Binance API follows the same JSON shape: a numeric code and a human-readable message. The code is what matters for programmatic handling — the message is helpful for logging but you should never parse it for logic.
import requests
import hmac
import hashlib
import time
from urllib.parse import urlencode
API_KEY = 'your_api_key'
API_SECRET = 'your_api_secret'
BASE_URL = 'https://api.binance.com'
def get_signed_params(params: dict) -> dict:
params['timestamp'] = int(time.time() * 1000)
query_string = urlencode(params)
signature = hmac.new(
API_SECRET.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
params['signature'] = signature
return params
def place_order(symbol: str, side: str, quantity: float):
endpoint = '/api/v3/order'
params = get_signed_params({
'symbol': symbol,
'side': side,
'type': 'MARKET',
'quantity': quantity
})
headers = {'X-MBX-APIKEY': API_KEY}
response = requests.post(BASE_URL + endpoint, params=params, headers=headers)
data = response.json()
if 'code' in data:
print(f'Error {data["code"]}: {data["msg"]}')
return None
return data
The critical thing to notice: Binance returns HTTP 200 even for some errors. Always check for the 'code' key in the response body, not just the HTTP status code. Relying only on status codes will cause silent failures in your bot.
These codes indicate problems on Binance's infrastructure side, or malformed requests before they reach order processing logic. Most -1xxx errors are your code's fault, not Binance's.
| Code | Meaning | Common Cause |
|---|---|---|
| −1000 | UNKNOWN | Unexpected server error — retry with backoff |
| −1001 | DISCONNECTED | Connection dropped mid-request |
| −1002 | UNAUTHORIZED | API key missing or invalid format |
| −1003 | TOO_MANY_REQUESTS | Rate limit hit — back off immediately |
| −1006 | UNEXPECTED_RESP | Non-standard response — log and retry |
| −1007 | TIMEOUT | Request took too long — check network |
| −1013 | INVALID_MESSAGE | Bad JSON in request body |
| −1015 | TOO_MANY_ORDERS | Order rate limit exceeded |
| −1020 | UNSUPPORTED_OPERATION | Method not allowed for this endpoint |
| −1021 | INVALID_TIMESTAMP | Server/client time drift > 1000ms |
| −1022 | INVALID_SIGNATURE | HMAC signature mismatch |
Time sync is the most common silent killer. The -1021 INVALID_TIMESTAMP error fires when your system clock drifts more than 1000ms from Binance's server time. Run NTP sync on any server running a bot, and call GET /api/v3/time periodically to verify your offset stays within bounds.
These are validation errors — your request reached Binance but failed parameter checks before any order logic ran. They're the easiest to fix once you know what each one means.
| Code | Meaning | Fix |
|---|---|---|
| −1100 | ILLEGAL_CHARS | Special chars in a field — sanitize inputs |
| −1101 | TOO_MANY_PARAMETERS | Duplicate or excess params in request |
| −1102 | MANDATORY_PARAM_EMPTY | Required field is null or missing |
| −1103 | UNKNOWN_PARAM | Extra field Binance doesn't recognize |
| −1104 | UNREAD_PARAMETERS | Some params weren't consumed — check spelling |
| −1105 | PARAM_EMPTY | A parameter exists but has empty string value |
| −1106 | PARAM_NOT_REQUIRED | You sent a param that's only valid for other order types |
| −1111 | BAD_PRECISION | Too many decimal places for this symbol |
| −1112 | NO_DEPTH | Order book is empty — unusual, log and skip |
| −1114 | TIF_NOT_REQUIRED | TimeInForce sent with order type that ignores it |
| −1115 | INVALID_TIF | Bad TimeInForce value — use GTC, IOC, or FOK |
| −1116 | INVALID_ORDER_TYPE | Order type string incorrect or not supported |
| −1117 | INVALID_SIDE | Side must be BUY or SELL exactly |
A practical pattern when hitting -1111 (bad precision): Binance's exchange info endpoint tells you the exact step size for each symbol. Fetch it once at startup and use it to round quantities before placing orders. This eliminates an entire class of errors.
import math
def get_step_size(symbol: str) -> float:
"""Fetch the minimum quantity step size for a symbol."""
response = requests.get(f'{BASE_URL}/api/v3/exchangeInfo?symbol={symbol}')
info = response.json()
for filt in info['symbols'][0]['filters']:
if filt['filterType'] == 'LOT_SIZE':
return float(filt['stepSize'])
return 0.001 # fallback
def round_step(quantity: float, step_size: float) -> float:
"""Round quantity down to the nearest valid step."""
precision = int(round(-math.log10(step_size)))
return round(math.floor(quantity / step_size) * step_size, precision)
# Usage
step = get_step_size('BTCUSDT')
qty = round_step(0.00314159, step)
print(f'Rounded quantity: {qty}') # e.g. 0.003
This range is where most trading bot failures live. These errors fire during order placement and modification — after parameter validation passes but before the order hits the matching engine successfully.
| Code | Meaning | What's Actually Happening |
|---|---|---|
| −2010 | NEW_ORDER_REJECTED | Catch-all for order rejection — check msg field |
| −2011 | CANCEL_REJECTED | Order doesn't exist or already filled/canceled |
| −2013 | NO_SUCH_ORDER | Order ID not found — may have been filled |
| −2014 | BAD_API_KEY_FMT | API key format wrong — check for whitespace |
| −2015 | REJECTED_MBX_KEY | IP not whitelisted, or key disabled/expired |
| −2018 | BALANCE_NOT_SUFFICIENT | Not enough funds — check available balance |
| −2019 | MARGIN_NOT_SUFFICIENT | Margin account underfunded |
| −2021 | PRICE_QTY_EXCEED_HARD_LIMITS | Order exceeds max notional or qty limits |
The -2010 code is intentionally vague — Binance uses it as a bucket for many rejection reasons. The actual reason lives in the 'msg' field. Common messages inside -2010: 'Account has insufficient balance', 'Filter failure: PRICE_FILTER', 'Filter failure: MIN_NOTIONAL'. Build a handler that logs the full message for this specific code.
import time
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('binance_bot')
RETRYABLE_CODES = {-1000, -1001, -1006, -1007}
RATE_LIMIT_CODES = {-1003, -1015}
def safe_place_order(symbol: str, side: str, quantity: float, max_retries: int = 3):
endpoint = '/api/v3/order'
for attempt in range(max_retries):
params = get_signed_params({
'symbol': symbol,
'side': side,
'type': 'MARKET',
'quantity': quantity
})
headers = {'X-MBX-APIKEY': API_KEY}
try:
response = requests.post(BASE_URL + endpoint, params=params, headers=headers, timeout=5)
data = response.json()
except requests.exceptions.Timeout:
logger.warning(f'Attempt {attempt+1}: Request timed out, retrying...')
time.sleep(2 ** attempt)
continue
if 'code' not in data:
logger.info(f'Order placed: {data["orderId"]}')
return data
code = data['code']
msg = data.get('msg', '')
if code in RATE_LIMIT_CODES:
wait = 60 if code == -1003 else 10
logger.warning(f'Rate limit hit (code {code}), waiting {wait}s')
time.sleep(wait)
continue
elif code in RETRYABLE_CODES:
logger.warning(f'Retryable error {code}: {msg}, attempt {attempt+1}')
time.sleep(2 ** attempt)
continue
elif code == -2010:
logger.error(f'Order rejected — full message: {msg}')
return None # Don't retry rejections
elif code == -1021:
logger.error('Timestamp error — check system clock sync')
return None
else:
logger.error(f'Unhandled error {code}: {msg}')
return None
logger.error(f'Max retries reached for {symbol} {side}')
return None
Binance enforces multiple rate limit tiers simultaneously. Ignoring them doesn't just cost you errors — it gets your IP or API key banned. The limits are tracked in response headers on every successful request.
Bybit and OKX use similar rate limit structures, and tools like VoiceOfChain that aggregate real-time signals across multiple exchanges are specifically designed to handle the edge case where you need to act fast on a signal without burning your rate limit budget on price polling. Instead of hammering the API for price updates, you consume a WebSocket or signal stream and reserve your weight budget for actual order operations.
The traders who get burned by API errors aren't the ones who hit them — everyone hits them. The ones who get burned are those without systematic error handling. A production bot needs to distinguish between errors that warrant a retry, errors that indicate a logic bug in your code, and errors that require human attention.
Platforms like VoiceOfChain handle this at the infrastructure level for signal delivery — the signal reaches you reliably regardless of what's happening at the exchange API layer. Your job as the bot developer is to handle the execution side: validate your order parameters against live exchange info, respect rate limits proactively rather than reactively, and build an alerting layer that pages you when error rates spike unexpectedly.
The Binance error code system is well-documented and consistent — once you've mapped your error handler to cover the codes in this guide, you've covered the vast majority of what you'll encounter in production. The same disciplined approach works whether you're trading on Binance Spot, Binance Futures, or expanding to Bybit, OKX, or KuCoin — the specifics differ, but the architecture is identical.