Le mécanisme central de limitation de débit de l'API Binance repose sur un seau de poids (Weight Bucket) : chaque adresse IP peut consommer un maximum de 6000 poids par minute (Spot). Chaque interface déduit entre 1 et 100 points de poids selon sa complexité. En cas de dépassement, l'API renvoie l'erreur 429 Too Many Requests. Des violations répétées peuvent entraîner une erreur 418 et un bannissement d'IP allant de 2 minutes à 3 jours. Cet article détaille le calcul du poids, la surveillance des en-têtes de réponse, la limitation côté client et les alternatives WebSocket, afin que votre stratégie fonctionne de manière stable sans déclencher le contrôle des risques. Les utilisateurs ne possédant pas encore de clé API doivent d'abord effectuer le KYC sur le site officiel de Binance ; ceux qui n'ont pas de compte peuvent s'inscrire via l' inscription gratuite.
I. Les trois dimensions de la limitation de débit
Binance applique trois types de restrictions indépendantes pour une même IP ou clé API :
| Dimension | Limite Spot | Limite Futures | Réponse en cas de violation |
|---|---|---|---|
| Poids des requêtes (REQUEST_WEIGHT) | 6000 / minute | 2400 / minute | 429 |
| Nombre d'ordres (ORDERS) | 100 / 10 sec ; 200 000 / jour | 300 / 10 sec ; 1200 / minute | 429 |
| Nombre de connexions (RAW_REQUESTS) | 61 000 / 5 minutes | 61 000 / 5 minutes | 429 |
| Bannissement d'IP | Violations continues | Violations continues | 418 |
Concept clé : Bien que le poids d'une interface de passage d'ordre soit de 1, elle consomme simultanément le seau ORDERS. Si l'un des deux seaux atteint sa limite, vous serez restreint.
II. Consulter votre quota de poids actuel
Obtenez les quotas en temps réel de votre compte via le champ rateLimits de GET /api/v3/exchangeInfo :
curl -s "https://api.binance.com/api/v3/exchangeInfo" | \
jq '.rateLimits'
Retour :
[
{"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}
]
Les comptes avec un niveau VIP élevé peuvent demander une augmentation des limites, mais la limite standard de 6000 suffit à 90 % des besoins des stratégies.
III. Tableau de comparaison des poids pour les interfaces courantes
| Interface | Poids | Description |
|---|---|---|
| GET /api/v3/ping | 1 | Test de connectivité |
| GET /api/v3/time | 1 | Heure du serveur |
| GET /api/v3/exchangeInfo | 20 | Règles de trading (mise en cache d'1h recommandée) |
| GET /api/v3/ticker/price (un seul symbole) | 1 | Prix d'une seule paire |
| GET /api/v3/ticker/price (tous) | 4 | Prix de toutes les paires en une fois |
| GET /api/v3/ticker/24hr (un seul symbole) | 1 | Stats 24h d'une seule paire |
| GET /api/v3/ticker/24hr (tous) | 80 | Stats de toutes les paires |
| GET /api/v3/depth limit=5/10/20/50/100 | 1 | Profondeur du carnet |
| GET /api/v3/depth limit=500 | 5 | Profondeur du carnet |
| GET /api/v3/depth limit=1000 | 10 | Profondeur du carnet |
| GET /api/v3/depth limit=5000 | 50 | Profondeur (à utiliser avec parcimonie) |
| GET /api/v3/klines | 2 | Chandeliers (K-lines) |
| GET /api/v3/historicalTrades | 5 | Historique des transactions |
| GET /api/v3/account | 20 | Solde du compte |
| GET /api/v3/openOrders (un seul symbole) | 6 | Ordres ouverts actuels |
| GET /api/v3/openOrders (tous) | 80 | Tous les ordres ouverts |
| GET /api/v3/allOrders | 20 | Historique des ordres |
| POST /api/v3/order | 1 | Passer un ordre |
| DELETE /api/v3/order | 1 | Annuler un ordre |
| DELETE /api/v3/openOrders | 1 | Tout annuler |
Piège de performance : N'appelez pas en boucle ticker/24hr pour chaque symbole individuellement. Utilisez l'interface globale sans paramètres ; le poids passera de 300 symboles × 1 = 300 à seulement 80.
IV. Lire les en-têtes de réponse pour une limitation adaptative
Chaque réponse REST renvoie le poids actuellement utilisé. Le client doit lire cette valeur et s'ajuster dynamiquement :
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 # Utilise seulement 80% pour prévenir les erreurs de démarrage à froid
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:
"""Renvoie le nombre de secondes à attendre, 0 si l'appel est possible"""
threshold = self.max_weight * self.safety
if self.used_weight >= threshold:
# Estimation des secondes avant la réinitialisation de la minute suivante
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"[Limitation] {limiter.used_weight} poids utilisé, pause de {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()
# Utilisation
data = safe_get("/api/v3/ticker/24hr")
print(f"Poids actuel consommé : {limiter.used_weight}/6000")
V. Limitation par seau à jetons (Contrôle actif côté client)
Une approche plus proactive que la simple lecture des en-têtes est le seau à jetons (Token Bucket) côté client :
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 / seconde
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()
# Utilisation combinée pour passer un ordre (poids 1) et consulter le solde (poids 20)
call("/api/v3/ticker/price", 1)
call("/api/v3/account", 20)
VI. Gestion correcte des erreurs 429 et 418
1. Recevoir une erreur 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"Limitation déclenchée, pause de {retry_after}s")
time.sleep(retry_after)
continue
if r.status_code == 418:
print("IP bannie, l'application doit s'arrêter !")
raise SystemExit(1)
return r
raise Exception("Nombre maximal de tentatives atteint")
L'en-tête Retry-After indique le nombre exact de secondes à attendre. Il suffit de mettre le programme en pause (sleep).
2. Recevoir une erreur 418
L'erreur 418 est un avertissement sérieux : continuer à envoyer des requêtes après un 429 entraînera un bannissement d'IP commençant à 2 minutes, et pouvant aller jusqu'à 3 jours dans les cas extrêmes. Dès réception, vous devez immédiatement arrêter toutes les requêtes et attendre au moins le temps indiqué par Retry-After.
VII. Alternative WebSocket : Consommation de poids quasi nulle
Alors que chaque extraction via REST consomme du poids, l'abonnement WebSocket n'en consomme qu'une seule fois lors de l'établissement de la connexion. Les données poussées en temps réel ne pèsent rien sur le quota :
import json, websocket
def on_message(ws, message):
data = json.loads(message)
print(f"{data['s']} Dernier prix {data['c']}, Volume 24h {data['v']}")
ws = websocket.WebSocketApp(
"wss://stream.binance.com:9443/stream?streams=btcusdt@ticker/ethusdt@ticker",
on_message=on_message
)
ws.run_forever()
Comparaison des coûts : S'abonner aux tickers en temps réel pour 10 paires : interrogation REST toutes les secondes = 600 fois/minute × poids 1 = 600 poids ; abonnement WebSocket = 0 poids.
VIII. Conseils pratiques pour l'optimisation du poids
- Cachez exchangeInfo : Une mise à jour toutes les heures suffit largement. Le faire plus souvent gaspille 20 poids à chaque fois.
- Priorité aux requêtes par lots :
/ticker/24hrsans paramètres pour tout récupérer économise 90 % de poids par rapport à des appels individuels. - Profondeur limitée au nécessaire : Pour des ordres limités, une limite de 20 suffit (poids 1). Ne chargez pas 5000 niveaux.
- Surveillance des ordres via userDataStream : Plus efficace et consomme zéro poids comparé à un
GET /orderpériodique. - Annulation en masse via l'interface dédiée :
DELETE /openOrders?symbol=BTCUSDTpèse 1, ce qui est plus économique que d'annuler les ordres un par un. - Limitation par plages horaires : Les fenêtres de règlement (0h, 8h, 16h UTC) sont plus chargées ; déalez vos appels pour éviter les pics.
IX. Questions Fréquentes FAQ
Q1 : Le poids est-il calculé par IP ou par clé API ?
R : Principalement par IP. Plusieurs clés sous une même IP partagent le même seau de poids. Changer d'IP peut contourner la limite par IP, mais les limites de passage d'ordre restent liées au compte. L'utilisation de proxies dynamiques pour contourner le poids est détectée par Binance et déconseillée.
Q2 : Quelle est la différence entre les en-têtes X-MBX-USED-WEIGHT et X-MBX-USED-WEIGHT-1m ?
R : X-MBX-USED-WEIGHT-1m est l'en-tête recommandé officiellement, indiquant la fenêtre d'une minute. X-MBX-USED-WEIGHT est un champ historique identique. Fiez-vous à celui avec le suffixe -1m.
Q3 : Combien de temps dure un bannissement 418 ?
R : 2 minutes pour une première violation, pouvant passer à 5, 15, 60 minutes, voire 3 jours. L'en-tête Retry-After donne le temps exact. Optimisez votre code immédiatement après le rétablissement pour éviter une récidive et une escalade des sanctions.
Q4 : Comment centraliser le calcul du poids pour des appels concurrents multi-threadés ?
R : Utilisez un seau à jetons global au niveau du processus (voir section V), ou un seau distribué via Redis pour des déploiements sur plusieurs machines. Un threading.Lock suffit pour une seule machine.
Q5 : Les règles de poids sont-elles identiques sur le testnet ?
R : Les limites sur le testnet (testnet.binance.vision) sont plus souples (souvent 10 fois supérieures au mainnet), mais la structure des règles est identique. Ne vous fiez pas à la consommation sur le testnet pour évaluer le mainnet ; validez toujours sur le mainnet avec un petit flux avant le déploiement complet.
Après avoir étudié les stratégies de limitation, retournez à la Navigation par catégorie pour consulter la catégorie « Intégration API » et les tutoriels pratiques sur WebSocket et les signatures.