API 연동

바이낸스 API 사용 시 IP 차단될까? 제한 정책 및 가중치 계산법 총정리

바이낸스 API 가중치 및 속도 제한(Rate Limit) 메커니즘 완벽 해부: 분당 6,000 가중치 할당, 인터페이스별 가중치 목록, 응답 헤더 읽기, 429/418 트리거 조건, 토큰 버킷 구현, WebSocket 대안 및 Python 제한 코드 포함.

바이낸스 API 제한의 핵심 메커니즘은 **가중치 버킷(Weight Bucket)**입니다. 각 IP는 분당 최대 6,000 가중치(현물 기준)를 소모할 수 있으며, 각 인터페이스는 복잡도에 따라 1에서 100까지의 가중치 점수를 차감합니다. 한도를 초과하면 429 Too Many Requests가 반환되며, 반복적으로 위반할 경우 418 응답과 함께 IP가 2분에서 최대 3일 동안 차단될 수 있습니다. 이 글에서는 가중치 계산, 응답 헤더 모니터링, 클라이언트측 제한, WebSocket 대안의 4단계에 걸쳐 전략이 안정적으로 실행되도록 하는 전체 코드를 제공합니다. 아직 API 키가 없다면 바이낸스 공식 사이트에서 KYC 인증을 완료하세요. 계정이 없다면 무료 회원가입이 가능합니다.

1. 속도 제한의 세 가지 차원

바이낸스는 동일한 IP 또는 API 키에 대해 세 가지 독립적인 제한을 적용합니다.

차원 현물(Spot) 상한 선물(Futures) 상한 위반 시 응답
요청 가중치 (REQUEST_WEIGHT) 6,000 / 분 2,400 / 분 429
주문 횟수 (ORDERS) 100 / 10초; 200,000 / 일 300 / 10초; 1,200 / 분 429
연결 수 (RAW_REQUESTS) 61,000 / 5분 61,000 / 5분 429
IP 차단 연속 위반 시 연속 위반 시 418

핵심 인지 사항: 주문 인터페이스의 가중치는 1에 불과하지만, ORDERS 버킷도 동시에 소모합니다. 두 버킷 중 하나라도 상한에 도달하면 제한이 적용됩니다.

2. 현재 가중치 할당량 조회

GET /api/v3/exchangeInforateLimits 필드를 통해 현재 계정의 실시간 할당량을 확인할 수 있습니다.

curl -s "https://api.binance.com/api/v3/exchangeInfo" | \
  jq '.rateLimits'

반환값:

[
  {"rateLimitType": "REQUEST_WEIGHT", "interval": "MINUTE", "intervalNum": 1, "limit": 6000},
  {"rateLimitType": "ORDERS", "interval": "SECOND", "intervalNum": 10, "limit": 100},
  {"rateLimitType": "ORDERS", "interval": "DAY", "intervalNum": 1, "limit": 200000},
  {"rateLimitType": "RAW_REQUESTS", "interval": "MINUTE", "intervalNum": 5, "limit": 61000}
]

VIP 등급이 높은 계정은 가중치 상한 증설을 신청할 수 있지만, 일반 계정의 6,000 상한으로도 전략의 90% 이상을 충족할 수 있습니다.

3. 주요 인터페이스 가중치 비교표

인터페이스 가중치 설명
GET /api/v3/ping 1 연결 테스트
GET /api/v3/time 1 서버 시간 확인
GET /api/v3/exchangeInfo 20 거래 규칙 (1시간마다 캐시 권장)
GET /api/v3/ticker/price (단일 symbol) 1 단일 종목 가격
GET /api/v3/ticker/price (전체) 4 한 번에 모든 시세 조회
GET /api/v3/ticker/24hr (단일 symbol) 1 단일 종목 24시간 통계
GET /api/v3/ticker/24hr (전체) 80 모든 거래쌍 통계
GET /api/v3/depth limit=5/10/20/50/100 1 호가창 깊이
GET /api/v3/depth limit=500 5 호가창 깊이
GET /api/v3/depth limit=1000 10 호가창 깊이
GET /api/v3/depth limit=5000 50 호가창 깊이 (주의 요망)
GET /api/v3/klines 2 K라인 (봉 차트)
GET /api/v3/historicalTrades 5 과거 체결 내역
GET /api/v3/account 20 계정 잔고
GET /api/v3/openOrders (단일 symbol) 6 현재 미체결 주문
GET /api/v3/openOrders (전체) 80 모든 미체결 주문
GET /api/v3/allOrders 20 과거 주문 내역
POST /api/v3/order 1 주문 실행
DELETE /api/v3/order 1 주문 취소
DELETE /api/v3/openOrders 1 전체 주문 취소

