Se você está construindo pipelines de dados quant ou alimentando modelos de trading com candles e order books da Binance, já esbarrou no limite de peso por IP. Este guia mostra como raspar a API REST da Binance com proxies de forma eficiente, cobrindo endpoints públicos, rate limiting baseado em peso, rotação de IPs e padrões de produção com retries e backoff exponencial.
Aviso de conformidade: Este artigo cobre apenas dados públicos de mercado via endpoints REST abertos. Respeite os limites de peso documentados, leia os Termos de Uso da Binance e prefira acesso oficial via API quando os termos exigirem. Não incentivamos coleta de dados privados, evasão de restrições regulatórias nem violação de ToS.
Por que raspar a API REST da Binance com proxies é necessário
A Binance opera uma das maiores APIs públicas de cripto do mundo. Os endpoints REST em api.binance.com são gratuitos, não exigem autenticação para dados de mercado e retornam JSON estruturado. O problema é que a Binance usa um sistema de limite de taxa baseado em peso por IP, não por conta. Cada endpoint consome uma quantidade diferente de "peso" e o orçamento por IP é de aproximadamente 6.000 pesos por minuto.
Quando você consome esse orçamento, recebe um HTTP 429 Too Many Requests. Se persistir, a Binance escala para um HTTP 418 (IP banido) com um cabeçalho Retry-After indicando quanto tempo esperar — tipicamente entre 2 minutos e 3 dias, conforme a severidade. É por isso que desenvolvedores precisam de proxies: distribuir o peso across múltiplos IPs é a única forma de escalar a coleta sem ser banido.
Segundo a documentação oficial da Binance Spot API, cada resposta inclui o cabeçalho X-MBX-USED-WEIGHT-1M, que indica quantos pesos você consumiu no último minuto. Monitorar esse valor é essencial para throttling dinâmico.
Endpoints-chave de dados públicos de mercado
| Endpoint | Descrição | Peso (aprox.) | Uso típico |
|---|---|---|---|
GET /api/v3/klines | Candlesticks OHLCV | 1–2 | Backfill histórico, gráficos |
GET /api/v3/depth | Order book snapshot | 5–20 (depende do limit) | Microestrutura de mercado |
GET /api/v3/ticker/24hr | Estatísticas 24h | 1–40 (por symbol ou all) | Dashboards, screening |
GET /api/v3/ticker/price | Preço atual | 1–2 | Monitoramento em tempo real |
O endpoint /api/v3/depth é o mais custoso: com limit=1000, consome 20 pesos por requisição. Em polling contínuo a cada 500ms, você queima 2.400 pesos por minuto — quase metade do orçamento em um único IP. Com limit=5000, são 50 pesos por chamada, esgotando o orçamento em ~2,4 minutos.
Arquitetura de coleta com proxies residenciais rotativos
A estratégia é simples: cada IP recebe seu próprio orçamento de 6.000 pesos/min. Com N IPs residenciais rotativos, seu orçamento efetivo é N × 6.000. Com 50 IPs, são 300.000 pesos por minuto — suficiente para polling agressivo de depth em múltiplos pares.
Proxies residenciais são preferidos sobre datacenter porque a Binance frequentemente penaliza ranges de IPs de datacenter conhecidos. IPs residenciais aparecem como tráfego de usuários finais legítimos, reduzindo a probabilidade de 429 e 418.
Bypass do geo-split Binance.com vs Binance.US
Usuários nos EUA frequentemente recebem HTTP 451 Unavailable For Legal Reasons ao acessar api.binance.com, devido ao bloqueio geográfico. Com geo-targeting no username do ProxyHat (-country-US), você pode rotear através de IPs de outros países para acessar endpoints globais, ou usar IPs dos EUA para acessar api.binance.us quando apropriado.
Configuração do ProxyHat: HTTP e SOCKS5
O ProxyHat oferece gateway único em gate.proxyhat.com com porta 8080 para HTTP e 1080 para SOCKS5. A rotação e geo-targeting são controlados via flags no username.
Formatos de URL
# HTTP — rotação automática por requisição
http://USERNAME:PASSWORD@gate.proxyhat.com:8080
# HTTP — geo-targeting EUA
http://USERNAME-country-US:PASSWORD@gate.proxyhat.com:8080
# HTTP — sessão fixa (sticky) para paginação
http://USERNAME-session-abc123:PASSWORD@gate.proxyhat.com:8080
# SOCKS5 — para clientes que exigem SOCKS
socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080
Exemplo 1: curl com proxy bruto e monitoramento de peso
# Consultar klines com proxy rotativo e capturar peso usado
curl -x http://USERNAME:PASSWORD@gate.proxyhat.com:8080 \
-s -D - \
'https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1m&limit=100' \
-o /dev/null | grep -i 'x-mbx-used-weight'
O cabeçalho X-MBX-USED-WEIGHT-1M na resposta diz exatamente quantos pesos aquele IP consumiu. Em rotação por requisição, cada IP começa com peso próximo de zero.
Exemplo 2: Python requests com rotação por requisição via ProxyHat SDK
import requests
import time
import random
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PROXYHAT_USER = 'seu_usuario'
PROXYHAT_PASS = 'sua_senha'
# Cada request_id único força novo IP
def get_proxy_url(request_id: str, country: str = None) -> str:
username = f'{PROXYHAT_USER}-session-{request_id}'
if country:
username += f'-country-{country}'
return f'http://{username}:{PROXYHAT_PASS}@gate.proxyhat.com:8080'
def fetch_klines(symbol: str, interval: str, limit: int, max_retries: int = 5) -> list:
url = f'https://api.binance.com/api/v3/klines'
params = {'symbol': symbol, 'interval': interval, 'limit': limit}
for attempt in range(max_retries):
request_id = f'klines-{int(time.time())}-{random.randint(1000,9999)}'
proxies = {
'http': get_proxy_url(request_id, country='DE'),
'https': get_proxy_url(request_id, country='DE'),
}
try:
resp = requests.get(url, params=params, proxies=proxies, timeout=10)
used_weight = resp.headers.get('X-MBX-USED-WEIGHT-1M', '?')
logger.info(f'Peso usado no IP: {used_weight} | attempt={attempt}')
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 429:
retry_after = int(resp.headers.get('Retry-After', 5))
logger.warning(f'429 recebido. Esperando {retry_after}s')
time.sleep(retry_after)
elif resp.status_code == 418:
retry_after = int(resp.headers.get('Retry-After', 60))
logger.error(f'418 banido neste IP. Esperando {retry_after}s')
time.sleep(retry_after)
else:
resp.raise_for_status()
except requests.RequestException as e:
backoff = (2 ** attempt) + random.uniform(0, 1)
logger.warning(f'Erro: {e}. Backoff {backoff:.1f}s')
time.sleep(backoff)
raise RuntimeError(f'Falha após {max_retries} tentativas')
# Uso
data = fetch_klines('BTCUSDT', '1m', 500)
print(f'Candles recebidos: {len(data)}')
Neste exemplo, cada chamada gera um novo session-ID, forçando o ProxyHat a atribuir um novo IP residencial. O monitoramento do cabeçalho X-MBX-USED-WEIGHT-1M permite throttling adaptativo.
Exemplo 3: Python httpx async com sessão fixa para backfill paginado de klines
import httpx
import asyncio
import logging
from datetime import datetime, timedelta
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PROXYHAT_USER = 'seu_usuario'
PROXYHAT_PASS = 'sua_senha'
# Sessão fixa mantém o MESMO IP durante todo o backfill
# Isso é crítico: se o IP mudar no meio da paginação,
# o peso acumulado é perdido e você recomeça do zero.
SESSION_ID = 'backfill-btcusdt-001'
BASE_PROXY = (
f'http://{PROXYHAT_USER}-session-{SESSION_ID}:'
f'{PROXYHAT_PASS}@gate.proxyhat.com:8080'
)
class WeightAwareThrottler:
def __init__(self, max_weight: int = 5000):
self.max_weight = max_weight
self.current_weight = 0
def check(self, used_weight_header: str):
if used_weight_header:
self.current_weight = int(used_weight_header)
if self.current_weight >= self.max_weight:
sleep_time = 62 # reseta a cada ~60s
logger.info(f'Peso {self.current_weight} >= limite. Dormindo {sleep_time}s')
asyncio.get_event_loop().run_until_complete(
asyncio.sleep(sleep_time)
)
async def fetch_klines_page(client, symbol, interval, start_time, end_time):
params = {
'symbol': symbol,
'interval': interval,
'startTime': start_time,
'endTime': end_time,
'limit': 1000,
}
resp = await client.get(
'https://api.binance.com/api/v3/klines',
params=params
)
used = resp.headers.get('x-mbx-used-weight-1m', '0')
logger.info(f'Peso usado: {used}')
if resp.status_code == 429:
retry_after = int(resp.headers.get('retry-after', 5))
await asyncio.sleep(retry_after)
return await fetch_klines_page(client, symbol, interval, start_time, end_time)
resp.raise_for_status()
return resp.json()
async def backfill_klines(symbol: str, interval: str, days: int = 7):
throttler = WeightAwareThrottler(max_weight=5000)
start = int((datetime.utcnow() - timedelta(days=days)).timestamp() * 1000)
end = int(datetime.utcnow().timestamp() * 1000)
all_candles = []
async with httpx.AsyncClient(
proxy=BASE_PROXY,
timeout=15.0,
limits=httpx.Limits(max_connections=5, max_keepalive_connections=2),
) as client:
current = start
while current < end:
page_end = min(current + (1000 * 60_000), end) # 1000 candles de 1m
candles = await fetch_klines_page(client, symbol, interval, current, page_end)
if not candles:
break
all_candles.extend(candles)
current = candles[-1][0] + 1 # next open time + 1ms
throttler.check(client.headers.get('x-mbx-used-weight-1m', '0'))
await asyncio.sleep(0.25) # 250ms entre chamadas
logger.info(f'Total candles coletados: {len(all_candles)}')
return all_candles
# Executar
asyncio.run(backfill_klines('BTCUSDT', '1m', days=3))
Aqui usamos uma sessão fixa (-session-backfill-btcusdt-001) para manter o mesmo IP durante toda a paginação. Isso preserva o contexto de peso acumulado e evita resets desnecessários. O WeightAwareThrottler monitora o peso e pausa antes de atingir o limite de 6.000.
Exemplo 4: Node.js com axios e rotação de IPs
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const PROXYHAT_USER = 'seu_usuario';
const PROXYHAT_PASS = 'sua_senha';
function buildProxy(requestId, country = null) {
let username = `${PROXYHAT_USER}-session-${requestId}`;
if (country) username += `-country-${country}`;
return `http://${username}:${PROXYHAT_PASS}@gate.proxyhat.com:8080`;
}
async function fetchDepth(symbol, limit = 100, maxRetries = 5) {
const url = 'https://api.binance.com/api/v3/depth';
for (let attempt = 0; attempt < maxRetries; attempt++) {
const requestId = `depth-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
const proxyUrl = buildProxy(requestId, 'JP');
const agent = new HttpsProxyAgent(proxyUrl);
try {
const resp = await axios.get(url, {
params: { symbol, limit },
httpsAgent: agent,
timeout: 10000,
headers: { 'User-Agent': 'Mozilla/5.0' },
});
const usedWeight = resp.headers['x-mbx-used-weight-1m'];
console.log(`Peso: ${usedWeight} | IP rotacionado: ${requestId}`);
return resp.data;
} catch (err) {
if (err.response) {
const status = err.response.status;
const retryAfter = err.response.headers['retry-after'] || 5;
if (status === 429) {
console.warn(`429: esperando ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
} else if (status === 418) {
console.error(`418 banido neste IP. Esperando ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
} else {
throw err;
}
} else {
const backoff = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.warn(`Erro de rede: ${err.message}. Backoff ${backoff}ms`);
await new Promise(r => setTimeout(r, backoff));
}
}
}
throw new Error(`Falha após ${maxRetries} tentativas`);
}
// Polling com concorrência limitada
async function pollDepth(symbols, intervalMs = 1000) {
const MAX_CONCURRENT = 5;
const queue = [...symbols];
const results = [];
async function worker() {
while (queue.length > 0) {
const sym = queue.shift();
try {
const data = await fetchDepth(sym, 100);
results.push({ symbol: sym, data });
} catch (e) {
console.error(`Falha em ${sym}: ${e.message}`);
}
await new Promise(r => setTimeout(r, intervalMs));
}
}
await Promise.all(Array.from({ length: MAX_CONCURRENT }, () => worker()));
return results;
}
// Uso
pollDepth(['BTCUSDT', 'ETHUSDT', 'SOLUSDT'], 800).then(() => {
console.log('Coleta concluída');
});
Exemplo 5: Throttling com peso adaptativo e circuit breaker
import httpx
import asyncio
import logging
import random
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
PROXYHAT_USER = 'seu_usuario'
PROXYHAT_PASS = 'sua_senha'
class BinanceScraper:
def __init__(self, max_concurrent=10, weight_threshold=5500):
self.max_concurrent = max_concurrent
self.weight_threshold = weight_threshold
self.circuit_open = False
self.consecutive_failures = 0
self.semaphore = asyncio.Semaphore(max_concurrent)
def _proxy_url(self, session_id, country=None):
user = f'{PROXYHAT_USER}-session-{session_id}'
if country:
user += f'-country-{country}'
return f'http://{user}:{PROXYHAT_PASS}@gate.proxyhat.com:8080'
async def _request(self, endpoint, params, session_id, country=None):
async with self.semaphore:
if self.circuit_open:
logger.warning('Circuit breaker aberto. Aguardando 30s')
await asyncio.sleep(30)
self.circuit_open = False
proxy = self._proxy_url(session_id, country)
async with httpx.AsyncClient(proxy=proxy, timeout=10) as client:
resp = await client.get(
f'https://api.binance.com{endpoint}',
params=params
)
used_weight = int(resp.headers.get('x-mbx-used-weight-1m', 0))
# Throttling adaptativo baseado no peso
if used_weight > self.weight_threshold:
sleep_time = max(60 - (used_weight // 100), 5)
logger.info(f'Peso {used_weight} alto. Dormindo {sleep_time}s')
await asyncio.sleep(sleep_time)
if resp.status_code == 200:
self.consecutive_failures = 0
return resp.json()
elif resp.status_code == 429:
retry_after = int(resp.headers.get('retry-after', 10))
await asyncio.sleep(retry_after)
return await self._request(endpoint, params, session_id, country)
elif resp.status_code == 418:
self.consecutive_failures += 1
if self.consecutive_failures >= 3:
self.circuit_open = True
retry_after = int(resp.headers.get('retry-after', 120))
logger.error(f'IP banido. Esperando {retry_after}s')
await asyncio.sleep(retry_after)
# Novo IP via nova session
new_sid = f'{session_id}-retry-{random.randint(100,999)}'
return await self._request(endpoint, params, new_sid, country)
else:
resp.raise_for_status()
async def scrape_ticker_24hr(self, symbols):
tasks = []
for sym in symbols:
sid = f'ticker24-{sym}-{random.randint(0,99999)}'
tasks.append(
self._request(
'/api/v3/ticker/24hr',
{'symbol': sym},
sid,
country='SG'
)
)
return await asyncio.gather(*tasks, return_exceptions=True)
# Uso
async def main():
scraper = BinanceScraper(max_concurrent=10, weight_threshold=5500)
symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT']
results = await scraper.scrape_ticker_24hr(symbols)
for sym, result in zip(symbols, results):
if isinstance(result, Exception):
logger.error(f'{sym}: {result}')
else:
logger.info(f'{sym}: var {result.get("priceChangePercent", "N/A")}%')
asyncio.run(main())
Quando usar WebSocket streams em vez de REST polling
Para dados em tempo real, a Binance oferece WebSocket streams em wss://stream.binance.com:9443. Para klines e depth contínuos, WebSocket é superior porque:
- Não consome peso REST: streams são rate-limited separadamente (máx 5 conexões por IP, 200 streams por conexão).
- Latência menor: pushes em vez de polling. Atualizações de depth chegam em ~10ms vs 500ms+ de polling.
- Menos overhead: uma conexão WebSocket substitui dezenas de polls REST por minuto.
Use REST para backfill histórico (klines paginadas), snapshots pontuais e endpoints sem equivalente WebSocket. Use WebSocket para monitoramento contínuo de preço, depth incremental e trade streams.
Erros comuns e armadilhas
- Ignorar o cabeçalho X-MBX-USED-WEIGHT-1M: sem monitorar o peso, você descobre o limite só quando recebe 429.
- Usar datacenter proxies para depth polling: ranges de datacenter são frequentemente penalizados. Prefira residenciais.
- Não respeitar Retry-After: receber 429 e continuar batendo garante um 418 com ban de horas ou dias.
- Mudar de IP no meio de paginação: se você está paginando klines com startTime/endTime, trocar de IP reinicia o contador de peso mas pode causar gaps se a paginação depende de estado do servidor.
- Concorrência sem limite: abrir 50 conexões simultâneas sem semáforo esgota o orçamento em segundos. Use
asyncio.Semaphoreou equivalente. - Não tratar HTTP 451: se você acessa api.binance.com de um IP dos EUA sem geo-targeting, pode receber 451. Configure
-country-XXapropriadamente.
Configuração específica do ProxyHat
Para começar com o ProxyHat, acesse o dashboard de preços e escolha um plano residencial. A configuração é imediata — sem SDK proprietário obrigatório, apenas HTTP/SOCKS5 com flags no username.
Consulte a documentação oficial do ProxyHat para detalhes sobre geo-targeting por cidade, rotação por header e integrações avançadas.
Para casos de uso de web scraping em geral, veja nosso guia de web scraping com proxies. Para rastreamento de SERPs, confira SERP tracking com proxies. A lista completa de localizações disponíveis está em proxy locations.
Pontos-chave
- Peso, não requisições: a Binance limita por peso (6.000/min por IP), não por count.
/api/v3/depthcom limit=1000 custa 20 pesos por chamada.- Monitore X-MBX-USED-WEIGHT-1M: esse cabeçalho é seu medidor de combustível. Sem ele, você opera no escuro.
- 429 é aviso, 418 é banimento: respeite
Retry-Aftersempre. Ignorar 429 leva a 418 com bans de horas a dias.- Residenciais > datacenter: IPs residenciais rotativos distribuem peso across múltiplos orçamentos e evitam penalizações de ranges conhecidos.
- Sessão fixa para paginação: use
-session-XXXpara manter o mesmo IP durante backfill. Rotação por requisição para coletas independentes.- WebSocket para tempo real: para monitoramento contínuo, streams são mais eficientes que REST polling.
- Geo-targeting resolve 451: use
-country-XXpara bypassar bloqueios regionais e acessar endpoints globais.
FAQ
O que é raspar a API REST da Binance com proxies?
É a prática de coletar dados públicos de mercado (candles, order book, tickers) dos endpoints REST abertos da Binance (api.binance.com/api/v3/...) roteando requisições através de proxies rotativos para distribuir o consumo de peso across múltiplos IPs, evitando rate limits e banimentos. A Binance usa limite baseado em peso (~6.000/min por IP), então proxies permitem escalar a coleta proporcionalmente ao número de IPs disponíveis.
Por que isso importa para usuários de proxy?
Porque a Binance penaliza agressivamente IPs que excedem o orçamento de peso. Sem proxies, um único IP é banido em minutos ao fazer polling de depth. Com proxies residenciais rotativos, cada IP tem seu próprio orçamento, multiplicando a capacidade de coleta. Além disso, o geo-targeting via username (-country-XX) resolve bloqueios regionais (HTTP 451) que afetam usuários em jurisdições restritas como os EUA.
Qual tipo de proxy funciona melhor para raspar a Binance?
Proxies residenciais rotativos são os melhores porque aparecem como tráfego de usuários finais legítimos e não são penalizados como ranges de datacenter. Para backfill paginado, use sessões fixas (-session-XXX) para manter o mesmo IP e preservar o contexto de peso. Para coletas independentes (snapshots pontuais), rotação por requisição é ideal. SOCKS5 (porta 1080) é útil para clientes que exigem esse protocolo, mas HTTP na porta 8080 funciona para a maioria dos casos.
Como evitar bloqueios ao raspar a Binance REST API?
Monitore o cabeçalho X-MBX-USED-WEIGHT-1M em cada resposta e implemente throttling adaptativo: se o peso se aproximar de 5.500, pause por 60s. Trate 429 respeitando Retry-After e nunca continue batendo após receber 418. Use backoff exponencial com jitter para erros de rede. Limite a concorrência com semáforos (10–20 conexões simultâneas é seguro). Rotacione IPs por requisição para endpoints independentes e use sessões fixas para paginação.
Quando devo usar WebSocket em vez de REST polling?
Use WebSocket (wss://stream.binance.com:9443) para monitoramento contínuo de preço, depth incremental e trade streams. WebSocket não consome peso REST, tem latência menor (~10ms vs 500ms+) e substitui dezenas de polls por minuto. Use REST para backfill histórico de klines paginadas, snapshots pontuais e endpoints sem equivalente WebSocket (como /api/v3/ticker/24hr para todos os símbolos de uma vez).






