Come fare scraping della Binance REST API con proxy: panoramica rapida
Raccogliere dati di mercato pubblici da Binance è apparentemente semplice — gli endpoint REST sono pubblici, ben documentati e non richiedono chiavi API per i dati di prezzo e order book. Il problema nasce quando si tenta di farlo su larga scala: Binance applica un rate limit basato su peso per IP, e un polling aggressivo di /api/v3/depth può esaurire il budget di circa 6.000 pesi al minuto in pochi cicli. La soluzione è distribuire il peso su più IP usando proxy residenziali rotanti come quelli di ProxyHat, che permette di superare sia i limiti di frequenza sia le restrizioni geografiche (HTTP 451) tra Binance.com e Binance.US.
Nota su conformità e ToS: questa guida riguarda esclusivamente dati di mercato pubblici (candlestick, order book top-of-book, ticker 24h, prezzo). Rispetta sempre i pesi documentati, leggi i Termini di servizio di Binance, e preferisci l'accesso API ufficiale quando i termini lo richiedono. Non usare proxy per aggirare restrizioni legali o sanzioni.
Endpoint pubblici chiave e peso per richiesta
Binance pubblica un set di endpoint REST pubblici su api.binance.com (e mirror regionali). Ogni endpoint ha un peso (weight) che conta contro il budget orario e per-minuto dell'IP chiamante. La tabella seguente mappa i quattro endpoint più rilevanti per dataset di prezzo/orderbook.
| Endpoint | Scopo | Peso tipico | Note |
|---|---|---|---|
GET /api/v3/klines | Candlestick OHLCV | 1–10 (dipende da limit) | max 1000 candele per richiesta; peso cresce con limit |
GET /api/v3/depth | Snapshot order book | 5–20 (dipende da limit) | limit=1000 → peso 20; limit=5000 → peso 50 (documentato) |
GET /api/v3/ticker/24hr | Statistiche 24h per simbolo | 1 (singolo) – 80 (tutti i simboli) | Chiamata senza simbolo restituisce tutti i ticker: peso alto |
GET /api/v3/ticker/price | Prezzo spot corrente | 1–4 | Leggerissimo; ideale per heartbeat |
Ogni risposta REST include l'header X-MBX-USED-WEIGHT-1M, che indica il peso consumato nell'ultimo minuto per quell'IP. Monitorare questo header è obbligatorio per evitare ban: la documentazione ufficiale di Binance Spot API descrive il sistema di peso in dettaglio.
Perché il rate limit basato su peso è insidioso
Il budget predefinito per IP è di 6.000 pesi al minuto (può variare; verifica l'header X-MBX-USED-WEIGHT-1M). Se fai polling di /api/v3/depth con limit=1000 (peso 20) ogni secondo, consumi 1.200 pesi al minuto — apparentemente sotto soglia. Ma se aggiungi klines multi-simbolo e ticker 24h per decine di pair, il peso si somma rapidamente e il budget si esaurisce in pochi minuti.
Quando superi il limite, Binance risponde con HTTP 429 Too Many Requests e l'header Retry-After (secondi di attesa). Se continui a forzare, l'IP viene bannato temporaneamente con HTTP 418 (un endpoint "ban" con corpo JSON {"code": -1003, "msg": ...}). I ban possono durare da 2 minuti a 3 giorni a seconda della gravità. Per un data pipeline in produzione, un ban significa gap nei dati e possibili perdite finanziarie per strategie quant.
La geo-restrizione aggiunge un ulteriore livello: da alcuni IP (tipicamente Stati Uniti), api.binance.com può rispondere con HTTP 451 Unavailable For Legal Reasons, reindirizzando verso Binance.US (endpoint diversi, liquidità diversa). I proxy residenziali geolocalizzati risolvono entrambi i problemi: distribuiscono il peso su molti IP e permettono di scegliere il Paese di uscita.
Proxy residenziali ProxyHat: configurazione di base
ProxyHat espone un gateway unico (gate.proxyhat.com) su porta 8080 per HTTP e 1080 per SOCKS5. La rotazione e la geolocalizzazione si controllano tramite il username, con flag separati da trattino:
user-country-US:pass— uscita da Stati Uniti (evita HTTP 451 su Binance.com)user-country-DE-city-berlin:pass— uscita da Berlinouser-session-abc123:pass— sessione sticky (stesso IP per più richieste)user-country-DE-session-abc123:pass— combinazione geo + sticky
Per backfill paginati di klines (dove serve coerenza e continuità di sessione), usa sessioni sticky; per polling distribuito di depth/ticker, ruota per-request senza sessione.
Esempio 1: Python requests con proxy raw e rotazione per-request
Questo snippet mostra l'uso diretto del proxy HTTP ProxyHat con rotazione casuale della sessione (nuovo IP per ogni richiesta). Include lettura dell'header X-MBX-USED-WEIGHT-1M, retry con backoff esponenziale e gestione di 429/418.
import requests
import time
import random
import logging
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
PROXYHAT_USER = "user"
PROXYHAT_PASS = "pass"
GATE = "gate.proxyhat.com:8080"
BASE = "https://api.binance.com"
def build_proxy_url(country=None):
# Rotazione per-request: sessione casuale = nuovo IP ogni volta
session_id = f"sess-{random.randint(0, 10_000_000)}"
user = f"{PROXYHAT_USER}-session-{session_id}"
if country:
user = f"{PROXYHAT_USER}-country-{country}-session-{session_id}"
return f"http://{user}:{PROXYHAT_PASS}@{GATE}"
def fetch_klines(symbol="BTCUSDT", interval="1m", limit=500):
url = f"{BASE}/api/v3/klines"
params = {"symbol": symbol, "interval": interval, "limit": limit}
proxies = {"http": build_proxy_url("DE"), "https": build_proxy_url("DE")}
for attempt in range(5):
try:
r = requests.get(url, params=params, proxies=proxies, timeout=10)
used = r.headers.get("X-MBX-USED-WEIGHT-1M", "?")
logging.info("%s weight_used=%s status=%s", symbol, used, r.status_code)
if r.status_code == 429:
retry_after = int(r.headers.get("Retry-After", 5))
logging.warning("429 received, sleeping %ss", retry_after)
time.sleep(retry_after)
continue
if r.status_code == 418:
logging.error("418 ban — IP bloccato, pausa lunga")
time.sleep(60)
continue
r.raise_for_status()
return r.json()
except requests.RequestException as e:
backoff = 2 ** attempt
logging.warning("errore %s, retry in %ss", e, backoff)
time.sleep(backoff)
return None
if __name__ == "__main__":
data = fetch_klines()
print(f"candele ricevute: {len(data) if data else 0}")
Esempio 2: Python httpx asincrono con ProxyHat SDK e throttling basato su peso
Per throughput più alto, httpx con async è preferibile. Qui usiamo il pattern SDK ProxyHat (costruzione dell'URL proxy) con un semaforo per limitare la concorrenza e un throttle che rispetta il peso cumulativo.
import asyncio
import httpx
import random
import logging
import time
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
PROXYHAT_USER = "user"
PROXYHAT_PASS = "pass"
GATE_HTTP = "gate.proxyhat.com:8080"
BASE = "https://api.binance.com"
MAX_CONCURRENCY = 10
WEIGHT_BUDGET_PER_MIN = 5500 # margine sotto 6000
class WeightThrottle:
def __init__(self, budget_per_min):
self.budget = budget_per_min
self.consumed = 0
self.window_start = time.monotonic()
self.lock = asyncio.Lock()
async def acquire(self, weight=1):
async with self.lock:
now = time.monotonic()
if now - self.window_start >= 60:
self.consumed = 0
self.window_start = now
if self.consumed + weight > self.budget:
sleep_for = 60 - (now - self.window_start)
logging.warning("peso %s/%s, attendo %.1fs", self.consumed, self.budget, sleep_for)
await asyncio.sleep(max(sleep_for, 0.5))
self.consumed = 0
self.window_start = time.monotonic()
self.consumed += weight
def proxy_url(country="DE", session=None):
sess = session or f"sess-{random.randint(0, 10_000_000)}"
user = f"{PROXYHAT_USER}-country-{country}-session-{sess}"
return f"http://{user}:{PROXYHAT_PASS}@{GATE_HTTP}"
async def fetch_depth(client, symbol, throttle, limit=100):
weight = 1 if limit <= 100 else 5 # approssimazione; vedi docs
await throttle.acquire(weight)
url = f"{BASE}/api/v3/depth"
params = {"symbol": symbol, "limit": limit}
proxy = proxy_url()
for attempt in range(4):
try:
r = await client.get(url, params=params, proxy=proxy, timeout=10)
used = r.headers.get("X-MBX-USED-WEIGHT-1M", "?")
logging.info("depth %s weight=%s status=%s", symbol, used, r.status_code)
if r.status_code == 429:
await asyncio.sleep(int(r.headers.get("Retry-After", 5)))
continue
r.raise_for_status()
return r.json()
except httpx.HTTPError as e:
await asyncio.sleep(2 ** attempt)
return None
async def main():
throttle = WeightThrottle(WEIGHT_BUDGET_PER_MIN)
sem = asyncio.Semaphore(MAX_CONCURRENCY)
async with httpx.AsyncClient() as client:
symbols = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT", "XRPUSDT"]
async def worker(sym):
async with sem:
return await fetch_depth(client, sym, throttle)
results = await asyncio.gather(*[worker(s) for s in symbols])
for sym, res in zip(symbols, results):
print(f"{sym}: bids={len(res['bids']) if res else 0}")
if __name__ == "__main__":
asyncio.run(main())
Esempio 3: Sessione sticky per backfill paginato di klines
Per ricostruire serie storiche lunghe, serve paginare con endTime e mantenere lo stesso IP (sticky) per evitare salti di peso tra IP diversi. Il flag -session-abc123 garantisce lo stesso IP per la durata della sessione.
import requests
import time
PROXYHAT_USER = "user"
PROXYHAT_PASS = "pass"
GATE = "gate.proxyhat.com:8080"
BASE = "https://api.binance.com"
# Sessione sticky: stesso IP per tutto il backfill
STICKY_SESSION = "klines-btc-backfill-001"
PROXY = {
"http": f"http://{PROXYHAT_USER}-country-DE-session-{STICKY_SESSION}:{PROXYHAT_PASS}@{GATE}",
"https": f"http://{PROXYHAT_USER}-country-DE-session-{STICKY_SESSION}:{PROXYHAT_PASS}@{GATE}",
}
def backfill_klines(symbol="BTCUSDT", interval="1m", start_ms=None, end_ms=None, max_candles=100_000):
url = f"{BASE}/api/v3/klines"
collected = []
cursor = start_ms
total = 0
while total < max_candles:
params = {"symbol": symbol, "interval": interval, "startTime": cursor, "limit": 1000}
if end_ms:
params["endTime"] = end_ms
r = requests.get(url, params=params, proxies=PROXY, timeout=10)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 5)))
continue
if r.status_code == 418:
print("Ban ricevuto, attesa 120s")
time.sleep(120)
continue
r.raise_for_status()
batch = r.json()
if not batch:
break
collected.extend(batch)
total += len(batch)
cursor = batch[-1][6] + 1 # close time + 1ms
print(f"raccolte {len(batch)} candele, totale {total}, peso {r.headers.get('X-MBX-USED-WEIGHT-1M')}")
time.sleep(0.2) # rispetto peso: klines limit=1000 → peso ~2
return collected
if __name__ == "__main__":
start = int(time.time() * 1000) - 3600_000 # ultima ora
data = backfill_klines(start_ms=start)
print(f"totale candele: {len(data)}")
Esempio 4: Node.js con axios e proxy HTTPS
In Node.js, axios con https-proxy-agent è il pattern standard. Qui mostriamo rotazione per-request e gestione di 429/418 con backoff.
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const PROXYHAT_USER = 'user';
const PROXYHAT_PASS = 'pass';
const GATE = 'gate.proxyhat.com:8080';
const BASE = 'https://api.binance.com';
function buildAgent(country = 'DE') {
const sess = `sess-${Math.floor(Math.random() * 10_000_000)}`;
const user = `${PROXYHAT_USER}-country-${country}-session-${sess}`;
const proxyUrl = `http://${user}:${PROXYHAT_PASS}@${GATE}`;
return new HttpsProxyAgent(proxyUrl);
}
async function fetchTicker24hr(symbol) {
const url = `${BASE}/api/v3/ticker/24hr`;
const params = { symbol };
for (let attempt = 0; attempt < 5; attempt++) {
try {
const agent = buildAgent('DE');
const res = await axios.get(url, { params, httpsAgent: agent, timeout: 10000 });
const used = res.headers['x-mbx-used-weight-1m'];
console.log(`${symbol} weight=${used} status=${res.status}`);
return res.data;
} catch (err) {
if (err.response) {
const { status, headers } = err.response;
if (status === 429) {
const ra = parseInt(headers['retry-after'] || '5', 10);
console.warn(`429, sleeping ${ra}s`);
await new Promise(r => setTimeout(r, ra * 1000));
continue;
}
if (status === 418) {
console.error('418 ban, sleeping 120s');
await new Promise(r => setTimeout(r, 120_000));
continue;
}
}
const backoff = Math.pow(2, attempt) * 1000;
console.warn(`errore ${err.message}, retry in ${backoff}ms`);
await new Promise(r => setTimeout(r, backoff));
}
}
return null;
}
(async () => {
const data = await fetchTicker24hr('BTCUSDT');
if (data) console.log(`prezzo: ${data.lastPrice}, var 24h: ${data.priceChangePercent}%`);
})();
Esempio 5: curl one-liner con proxy geolocalizzato
Per test rapidi o integrazione in pipeline shell, ecco un comando curl che usa il proxy HTTP ProxyHat con uscita dagli Stati Uniti (utile per verificare la geo-restrizione HTTP 451 o per accedere a Binance.com da IP US).
# Test ticker/price con proxy US
curl -x http://user-country-US-session-test01:pass@gate.proxyhat.com:8080 \
-s -w "\nHTTP %{http_code} weight=%header{x-mbx-used-weight-1m}\n" \
"https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
# Backfill klines sticky session DE
curl -x http://user-country-DE-session-backfill01:pass@gate.proxyhat.com:8080 \
-s "https://api.binance.com/api/v3/klines?symbol=ETHUSDT&interval=1h&limit=100" \
| python -m json.tool | head -20
Quando preferire WebSocket invece del polling REST
Binance offre stream WebSocket pubblici su wss://stream.binance.com:9443 per kline, depth (diff e partial book), trade e ticker. Per dati in tempo reale continuo, i WebSocket sono quasi sempre superiori al polling REST:
- Nessun peso REST: i consumano peso API; il rate limit REST non si applica.
- Latenza minima: push di aggiornamenti in tempo reale invece di polling a intervallo fisso.
- Banda ottimizzata: stream diff depth invia solo le modifiche, non l'intero snapshot.
Il polling REST resta preferibile per snapshot puntuali, backfill storici (klines paginati), o quando serve un singolo punto nel tempo. Una pipeline ibrida (REST per backfill + WebSocket per live) è il pattern più efficiente per dataset quant. Per approfondire, consulta la documentazione WebSocket di Binance.
Errori comuni e edge case
- Ignorare X-MBX-USED-WEIGHT-1M: è l'unica fonte di verità sul consumo. Senza monitorarlo, vai alla cieca verso un ban.
- Usare proxy datacenter per Binance: IP datacenter sono spesso già flaggati o rate-limited più aggressivamente. I proxy residenziali hanno fingerprint più naturale e tasso di successo più alto.
- Rotazione troppo aggressiva senza sticky per paginazione: se cambi IP a metà backfill, i pesi si distribuiscono ma potresti perdere coerenza temporale o ricevere 451 su un IP US mid-stream.
- Non gestire HTTP 451: se l'IP è in US e l'endpoint è Binance.com, ricevi 451. Usa
-country-DEo un Paese supportato, oppure passa aapi.binance.uscon endpoint equivalenti. - Concorrenza illimitata: anche con molti IP, la CPU locale e la banda diventano colli di bottiglia. Limita a 10–50 connessioni simultanee.
Configurazione ProxyHat e risorse
Per iniziare con ProxyHat, configura le credenziali nel pannello delle location e consulta la documentazione ufficiale ProxyHat per dettagli su rotazione, sessioni sticky e geolocalizzazione. Per i prezzi dei piani residenziali, vedi la pagina pricing. Se stai costruendo pipeline di web scraping o SERP tracking, gli stessi pattern di rotazione si applicano.
Punti chiave (Key Takeaways)
- Binance usa rate limit basato su peso per IP (~6.000/min); monitora sempre
X-MBX-USED-WEIGHT-1M./api/v3/depthcon limit alto è il consumer più vorace: peso 20–50 per richiesta.- HTTP 429 = rallenta (leggi
Retry-After); HTTP 418 = ban (minuti/giorni).- Proxy residenziali rotanti distribuiscono il peso su molti IP; geolocalizzazione
-country-USrisolve HTTP 451.- Usa sessioni sticky (
-session-abc123) per backfill paginati di klines.- Per dati live continui, preferisci WebSocket; per snapshot e storici, REST con proxy.
- Usa solo dati di mercato pubblici e rispetta i ToS di Binance.
FAQ
Come fare scraping della Binance REST API con proxy?
Significa raccogliere dati di mercato pubblici (klines, depth, ticker, prezzo) dagli endpoint REST di Binance instradando le richieste attraverso proxy rotanti per distribuire il peso del rate limit su più IP ed evitare ban. Si implementa configurando il client HTTP per usare il gateway ProxyHat (gate.proxyhat.com:8080) con flag di rotazione e geolocalizzazione nel username, monitorando l'header X-MBX-USED-WEIGHT-1M e gestendo 429/418 con backoff.
Perché usare proxy per la Binance REST API è importante?
Binance applica un limite di peso per IP (~6.000/min). Un polling aggressivo di /api/v3/depth o chiamate multi-simbolo possono esaurire il budget in pochi minuti, causando HTTP 429 e poi ban HTTP 418 che durano da 2 minuti a 3 giorni. I proxy residenziali rotanti distribuiscono il peso su molti IP, mantenendo throughput elevato senza interruzioni, e permettono di superare la geo-restrizione HTTP 451 scegliendo il Paese di uscita.
Quale tipo di proxy funziona meglio per Binance?
I proxy residenziali sono i migliori per Binance perché hanno fingerprint IP naturale (ISP reali), tasso di successo più alto e meno probabilità di essere pre-flaggati rispetto ai datacenter. ProxyHat offre residenziali rotanti con geolocalizzazione per paese e città. Per backfill paginati, usa sessioni sticky; per polling distribuito, ruota per-request. SOCKS5 (porta 1080) è utile per client che lo preferiscono, ma HTTP (porta 8080) è sufficiente per la maggior parte dei casi.
Come evitare i ban quando si fa scraping della Binance REST API?
Rispetta il peso: leggi sempre X-MBX-USED-WEIGHT-1M e mantieniti sotto ~5.500/min per margine. Implementa backoff esponenziale su 429 e rispetta Retry-After. Su 418, fermati per almeno 120 secondi. Distribuisci il peso su più IP residenziali rotanti. Limita la concorrenza (10–50 connessioni). Per dati live, preferisci WebSocket. Non forzare mai oltre il limite: un ban di 3 giorni può invalidare giorni di raccolta dati.