성능 팁: 단일 종목의 ticker/24hr를 반복적으로 호출하지 마세요. 파라미터가 없는 일괄 인터페이스를 사용하면 가중치 소모를 300개 종목 × 1 = 300에서 80으로 줄일 수 있습니다.

4. 응답 헤더를 통한 적응형 제한

모든 REST 요청 응답은 현재 사용된 가중치를 반환하므로, 클라이언트는 이를 읽고 동적으로 조정해야 합니다.

import requests, time

BASE_URL = "https://api.binance.com"

class RateLimiter:
    def __init__(self, max_weight=6000, safety_ratio=0.8):
        self.max_weight = max_weight
        self.safety = safety_ratio  # 콜드 스타트 오차 방지를 위해 80%만 사용
        self.used_weight = 0

    def update_from_headers(self, headers: dict):
        used = headers.get("X-MBX-USED-WEIGHT-1m")
        if used:
            self.used_weight = int(used)

    def should_wait(self) -> float:
        """권장 대기 시간(초) 반환, 0은 즉시 호출 가능 의미"""
        threshold = self.max_weight * self.safety
        if self.used_weight >= threshold:
            # 다음 분 리셋까지 남은 시간 예상
            return 60 - (int(time.time()) % 60)
        return 0

limiter = RateLimiter()

def safe_get(path, params=None):
    wait = limiter.should_wait()
    if wait > 0:
        print(f"[제한] 사용된 가중치 {limiter.used_weight}, {wait}초간 일시 중지")
        time.sleep(wait)
    r = requests.get(f"{BASE_URL}{path}", params=params, timeout=10)
    limiter.update_from_headers(r.headers)
    return r.json()

# 사용 예시
data = safe_get("/api/v3/ticker/24hr")
print(f"현재 가중치 점유율: {limiter.used_weight}/6000")

5. 토큰 버킷 제한 (클라이언트 주도 제어)

응답 헤더를 수동적으로 확인하는 것보다 더 안정적인 방법은 클라이언트측 토큰 버킷을 사용하는 것입니다.

import time, threading

class TokenBucket:
    def __init__(self, capacity=6000, refill_per_sec=100):
        self.capacity = capacity
        self.tokens = capacity
        self.refill = refill_per_sec  # 6,000/60 = 100개/초
        self.last = time.time()
        self.lock = threading.Lock()

    def acquire(self, cost=1):
        with self.lock:
            now = time.time()
            elapsed = now - self.last
            self.tokens = min(self.capacity, self.tokens + elapsed * self.refill)
            self.last = now
            if self.tokens < cost:
                wait = (cost - self.tokens) / self.refill
                time.sleep(wait)
                self.tokens = 0
            else:
                self.tokens -= cost

bucket = TokenBucket(capacity=6000, refill_per_sec=100)

def call(path, weight):
    bucket.acquire(weight)
    return requests.get(f"{BASE_URL}{path}").json()

# 주문(가중치 1)과 잔고 조회(가중치 20) 시 병행 사용
call("/api/v3/ticker/price", 1)
call("/api/v3/account", 20)

6. 429 및 418 오류 시 올바른 처리 방법

1. 429 응답을 받았을 때

def request_with_retry(method, url, **kwargs):
    for attempt in range(3):
        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:
            print("IP 차단됨, 애플리케이션 중단 필요!")
            raise SystemExit(1)
        return r
    raise Exception("최대 재시도 횟수 초과")

Retry-After 응답 헤더는 **정확한 대기 시간(초)**을 제공하므로 해당 시간 동안 sleep을 수행하면 됩니다.

2. 418 응답을 받았을 때

418은 심각한 경고입니다. 429 수신 후에도 계속해서 요청을 보내면 IP가 2분부터 시작하여 심각한 경우 3일 동안 차단됩니다. 418을 수신하면 즉시 모든 요청을 중단하고, 최소 Retry-After 시간에 명시된 기간이 지난 후에 재개해야 합니다.

7. WebSocket 대안: 가중치 소모 거의 없음

REST 방식은 시세를 가져올 때마다 가중치를 소모하지만, WebSocket 구독은 연결 시에만 한 번 소모되며 실시간 푸시 데이터는 가중치를 차지하지 않습니다.

import json, websocket

def on_message(ws, message):
    data = json.loads(message)
    print(f"{data['s']} 최신가 {data['c']}, 24h 거래량 {data['v']}")

