바이낸스(Binance) API 에러 코드는 음수 정수 인코딩 방식을 사용하며, 범위는 -1000에서 -2099까지입니다. 이는 공통 오류 (-1000 ~ -1199), 요청 오류 (-1100 ~ -1199), 빈도 제한/리스크 관리 (-1003/-1013), **주문 오류 (-2010 ~ -2099)**의 네 가지 카테고리로 나뉩니다. 본문에서는 실제 프로덕션 환경에서 자주 마주치는 80개 이상의 에러 코드와 해결 방법을 정리하였으며, Python 오류 처리 미들웨어 코드 예시를 함께 제공합니다. API를 개통하지 않은 독자께서는 먼저 바이낸스 공식 사이트에서 KYC를 완료해 주시고, 신규 사용자는 무료 가입을 통해 시작할 수 있습니다.
1. 오류 응답 구조
바이낸스 API의 모든 오류 응답 형식은 동일합니다:
{
"code": -1021,
"msg": "Timestamp for this request is outside of the recvWindow."
}
HTTP 상태 코드는 주로 400 (비즈니스 오류) 또는 430/429/418 (게이트웨이 오류)입니다.
2. 공통 오류 (-1000 ~ -1099)
| 에러 코드 | 메시지 | 의미 | 해결 방법 |
|---|---|---|---|
| -1000 | UNKNOWN | 알 수 없는 오류 | 재시도; 지속될 경우 고객센터 문의 |
| -1001 | DISCONNECTED | 서버 내부 연결 끊김 | 5초 대기 후 재시도 |
| -1002 | UNAUTHORIZED | 권한 인증 실패 | X-MBX-APIKEY 헤더 확인 |
| -1003 | TOO_MANY_REQUESTS | 빈도 제한 초과 | Retry-After 헤더의 시간만큼 대기 |
| -1006 | UNEXPECTED_RESP | 예외적 응답 | 재시도 |
| -1007 | TIMEOUT | 요청 시간 초과 | recvWindow 값 증가 |
| -1014 | UNKNOWN_ORDER_COMPOSITION | 지원되지 않는 주문 조합 | 주문 유형 확인 |
| -1015 | TOO_MANY_ORDERS | 주문 수 한도 초과 | 미체결 주문 일부 취소 |
| -1016 | SERVICE_SHUTTING_DOWN | 서비스 중단 중 | 복구 시까지 대기 |
| -1020 | UNSUPPORTED_OPERATION | 지원되지 않는 조작 | API 문서 참조 |
| -1021 | INVALID_TIMESTAMP | 타임스탬프 오차 | NTP 동기화 또는 recvWindow 확장 |
| -1022 | INVALID_SIGNATURE | 유효하지 않은 서명 | 서명 알고리즘 확인 |
주요 오류 상세 분석
-1003 TOO_MANY_REQUESTS:
{"code": -1003, "msg": "Too many requests; current limit is 6000 request weight per 1 MINUTE. Please use WebSocket Streams for live updates to avoid polling the API."}
처리 예시:
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
time.sleep(retry_after)
# 이후 재시도
-1021 INVALID_TIMESTAMP:
{"code": -1021, "msg": "Timestamp for this request is outside of the recvWindow."}
처리 예시:
# 방법 1: 서버 시간 차이 동기화
server_time = requests.get("https://api.binance.com/api/v3/time").json()["serverTime"]
local_time = int(time.time() * 1000)
offset = server_time - local_time # 저장 후 매 서명 시 더해줌
# 방법 2: recvWindow 확장 (최대 60,000)
params["recvWindow"] = 10000
-1022 INVALID_SIGNATURE:
{"code": -1022, "msg": "Signature for this request is not valid."}
일반적인 원인:
- 서명 문자열과 요청 문자열의 파라미터 순서 불일치
- 숫자 파라미터에 따옴표 사용
- Secret Key가 잘렸거나 공백 포함
- URL 인코딩 방식 차이
3. 파라미터 오류 (-1100 ~ -1199)
| 에러 코드 | 메시지 | 의미 | 처리 방법 |
|---|---|---|---|
| -1100 | ILLEGAL_CHARS | 파라미터에 잘못된 문자 포함 | 입력값 정리 |
| -1101 | TOO_MANY_PARAMETERS | 파라미터 과다 | 파라미터 간소화 |
| -1102 | MANDATORY_PARAM_EMPTY_OR_MALFORMED | 필수 파라미터 누락/잘못된 형식 | API 문서 확인 |
| -1103 | UNKNOWN_PARAM | 알 수 없는 파라미터 | 불필요한 필드 제거 |
| -1104 | UNREAD_PARAMETERS | 파라미터 미사용 | 중복 파라미터 삭제 |
| -1105 | PARAM_EMPTY | 파라미터 값이 비어 있음 | 유효한 값 입력 |
| -1106 | PARAM_NOT_REQUIRED | 불필요한 파라미터 전달 | 제거 |
| -1111 | BAD_PRECISION | 정밀도 오류 | LOT_SIZE / PRICE_FILTER 준수 |
| -1112 | NO_DEPTH | 호가창이 비어 있음 | 시세 복구 대기 |
| -1114 | TIF_NOT_REQUIRED | TIF 적용 불가 | 시장가 주문 시 timeInForce 제외 |
| -1115 | INVALID_TIF | 유효하지 않은 TIF | GTC / IOC / FOK 사용 |
| -1116 | INVALID_ORDER_TYPE | 유효하지 않은 주문 유형 | 지원 유형 확인 |
| -1117 | INVALID_SIDE | side 값이 잘못됨 | BUY 또는 SELL 사용 |
| -1118 | EMPTY_NEW_CL_ORD_ID | 클라이언트 주문 ID가 비어 있음 | newClientOrderId 제공 또는 생략 |
| -1119 | EMPTY_ORG_CL_ORD_ID | 원본 주문 ID가 비어 있음 | 취소/조회 시 orderId 제공 |
| -1120 | BAD_INTERVAL | K라인 간격이 잘못됨 | 1m/5m/15m/1h/4h/1d 등 |
| -1121 | BAD_SYMBOL | 유효하지 않은 거래쌍 | exchangeInfo를 통한 검증 |
| -1125 | INVALID_LISTEN_KEY | listenKey가 만료됨 | userDataStream 재호출 |
| -1127 | MORE_THAN_XX_HOURS | 조회 시간 범위 초과 | 여러 번 나누어 조회 |
| -1128 | OPTIONAL_PARAMS_BAD_COMBO | 선택 파라미터 조합 오류 | 문서의 상호 배타적 필드 확인 |
| -1130 | INVALID_PARAMETER | 파라미터 값이 유효 범위를 벗어남 | 값 범위 수정 |
| -1131 | INVALID_JSON | JSON 형식 오류 | JSON 유효성 검사 |
주요 오류 상세 분석
-1111 BAD_PRECISION (매우 빈번한 오류):
Filter failure: LOT_SIZE
원인: 수량 또는 가격이 해당 거래쌍의 stepSize / tickSize와 일치하지 않음.
# 올바른 방법: 거래 규칙 조회 후 반올림/버림 처리
import math
def get_filters(symbol: str) -> dict:
r = requests.get("https://api.binance.com/api/v3/exchangeInfo",
params={"symbol": symbol}).json()
return {f["filterType"]: f for f in r["symbols"][0]["filters"]}
def round_step(value: float, step: float) -> float:
return math.floor(value / step) * step
filters = get_filters("BTCUSDT")
step_size = float(filters["LOT_SIZE"]["stepSize"]) # 예: 0.00001
tick_size = float(filters["PRICE_FILTER"]["tickSize"]) # 예: 0.01
min_notional = float(filters["NOTIONAL"]["minNotional"]) # 예: 5.0
# 주문 전 정규화
quantity = round_step(0.0015789, step_size) # → 0.00157
price = round_step(60123.4567, tick_size) # → 60123.45
4. 필터 실패 (-1013)
-1013 INVALID_MESSAGE는 다양한 필터와 함께 구체적인 이유를 제공합니다:
| 필터 유형 | 의미 | 처리 방법 |
|---|---|---|
| PRICE_FILTER | 가격이 tickSize와 맞지 않음 | tickSize에 맞춰 보정 |
| LOT_SIZE | 수량이 stepSize와 맞지 않음 | stepSize에 맞춰 보정 |
| MIN_NOTIONAL | 주문 총액이 너무 낮음 (<5 USDT) | 수량 증가 |
| NOTIONAL | 명목 가치 불일치 | MIN_NOTIONAL과 동일 |
| PERCENT_PRICE | 가격이 시장가와 너무 동떨어짐 | 시장가에 가깝게 조정 |
| MARKET_LOT_SIZE | 시장가 주문 수량 부적합 | 시장가 주문 규칙 준수 |
| MAX_NUM_ORDERS | 해당 거래쌍의 대기 주문 수 초과 | 기존 주문 취소 후 재주문 |
| MAX_NUM_ALGO_ORDERS | 알고리즘 주문 수 초과 | 일부 손절/익절 주문 취소 |
5. 주문 오류 (-2010 ~ -2099)
| 에러 코드 | 메시지 | 의미 | 처리 방법 |
|---|---|---|---|
| -2010 | NEW_ORDER_REJECTED | 주문 거부됨 | msg 필드 상세 내용 확인 |
| -2011 | CANCEL_REJECTED | 취소 거부됨 | 주문이 이미 체결되었을 수 있음 |
| -2013 | NO_SUCH_ORDER | 존재하지 않는 주문 | orderId 확인 |
| -2014 | BAD_API_KEY_FMT | Key 형식 오류 | 64자리 확인 |
| -2015 | REJECTED_MBX_KEY | Key/IP/권한 오류 | 화이트리스트 및 권한 확인 |
| -2016 | NO_TRADING_WINDOW | 거래 창 없음 | 해당 거래쌍 거래 중지 상태 |
| -2018 | BALANCE_NOT_SUFFICIENT | 잔액 부족 | 충전 또는 수량 감소 |
| -2019 | MARGIN_NOT_SUFFICIENT | 증거금 부족 | 레버리지 축소 또는 증거금 추가 |
| -2020 | UNABLE_TO_FILL | 체결 불가 | 지정가 주문 가격 조정 |
| -2021 | ORDER_WOULD_IMMEDIATELY_TRIGGER | 즉시 트리거되는 주문 | 손절/익절가 조정 |
| -2022 | REDUCE_ONLY_REJECT | Reduce Only 거부됨 | 포지션 방향 확인 |
| -2023 | USER_IN_LIQUIDATION | 강제 청산 진행 중 | 청산 완료 시까지 대기 |
| -2024 | POSITION_NOT_SUFFICIENT | 포지션 부족 | 평단가 및 보유 수량 확인 |
| -2025 | MAX_OPEN_ORDER_EXCEEDED | 대기 주문 수 한도 초과 | 기존 주문 취소 |
| -2026 | REDUCE_ONLY_ORDER_TYPE_NOT_SUPPORTED | 유형 미지원(Reduce Only) | 시장가 또는 지정가로 변경 |
| -2027 | MAX_LEVERAGE_RATIO | 레버리지 한도 초과 | 레버리지 하향 |
| -2028 | MIN_LEVERAGE_RATIO | 레버리지 너무 낮음 | 레버리지 상향 |
주요 오류 상세 분석
-2010 NEW_ORDER_REJECTED (가장 흔함):
msg 필드에서 구체적인 이유를 확인할 수 있습니다:
"Account has insufficient balance for requested action"→ 잔액 부족"Order would immediately match and take"→ 지정가 주문이 즉시 체결됨 (LIMIT_MAKER 모드 시)"Filter failure: LOT_SIZE"→ 수량이 단계를 따르지 않음"Filter failure: PERCENT_PRICE"→ 가격 편차가 너무 큼
def place_order_safely(symbol, side, qty, price):
try:
return client.create_order(
symbol=symbol, side=side, type="LIMIT",
timeInForce="GTC", quantity=qty, price=price
)
except BinanceAPIException as e:
if e.code == -2010:
if "insufficient balance" in e.message:
return {"error": "잔액 부족"}
if "Filter failure: LOT_SIZE" in e.message:
return {"error": "수량 정밀도 오류, stepSize에 맞춰 조정 필요"}
raise
-2015 REJECTED_MBX_KEY:
세 가지 세부 원인 (오류 메시지에서 유추):
- API Key 형식 오류: 64자리 길이 확인
- IP 화이트리스트 미포함: 바이낸스 로그인 후 업데이트
- 권한 미설정: 예: /fapi/ 호출 시 Enable Futures 체크 여부 확인
6. 리스크 관리 관련 오류
| 에러 코드 | 메시지 | 의미 | 처리 방법 |
|---|---|---|---|
| -4001 | PRICE_LESS_THAN_ZERO | 가격이 음수임 | 파라미터 확인 |
| -4002 | PRICE_GREATER_THAN_MAX_PRICE | 가격이 상한선 초과 | 가격 하향 조정 |
| -4003 | QTY_LESS_THAN_ZERO | 수량이 음수임 | 양수로 수정 |
| -4004 | QTY_LESS_THAN_MIN_QTY | 수량이 최소치 미달 | LOT_SIZE 참조 |
| -4005 | QTY_GREATER_THAN_MAX_QTY | 수량이 상한선 초과 | 분할 주문 |
| -4006 | STOP_PRICE_LESS_THAN_ZERO | 손절가가 음수임 | 수정 필요 |
| -4164 | MIN_NOTIONAL | 명목 금액이 5 USDT 미만 | 수량 증가 |
| -5021 | FOK_ORDER_REJECT | FOK 주문 거부됨 | 유동성 부족, GTC로 교체 |
| -5022 | GTX_ORDER_REJECT | GTX (Post Only) 거부됨 | 시장가 변동으로 인해 지정가 주문 불가 |
7. 통합 오류 처리 미들웨어 (Python)
import time
import requests
from typing import Optional
class BinanceAPIError(Exception):
def __init__(self, code: int, msg: str, raw: dict):
self.code = code
self.msg = msg
self.raw = raw
super().__init__(f"Binance {code}: {msg}")
def handle_response(response: requests.Response) -> dict:
data = response.json()
if "code" in data and data["code"] < 0:
raise BinanceAPIError(data["code"], data.get("msg", ""), data)
return data
def request_with_recovery(method, url, **kwargs) -> Optional[dict]:
for attempt in range(5):
try:
r = requests.request(method, url, **kwargs)
if r.status_code == 429:
retry_after = int(r.headers.get("Retry-After", 60))
print(f"빈도 제한, {retry_after}초 대기")
time.sleep(retry_after)
continue
if r.status_code == 418:
raise SystemExit("IP 차단됨, 프로그램 종료")
data = handle_response(r)
return data
except BinanceAPIError as e:
if e.code == -1021:
print("타임스탬프 오차, 재동기화 중")
# 시간 동기화 후 재시도
continue
if e.code in (-1001, -1006, -1007):
wait = 2 ** attempt
print(f"일시적 오류 {e.code}, {wait}초 후 재시도")
time.sleep(wait)
continue
# 복구 불가능한 비즈니스 오류는 그대로 발생시킴
raise
return None
8. 자주 묻는 질문 FAQ
Q1: 에러 코드 -1021이 반복적으로 발생하면 어떻게 하나요?
A: 시스템 시계의 문제입니다. Linux라면 chronyd를 활성화하세요:
sudo systemctl enable chronyd
sudo chronyc sources -v # 동기화 소스 확인
Windows라면 제어판 → 날짜 및 시간 → 인터넷 시간 → time.windows.com과 동기화를 진행하세요.
Q2: -1015 TOO_MANY_ORDERS가 발생하는 이유는 무엇인가요?
A: 단일 거래쌍의 미체결 대기 주문 수가 200개(현물) 또는 MAX_NUM_ORDERS 필터에 설정된 한도를 초과했기 때문입니다. 현재 가격과 먼 대기 주문을 일부 취소하면 해결됩니다.
Q3: -2015 오류가 발생하는데 권한과 IP를 확인해도 문제가 없습니다.
A: 현물(Spot) Key를 선물 도메인(fapi.binance.com)에 사용했거나 그 반대인지 확인하세요. 현물과 선물은 서로 다른 권한 비트가 필요하며, 동일한 키라도 Enable Futures가 체크되어 있지 않으면 선물 인터페이스를 사용할 수 없습니다.
Q4: 로그에 모든 에러 코드를 기록하려면 어떻게 하나요?
A: 미들웨어 계층을 캡슐화하여 에러 코드별로 통계를 내는 것이 좋습니다:
from collections import Counter
error_counter = Counter()
def log_error(code: int):
error_counter[code] += 1
if error_counter[code] > 100:
alert(f"에러 {code}가 {error_counter[code]}번 발생했습니다. 시스템 점검이 필요합니다.")
Q5: -1013과 -2010의 차이점은 무엇인가요?
A: -1013은 필터(filter) 오류 (파라미터가 거래 규칙에 맞지 않음)이며, -2010은 주문 거부 (비즈니스 계층에서의 거부, 이유는 msg에 기재)입니다. -1013은 보통 클라이언트 측에서 미리 계산하여 방지할 수 있지만, -2010은 잔액, 슬리피지, 리스크 관리 등 실시간 시장 상태에 따라 달라집니다.
에러 코드 목록을 확인하셨다면 카테고리로 돌아가 「API 연동」 카테고리의 다른 기술 주제를 살펴보세요.