Intégration API

L'API Binance peut-elle bloquer mon IP ? Explication détaillée de la stratégie de limitation de débit et du calcul du poids

Compréhension complète du mécanisme de poids et de limitation de débit de l'API Binance : allocation de 6000 poids par minute, liste des poids par interface, lecture des en-têtes de réponse, conditions de déclenchement 429/418, implémentation du seau à jetons et alternatives WebSocket avec code Python.

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

  1. Cachez exchangeInfo : Une mise à jour toutes les heures suffit largement. Le faire plus souvent gaspille 20 poids à chaque fois.
  2. Priorité aux requêtes par lots : /ticker/24hr sans paramètres pour tout récupérer économise 90 % de poids par rapport à des appels individuels.
  3. Profondeur limitée au nécessaire : Pour des ordres limités, une limite de 20 suffit (poids 1). Ne chargez pas 5000 niveaux.
  4. Surveillance des ordres via userDataStream : Plus efficace et consomme zéro poids comparé à un GET /order périodique.
  5. Annulation en masse via l'interface dédiée : DELETE /openOrders?symbol=BTCUSDT pèse 1, ce qui est plus économique que d'annuler les ordres un par un.
  6. 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.

Continuer la navigation

Vous avez encore des questions sur l'utilisation de Binance ? Retournez à la page de catégorie pour trouver d'autres tutoriels sur le même sujet.

Navigation par catégorie

Tutoriels connexes

Comment demander une API Binance ? Comment générer généralement les signatures de clés 2026-04-14 Comment utiliser l'API Binance Spot ? Code prêt à l'emploi de zéro à votre premier ordre 2026-04-14 Quelles sont les différences entre l'API Binance Futures et l'API Spot ? Comparaison des endpoints, paramètres et poids 2026-04-14 Comment s'abonner aux flux de marché via WebSocket Binance ? Code de niveau production 2026-04-14