ws = websocket.WebSocketApp(
    "wss://stream.binance.com:9443/stream?streams=btcusdt@ticker/ethusdt@ticker",
    on_message=on_message
)
ws.run_forever()

비용 환산: 10개 거래쌍의 실시간 티커를 구독할 때, REST 폴링(1초에 한 번) = 600회/분 × 가중치 1 = 600 가중치 소모; WebSocket 구독 = 0 가중치 소모.

8. 가중치 최적화 실전 팁

  1. exchangeInfo 캐싱: 1시간마다 업데이트하는 것으로 충분하며, 그 이상의 빈도는 1회당 20 가중치를 낭비할 뿐입니다.
  2. 일괄 조회 우선: /ticker/24hr 호출 시 파라미터 없이 전체를 가져오는 것이 개별 조회보다 가중치를 90% 이상 절약합니다.
  3. 필요한만큼의 Depth만 요청: 지정가 주문 시에는 limit=20(가중치 1)으로 충분합니다. 5,000개 층을 모두 가져올 필요가 없습니다.
  4. 주문 상태 모니터링은 userDataStream 사용: 주기적인 GET /order 호출보다 효율적이며 가중치 소모가 0입니다.
  5. 일괄 취소 인터페이스 활용: DELETE /openOrders?symbol=BTCUSDT는 가중치 1로 개별 취소보다 훨씬 경제적입니다.
  6. 시간대별 제한 관리: UTC 0시, 8시, 16시는 정산 시간대이므로 가중치 소모가 더 민감할 수 있습니다. 전략적으로 피해서 실행하는 것이 좋습니다.

9. 자주 묻는 질문 (FAQ)

Q1: 가중치는 IP 기준인가요, 아니면 API 키 기준인가요?

A: 주로 IP 기준입니다. 동일 IP 아래의 여러 키는 가중치 버킷을 공유합니다. IP를 변경하면 IP 등급 가중치는 우회할 수 있지만 주문 횟수 제한은 계정 기준으로 적용됩니다. 공용 프록시로 IP를 수시로 바꾸는 행위는 바이낸스에 의해 감지될 수 있으므로 권장하지 않습니다.

Q2: 응답 헤더 X-MBX-USED-WEIGHT와 X-MBX-USED-WEIGHT-1m의 차이는 무엇인가요?

A: X-MBX-USED-WEIGHT-1m이 공식 권장 필드이며, 1분 윈도우임을 명시합니다. X-MBX-USED-WEIGHT는 과거 필드로 값은 동일합니다. -1m 접미사가 붙은 필드를 기준으로 삼으세요.

Q3: 418 차단을 당하면 얼마나 기다려야 하나요?

A: 첫 위반 시 2분이며, 반복되면 5/15/60분, 심각한 경우 3일까지 늘어납니다. Retry-After 헤더에 정확한 시간이 표시됩니다. 차단 해제 후 즉시 코드를 최적화하여 단기간 내 반복 위반으로 인한 가중 처벌을 피하세요.

Q4: 멀티 스레드 병렬 호출 시 가중치를 어떻게 통합 계산하나요?

A: 프로세스 수준의 전역 토큰 버킷(5절 참조)을 사용하거나, 다중 머신 배포 시에는 Redis 기반의 분산 토큰 버킷을 사용해야 합니다. 단일 머신 내에서는 threading.Lock으로 충분합니다.

Q5: 테스트넷(testnet.binance.vision)의 가중치 규칙도 동일한가요?

A: 테스트넷의 가중치는 더 완만하지만(보통 메인넷의 10배), 규칙 구조는 동일합니다. 테스트넷의 데이터로 메인넷 성과를 평가하지 마시고, 운영 전 반드시 메인넷에서 소규모 테스트를 거치시기 바랍니다.

제한 정책을 확인하셨다면, 카테고리로 돌아가 「API 연동」 분류에서 WebSocket과 서명 실전을 확인해 보세요.

계속 둘러보기

바이낸스 사용에 대한 추가 질문이 있으신가요? 카테고리 페이지로 돌아가 같은 주제의 다른 가이드를 찾아보세요.

카테고리

관련 가이드

바이낸스 API 신청 방법? 키 및 서명 생성 가이드 2026-04-14 바이낸스 현물(Spot) API 사용법: 첫 주문까지 가능한 실행 코드 가이드 2026-04-14 바이낸스 선물(Futures)과 현물(Spot) API의 차이점은? 엔드포인트와 가중치 비교 2026-04-14 바이낸스 WebSocket 시세 구독 방법: 단일/조합 스트림 실무 코드 2026-04-14