Comment scraper l'API REST de Binance avec des proxies : vue d'ensemble
Si vous construisez des jeux de données de prix ou d'order book pour du backtesting quant, vous avez probablement besoin de récupérer les données de marché publiques de Binance. Mais comment scraper l'API REST de Binance avec des proxies sans se faire bannir ? La réponse tient en trois mots : endpoints, poids, rotation. Binance impose un système de rate limiting basé sur un budget de poids par adresse IP, et un seul endpoint mal choisi peut épuiser votre quota en quelques minutes.
Ce guide s'adresse aux développeurs quant et ingénieurs de données crypto. Nous couvrons les endpoints clés (/api/v3/klines, /api/v3/depth, /api/v3/ticker/24hr, /api/v3/ticker/price), le système de poids de Binance, la rotation d'IP via ProxyHat, et des exemples de code prêts à l'emploi en Python et Node.js.
Conformité : ce guide couvre uniquement les données de marché publiques exposées par l'API REST publique de Binance. Respectez les poids documentés, lisez le Terms of Service de Binance, et utilisez l'accès officiel si les conditions l'exigent. Ne scrapez pas de données authentifiées ou privées.
Endpoints de marché public et système de poids
L'API REST publique de Binance (domaine api.binance.com) expose des endpoints de marché sans authentification. Chaque endpoint consomme un certain « poids » (weight) dans votre budget de rate limit. Le tableau ci-dessous récapitule les endpoints les plus utiles pour la collecte de données.
| Endpoint | Description | Poids par requête | Paramètres clés |
|---|---|---|---|
/api/v3/klines | Bougies OHLCV (chandeliers) | 1–2 (selon limit) | symbol, interval, limit (max 1000) |
/api/v3/depth | Order book (carnet d'ordres) | 5–20 (selon limit) | symbol, limit (5, 10, 20, 50, 100, 500, 1000) |
/api/v3/ticker/24hr | Statistiques 24h par symbole | 1–40 (selon paramètres) | symbol (optionnel) |
/api/v3/ticker/price | Prix spot en temps réel | 1–2 | symbol (optionnel) |
Binance renvoie l'en-tête de réponse X-MBX-USED-WEIGHT-1M qui indique le poids total consommé par votre IP dans la fenêtre courante d'une minute. C'est la métrique la plus importante à surveiller en production. Vous trouverez la documentation officielle des poids sur developers.binance.com.
Le piège de l'order book profond
Un appel /api/v3/depth?limit=1000 coûte 20 poids. Le budget par IP est d'environ 6000 poids par minute. À 20 poids par requête, vous pouvez faire 300 appels/minute/IP — soit un toutes les 200 ms. Si vous sondez /api/v3/depth en continu sans rotation d'IP, vous épuisez le budget en moins de 5 minutes, puis recevez des HTTP 429 (rate limit) puis potentiellement un HTTP 418 (IP bannie temporairement).
Rate limiting Binance : 429, 418 et Retry-After
Binance utilise un système de limite de débit basé sur le poids, pas sur le nombre brut de requêtes. Le budget par défaut est de 6000 poids par minute et par IP pour les endpoints de marché. Quand vous dépassez ce budget :
- HTTP 429 (Too Many Requests) — vous avez dépassé la limite. L'en-tête
Retry-Afterindique combien de secondes attendre. - HTTP 418 (I'm a teapot) — Binance a détecté un comportement abusif répété et bannit votre IP temporairement (souvent 2 minutes à plusieurs heures).
- HTTP 451 (Unavailable for Legal Reasons) — l'IP est dans une juridiction bloquée (ex. États-Unis vers Binance.com).
La stratégie de mitigation comporte trois axes : (1) surveiller X-MBX-USED-WEIGHT-1M, (2) implémenter un backoff exponentiel sur 429, (3) répartir le poids sur plusieurs IP via des proxies résidentiels rotatifs.
Avec 10 IP résidentielles rotatives et 6000 poids/IP/minute, votre budget total passe à 60 000 poids/minute — suffisant pour sonder l'order book BTCUSDT à limit=1000 toutes les 20 ms sans interruption.
Rotation d'IP avec ProxyHat : répartition du poids et contournement géo
Le web scraping à grande échelle exige une rotation d'IP fiable. ProxyHat fournit des proxies résidentiels, mobiles et datacenter accessibles via le gateway gate.proxyhat.com (HTTP port 8080, SOCKS5 port 1080). La rotation et le ciblage géographique se contrôlent directement dans le nom d'utilisateur.
Format de connexion
# HTTP — rotation automatique par requête
http://USERNAME:PASSWORD@gate.proxyhat.com:8080
# HTTP — ciblage pays (ex. États-Unis pour Binance.US)
http://user-country-US:PASSWORD@gate.proxyhat.com:8080
# HTTP — session collante (même IP pour pagination/backfill)
http://user-session-abc123:PASSWORD@gate.proxyhat.com:8080
# SOCKS5
socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080
Binance.com vs Binance.US et le HTTP 451
Binance.com restreint l'accès depuis certaines juridictions (notamment les États-Unis) avec un HTTP 451. Si vous devez accéder à Binance.US depuis une IP non-US, ou à l'inverse accéder à Binance.com depuis une IP US, le ciblage géographique -country-US ou -country-DE dans le nom d'utilisateur ProxyHat résout le problème. Consultez la liste complète des localisations disponibles.
Implémentation Python : rotation par requête avec requests
Voici un exemple complet en Python utilisant requests avec rotation d'IP ProxyHat, throttling basé sur le poids, et retries avec backoff exponentiel.
import requests
import time
import logging
from urllib.parse import urlencode
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
logger = logging.getLogger("binance_scraper")
PROXYHAT_GATEWAY = "gate.proxyhat.com"
PROXYHAT_PORT = 8080
PROXYHAT_USER = "USERNAME"
PROXYHAT_PASS = "PASSWORD"
BASE_URL = "https://api.binance.com"
WEIGHT_BUDGET_PER_IP = 6000 # poids par minute et par IP
# Dictionnaire des poids par endpoint/paramètres
ENDPOINT_WEIGHTS = {
"/api/v3/klines": 2, # limit <= 100
"/api/v3/depth_100": 5,
"/api/v3/depth_1000": 20,
"/api/v3/ticker/price": 1,
"/api/v3/ticker/24hr_single": 1,
}
class BinanceScraper:
def __init__(self, max_retries=5):
self.session = requests.Session()
self.max_retries = max_retries
self.used_weight = 0
def _get_proxy_url(self, country=None, session_id=None):
"""Construit l'URL du proxy ProxyHat avec rotation ou session collante."""
user = PROXYHAT_USER
if country:
user += f"-country-{country}"
if session_id:
user += f"-session-{session_id}"
return f"http://{user}:{PROXYHAT_PASS}@{PROXYHAT_GATEWAY}:{PROXYHAT_PORT}"
def _fetch(self, path, params=None, country=None, session_id=None):
url = f"{BASE_URL}{path}"
proxy_url = self._get_proxy_url(country=country, session_id=session_id)
proxies = {"http": proxy_url, "https": proxy_url}
for attempt in range(self.max_retries):
try:
resp = self.session.get(url, params=params, proxies=proxies, timeout=15)
# Surveiller le poids consommé
weight_header = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight_header:
self.used_weight = int(weight_header)
logger.info(f"Used weight (1M): {self.used_weight} | status={resp.status_code}")
if resp.status_code == 200:
return resp.json()
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 5))
logger.warning(f"429 rate limited — waiting {retry_after}s")
time.sleep(retry_after)
continue
if resp.status_code == 418:
retry_after = int(resp.headers.get("Retry-After", 120))
logger.error(f"418 IP banned — waiting {retry_after}s")
time.sleep(retry_after)
continue
if resp.status_code == 451:
logger.error("451 geo-blocked — switching country")
# Forcer un autre pays au prochain appel
return None
resp.raise_for_status()
except requests.RequestException as e:
wait = 2 ** attempt
logger.warning(f"Attempt {attempt+1} failed: {e} — retry in {wait}s")
time.sleep(wait)
raise RuntimeError(f"Max retries exceeded for {path}")
def get_klines(self, symbol, interval="1m", limit=100, session_id=None):
params = {"symbol": symbol, "interval": interval, "limit": limit}
return self._fetch("/api/v3/klines", params=params, session_id=session_id)
def get_depth(self, symbol, limit=100):
params = {"symbol": symbol, "limit": limit}
path = "/api/v3/depth"
weight = ENDPOINT_WEIGHTS.get(f"/api/v3/depth_{limit}", 5)
# Throttle : si on approche du budget, attendre
if self.used_weight + weight > WEIGHT_BUDGET_PER_IP * 0.8:
logger.info("Approaching weight budget — pausing 10s")
time.sleep(10)
return self._fetch(path, params=params)
def get_ticker_price(self, symbol=None):
params = {"symbol": symbol} if symbol else {}
return self._fetch("/api/v3/ticker/price", params=params)
# Utilisation
scraper = BinanceScraper(max_retries=5)
# Rotation par requête — chaque appel utilise une nouvelle IP
klines = scraper.get_klines("BTCUSDT", interval="1m", limit=100)
print(f"Récupéré {len(klines)} bougies BTCUSDT")
# Order book profond
depth = scraper.get_depth("BTCUSDT", limit=1000)
print(f"Bids: {len(depth.get('bids', []))} | Asks: {len(depth.get('asks', []))}")
Session collative pour backfill de klines (httpx async)
Pour récupérer des données historiques via pagination (backfill), vous devez garder la même IP pendant toute la séquence afin d'éviter des incohérences de poids et de latence. Utilisez le flag -session-abc123 dans le nom d'utilisateur ProxyHat.
import httpx
import asyncio
import logging
from datetime import datetime, timedelta
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("klines_backfill")
PROXYHAT_USER = "USERNAME"
PROXYHAT_PASS = "PASSWORD"
GATEWAY = "gate.proxyhat.com"
PORT = 8080
async def backfill_klines(symbol, interval, start_ms, end_ms, batch_size=1000):
"""Récupère toutes les bougies entre start_ms et end_ms avec session collante."""
session_id = f"backfill-{symbol}-{int(start_ms/1000)}"
proxy_url = (
f"http://{PROXYHAT_USER}-session-{session_id}"
f":{PROXYHAT_PASS}@{GATEWAY}:{PORT}"
)
all_klines = []
current_start = start_ms
async with httpx.AsyncClient(
proxy=proxy_url,
timeout=httpx.Timeout(30.0),
limits=httpx.Limits(max_connections=5, max_keepalive_connections=3),
) as client:
retry_count = 0
while current_start < end_ms:
params = {
"symbol": symbol,
"interval": interval,
"startTime": current_start,
"endTime": end_ms,
"limit": batch_size,
}
try:
resp = await client.get(
"https://api.binance.com/api/v3/klines",
params=params,
)
weight = resp.headers.get("X-MBX-USED-WEIGHT-1M", "?")
logger.info(f"Status {resp.status_code} | Weight: {weight} | Batch from {current_start}")
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 5))
logger.warning(f"429 — sleeping {retry_after}s")
await asyncio.sleep(retry_after)
continue
if resp.status_code == 418:
retry_after = int(resp.headers.get("Retry-After", 120))
logger.error(f"418 ban — sleeping {retry_after}s")
await asyncio.sleep(retry_after)
continue
resp.raise_for_status()
data = resp.json()
if not data:
break
all_klines.extend(data)
current_start = data[-1][0] + 1 # open time du dernier + 1ms
retry_count = 0
# Throttle doux : 10 req/s max
await asyncio.sleep(0.1)
except httpx.HTTPStatusError as e:
retry_count += 1
if retry_count > 5:
raise
wait = 2 ** retry_count
logger.warning(f"HTTP error — retry in {wait}s: {e}")
await asyncio.sleep(wait)
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise
return all_klines
# Exemple : récupérer 7 jours de bougies 1m pour ETHUSDT
now_ms = int(datetime.utcnow().timestamp() * 1000)
week_ago_ms = now_ms - int(timedelta(days=7).total_seconds() * 1000)
klines = asyncio.run(backfill_klines("ETHUSDT", "1m", week_ago_ms, now_ms))
print(f"Total bougies récupérées : {len(klines)}")
ProxyHat SDK vs proxy brut : comparaison côte à côte
ProxyHat expose un SDK Python qui simplifie la rotation, les retries et la gestion des sessions. Voici le même appel /api/v3/ticker/price réalisé avec un proxy brut puis avec le SDK.
# --- Méthode 1 : proxy brut avec requests ---
import requests
proxy_url = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
proxies = {"http": proxy_url, "https": proxy_url}
resp = requests.get(
"https://api.binance.com/api/v3/ticker/price",
params={"symbol": "BTCUSDT"},
proxies=proxies,
timeout=10,
)
print(resp.json())
# --- Méthode 2 : ProxyHat SDK ---
from proxyhat import ProxyHatClient
client = ProxyHatClient(
username="USERNAME",
password="PASSWORD",
gateway="gate.proxyhat.com",
port=8080,
)
# Rotation automatique par requête
resp = client.get(
"https://api.binance.com/api/v3/ticker/price",
params={"symbol": "BTCUSDT"},
country="US", # optionnel : ciblage géo
timeout=10,
)
print(resp.json())
# Session collante pour pagination
resp2 = client.get(
"https://api.binance.com/api/v3/klines",
params={"symbol": "ETHUSDT", "interval": "1m", "limit": 100},
session_id="backfill-eth-001",
)
print(f"Bougies : {len(resp2.json())}")
Node.js (axios) : scraping concurrent avec rotation
Pour les équipes JavaScript/TypeScript, voici un exemple avec axios et un pool de proxies rotatifs ProxyHat.
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent').HttpsProxyAgent;
const PROXYHAT_USER = 'USERNAME';
const PROXYHAT_PASS = 'PASSWORD';
const GATEWAY = 'gate.proxyhat.com';
const PORT = 8080;
const BASE_URL = 'https://api.binance.com';
const MAX_CONCURRENT = 10;
const WEIGHT_BUDGET = 6000;
let usedWeight = 0;
function buildProxy(country = null, sessionId = null) {
let user = PROXYHAT_USER;
if (country) user += `-country-${country}`;
if (sessionId) user += `-session-${sessionId}`;
return `http://${user}:${PROXYHAT_PASS}@${GATEWAY}:${PORT}`;
}
async function fetchBinance(path, params = {}, options = {}) {
const proxyUrl = buildProxy(options.country, options.sessionId);
const agent = new HttpsProxyAgent(proxyUrl);
for (let attempt = 0; attempt < 5; attempt++) {
try {
const resp = await axios.get(`${BASE_URL}${path}`, {
params,
httpsAgent: agent,
timeout: 15000,
});
const weight = parseInt(resp.headers['x-mbx-used-weight-1m'] || '0', 10);
usedWeight = weight;
console.log(`Status ${resp.status} | Weight: ${usedWeight} | ${path}`);
// Throttle : si on approche du budget, attendre
if (usedWeight > WEIGHT_BUDGET * 0.8) {
console.log('Approaching weight budget — pausing 10s');
await new Promise(r => setTimeout(r, 10000));
}
return resp.data;
} catch (err) {
if (err.response) {
const status = err.response.status;
const retryAfter = parseInt(err.response.headers['retry-after'] || '5', 10);
if (status === 429) {
console.warn(`429 rate limited — waiting ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
if (status === 418) {
console.error(`418 banned — waiting ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
if (status === 451) {
console.error('451 geo-blocked — try different country');
throw err;
}
}
const wait = Math.pow(2, attempt);
console.warn(`Attempt ${attempt + 1} failed: ${err.message} — retry in ${wait}s`);
await new Promise(r => setTimeout(r, wait * 1000));
}
}
throw new Error(`Max retries exceeded for ${path}`);
}
// Scraping concurrent de plusieurs symboles
async function scrapeMultipleSymbols() {
const symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT'];
const semaphore = { count: 0 };
const tasks = symbols.map(async (symbol) => {
while (semaphore.count >= MAX_CONCURRENT) {
await new Promise(r => setTimeout(r, 50));
}
semaphore.count++;
try {
const data = await fetchBinance('/api/v3/ticker/price', { symbol });
console.log(`${symbol}: $${data.price}`);
return data;
} finally {
semaphore.count--;
}
});
return Promise.all(tasks);
}
scrapeMultipleSymbols().catch(console.error);
curl : test rapide d'un endpoint via ProxyHat
Pour valider votre configuration proxy avant de lancer un script complet, un simple curl suffit.
# Vérifier le prix BTCUSDT via un proxy résidentiel US
curl -x "http://user-country-US:PASSWORD@gate.proxyhat.com:8080" \
"https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT" \
-D -
# L'option -D - affiche les en-têtes, dont X-MBX-USED-WEIGHT-1M
# Récupérer l'order book avec limit=100
curl -x "http://USERNAME:PASSWORD@gate.proxyhat.com:8080" \
"https://api.binance.com/api/v3/depth?symbol=BTCUSDT&limit=100" \
-H "Accept: application/json"
# Via SOCKS5
curl -x "socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080" \
"https://api.binance.com/api/v3/klines?symbol=ETHUSDT&interval=1m&limit=10"
Quand préférer les WebSockets au REST polling
Binance propose des flux WebSocket publics (wss://stream.binance.com:9443/ws) pour les bougies (kline), l'order book (depth), et les trades en temps réel. Pour des cas d'usage en temps réel, les WebSockets sont nettement supérieurs au polling REST :
| Critère | REST polling | WebSocket streams |
|---|---|---|
| Latence | 200–500 ms par requête | < 100 ms (push) |
| Coût en poids | Consomme le budget 6000/min | 0 poids (hors limite REST) |
| Backfill historique | Idéal (pagination startTime/endTime) | Impossible (temps réel uniquement) |
| Connexions simultanées | Limitées par le budget poids | Max 5 connexions par IP, 1024 streams |
| Fiabilité réseau | Requêtes indépendantes | Reconnexion nécessaire si coupure |
Règle pratique : utilisez le REST pour le backfill historique et les snapshots d'order book, et les WebSockets pour le suivi en temps réel. Pour le suivi de données en continu, combinez les deux : un snapshot REST initial puis un flux WebSocket pour les mises à jour incrémentales.
Erreurs courantes et edge cases
- Ignorer X-MBX-USED-WEIGHT-1M — c'est la seule façon de savoir où vous en êtes dans le budget. Loggez-le systématiquement.
- Poller /api/v3/depth avec limit=1000 en continu — 20 poids par appel = budget épuisé en 300 appels. Utilisez limit=100 (5 poids) ou passez aux WebSockets.
- Pas de rotation d'IP — un seul proxy datacenter en IP fixe sera banni en minutes si le volume est élevé.
- Oublier le Retry-After sur 429 — continuer à envoyer des requêtes pendant un 429 accélère le passage au 418.
- Mélanger Binance.com et Binance.US sans géo-ciblage — un proxy EU sur Binance.US renverra 451 ; un proxy US sur Binance.com aussi.
- Concurrency non contrôlée — lancer 50 requêtes parallèles sans semaphore peut saturer le proxy et déclencher des timeouts côté Binance.
Configuration ProxyHat et liens utiles
Pour configurer vos proxies ProxyHat, connectez-vous au dashboard et récupérez vos identifiants. Consultez la tarification pour choisir le plan adapté à votre volume, et la documentation officielle pour les détails techniques du SDK.
- Tarification ProxyHat — plans résidentiels, datacenter et mobile
- Cas d'usage : web scraping
- Cas d'usage : SERP tracking
- Localisations disponibles — 190+ pays pour le géo-ciblage
Points clés à retenir
- Surveillez X-MBX-USED-WEIGHT-1M — c'est votre jauge de consommation. Budget : ~6000 poids/min/IP.
- Rottez vos IP via
gate.proxyhat.com:8080(HTTP) ou:1080(SOCKS5) pour répartir le poids.- Utilisez des sessions collantes (
-session-abc123) pour le backfill paginé de klines.- Gérez 429 et 418 avec Retry-After et backoff exponentiel — ne jamais ignorer ces codes.
- Préférez les WebSockets pour le temps réel ; le REST reste roi pour le backfill historique.
- Respectez les ToS de Binance — données publiques uniquement, ne scrapez pas de endpoints authentifiés.
FAQ
Qu'est-ce que scraper l'API REST de Binance avec des proxies ?
C'est la collecte de données de marché publiques (prix, bougies, order book) via les endpoints REST de Binance en utilisant des proxies rotatifs pour répartir la charge d'IP et éviter le rate limiting basé sur le poids. Chaque IP dispose d'un budget de ~6000 poids par minute ; la rotation permet d'augmenter ce budget global proportionnellement au nombre d'IP.
Pourquoi utiliser des proxies pour scraper l'API Binance ?
Binance limite les requêtes par adresse IP avec un système de poids. Sans rotation, un polling intensif (ex. order book à limit=1000) épuise le budget en quelques minutes, déclenchant des HTTP 429 puis des bannissements temporaires (HTTP 418). Les proxies résidentiels rotatifs répartissent le poids sur des dizaines d'IP, multipliant le budget effectif et contournant les blocages géographiques (HTTP 451).
Quel type de proxy fonctionne le mieux pour l'API Binance ?
Les proxies résidentiels rotatifs sont recommandés car ils offrent des IP d'apparence humaine, un faible taux de détection, et un grand pool d'adresses. Les proxies datacenter sont plus rapides mais plus facilement identifiés. Pour le backfill paginé, utilisez des sessions collantes (même IP pendant toute la séquence). Pour le scraping haute fréquence, la rotation par requête maximise le budget de poids.
Comment éviter les blocages quand on scrape l'API Binance ?
Surveillez l'en-tête X-MBX-USED-WEIGHT-1M, implémentez un backoff exponentiel sur HTTP 429, respectez l'en-tête Retry-After, rottez vos IP via ProxyHat, et limitez la profondeur de l'order book (préférez limit=100 à limit=1000). Pour le temps réel, basculez sur les WebSockets qui ne consomment pas de poids REST. Ne dépassez jamais 80% du budget avant de marquer une pause.
Faut-il utiliser Binance.com ou Binance.US avec des proxies ?
Cela dépend de votre juridiction et de vos besoins. Binance.com bloque les IP américaines (HTTP 451), tandis que Binance.US est destiné au marché US. Avec ProxyHat, utilisez le flag -country-US pour cibler des IP américaines vers Binance.US, ou un autre pays pour Binance.com. Vérifiez toujours les Terms of Service de chaque plateforme.






