幣安 API 限頻的核心機制是 權重桶(Weight Bucket):每個 IP 每分鐘最多消耗 6000 權重(Spot),每個介面按複雜度扣除 1-100 不等的權重分,超限返回 429 Too Many Requests,重複違規會升級為 418 並 IP 封禁 2 分鐘至 3 天。本文按「權重計算 → 響應頭監控 → 客戶端限頻 → WebSocket 替代」四步給出完整程式碼,讓你的策略穩定執行不觸發風控。尚未持有 API 金鑰的使用者請先在 幣安官網 完成 KYC;沒有賬號的可以 免費註冊。
一、限頻的三個維度
幣安對同一個 IP / API Key 施加三種獨立限制:
| 維度 | Spot 上限 | Futures 上限 | 違規響應 |
|---|---|---|---|
| 請求權重(REQUEST_WEIGHT) | 6000 / 分鐘 | 2400 / 分鐘 | 429 |
| 下單次數(ORDERS) | 100 / 10 秒;200000 / 天 | 300 / 10 秒;1200 / 分鐘 | 429 |
| 連線數(RAW_REQUESTS) | 61000 / 5 分鐘 | 61000 / 5 分鐘 | 429 |
| IP 封禁 | 連續違規 | 連續違規 | 418 |
關鍵認知:下單介面權重雖然只有 1,但會同時消耗 ORDERS 桶,兩個桶任一達到上限都會被限。
二、查詢當前權重配額
透過 GET /api/v3/exchangeInfo 的 rateLimits 欄位獲取當前賬戶的實時配額:
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 等級高的賬戶可以申請提升權重上限,但標準賬戶 6000 已能滿足 90% 策略需要。
三、常見介面的權重對照表
| 介面 | 權重 | 說明 |
|---|---|---|
| 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 | 單品種 24h 統計 |
| 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 | 全撤 |
效能陷阱:不要迴圈呼叫單 symbol 的 ticker/24hr,直接用不帶引數的一次性介面,權重從 300 個 symbol × 1 = 300 降到 80。
四、讀取響應頭做自適應限頻
每次 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:
# 距下一分鐘 reset 的秒數估算
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}s")
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")
五、令牌桶限頻(客戶端主動控制)
比被動看響應頭更穩妥的做法是 客戶端令牌桶:
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 # 6000/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)
六、遇到 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}s")
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 天。一旦收到必須立刻停止所有請求,等待至少 Retry-After 的時間後才能恢復。
七、WebSocket 替代:幾乎 0 權重消耗
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 個交易對的實時 ticker,REST 輪詢 1 秒一次 = 600 次/分鐘 × 權重 1 = 600 權重;WebSocket 訂閱 = 0 權重。
八、權重最佳化實戰建議
- 快取 exchangeInfo:1 小時更新一次足夠,頻率提高只是浪費 20 權重 / 次
- 批次查詢優先:
/ticker/24hr不帶引數一次拿全部比迴圈單個省 90% 權重 - 深度只取需要的 limit:限價單只需要 limit=20 即可(權重 1),不要拉 5000 層
- 訂單狀態監聽用 userDataStream:比定時
GET /order高效且零權重 - 撤全單用批次介面:
DELETE /openOrders?symbol=BTCUSDT權重 1,比逐個撤單更節省 - 分時段限頻:UTC 0 點、UTC 8 點、UTC 16 點是結算視窗,權重消耗更緊張,策略可以錯峰
九、常見問題 FAQ
Q1: 權重是按 IP 算還是按 API Key 算?
A: 按 IP 為主。同一 IP 下多個 Key 共享權重桶;切換 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: 用程序級全域性令牌桶(見本文第五節),或用 Redis 分散式令牌桶(多機部署時必需)。單機內的 threading.Lock 足夠。
Q5: 測試網 testnet.binance.vision 的權重規則一樣嗎?
A: 測試網權重更寬鬆(通常是主網的 10 倍),但規則結構一致。不要基於測試網的權重消耗評估主網表現,上線前務必在主網小流量驗證。
看完限頻策略,回到 分類導航 選擇「API接入」分類檢視 WebSocket 與簽名實戰。