Как скрейпить Binance REST API через прокси: быстрый ответ
Чтобы скрейпить Binance REST API через прокси, нужно направлять запросы к публичным эндпоинтам api.binance.com через вращающиеся резидентные прокси gate.proxyhat.com:8080, распределяя весовые лимиты по нескольким IP-адресам. Binance использует весовую систему ограничения скорости — около 6000 единиц веса в минуту на IP. Ротация IP через прокси позволяет собирать данные свечей (klines), книги ордеров (depth) и тикеров в масштабе, недоступном с одного адреса.
Дисклеймер: Данная статья охватывает только публичные рыночные данные Binance (эндпоинты
/api/v3/без аутентификации). Уважайте Условия использования Binance и весовые лимиты API. Если ваши задачи требуют официального доступа, используйте официальную документацию Binance API и при необходимости запрашивайте повышенные лимиты через поддержку.
Технический контекст: эндпоинты, веса и лимиты
Binance REST API для спот-торговли работает на домене api.binance.com и предоставляет публичные эндпоинты рыночных данных без аутентификации. Каждый запрос имеет вес — единицу нагрузки, которая вычитается из минутного бюджета IP-адреса. Заголовок ответа X-MBX-USED-WEIGHT-1M показывает текущий израсходованный вес за последнюю минуту.
Ключевые эндпоинты публичных рыночных данных
| Эндпоинт | Назначение | Вес (зависит от параметров) |
|---|---|---|
/api/v3/klines | Свечи (OHLCV) по интервалам | 1 (limit ≤ 100), 2 (limit > 100) |
/api/v3/depth | Книга ордеров (bid/ask) | 1–20 (зависит от limit) |
/api/v3/ticker/24hr | 24-часовая статистика | 1 (1 символ), 80 (все) |
/api/v3/ticker/price | Текущая цена | 1 (1 символ), 2 (все) |
Весовая система лимитов и блокировки
Binance применяет весовое ограничение скорости — примерно 6000 единиц веса в минуту на IP-адрес для спот-API. Превышение приводит к следующим ответам:
- HTTP 429 — превышен лимит скорости. Запросы нужно приостановить. Заголовок
Retry-Afterуказывает время ожидания в секундах. - HTTP 418 — IP автоматически заблокирован после многократных нарушений 429. Бан длится от 2 минут до нескольких дней.
- HTTP 451 — регион недоступен (географическое ограничение, актуально для Binance.com в США).
Тесный поллинг /api/v3/depth с limit=1000 (вес 5) исчерпает бюджет 6000 за ~1200 запросов — примерно 20 запросов в секунду исчерпают лимит за 5 минут. С limit=5000 (вес 20) бюджет уйдёт за 300 запросов — менее чем за 2 минуты при 3 запросах/сек. Подробнее см. в официальной документации Binance Spot API и MDN: HTTP 429 Too Many Requests.
Почему прокси необходимы для скрейпинга Binance
С одного IP-адреса вы упрётесь в лимит 6000 веса/мин. Для quant-разработчиков и инженеров криптоданных, собирающих исторические свечи по десяткам торговых пар или поллинг книги ордеров с высокой частотой, этого недостаточно. Вращающиеся резидентные прокси распределяют вес по пулу IP-адресов — каждый IP получает свой бюджет 6000/мин.
Дополнительно, Binance разделяет binance.com (глобальный) и binance.us (США). Запросы к глобальному API из США могут получить HTTP 451. Геотаргетинг ProxyHat через параметр -country-US в имени пользователя позволяет направлять запросы через IP нужной страны.
Практическая реализация: код и примеры
1. Базовый запрос через ProxyHat (curl, HTTP)
Простейший способ — направить запрос через HTTP-прокси ProxyHat с геотаргетингом на США:
# Получение свечей BTCUSDT через прокси с IP в США
curl -x "http://user-country-US:pass@gate.proxyhat.com:8080" \
"https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1m&limit=100"
# Проверка израсходованного веса
curl -s -o /dev/null -D - -x "http://user-country-US:pass@gate.proxyhat.com:8080" \
"https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT" \
| grep -i "x-mbx-used-weight-1m"
2. Python requests: ротация IP через ProxyHat SDK-паттерн
Инкапсулируем логику ProxyHat в класс-обёртку (SDK-паттерн) с отслеживанием веса, retry с экспоненциальной задержкой и автоматической ротацией IP:
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")
class ProxyHatBinanceClient:
"""SDK-обёртка для скрейпинга Binance через ротацию IP ProxyHat."""
BASE_URL = "https://api.binance.com"
GATE = "gate.proxyhat.com:8080"
MAX_WEIGHT_PER_MIN = 5500 # буфер 500 ниже лимита 6000
def __init__(self, username: str, password: str, country: str = None):
self.username = username
self.password = password
self.country = country
self.used_weight = 0
self.session = requests.Session()
def _proxy_url(self, session_id: str = None) -> str:
"""Формирует URL прокси с опциональной гео-привязкой и сессией."""
user = self.username
if self.country:
user += f"-country-{self.country}"
if session_id:
user += f"-session-{session_id}"
return f"http://{user}:{self.password}@{self.GATE}"
def _request(self, path: str, params: dict = None, session_id: str = None) -> dict:
"""Выполняет запрос с retry, отслеживанием веса и ротацией IP."""
url = f"{self.BASE_URL}{path}"
proxies = {"http": self._proxy_url(session_id), "https": self._proxy_url(session_id)}
for attempt in range(5):
# Throttle: если вес близок к лимиту — ждём
if self.used_weight >= self.MAX_WEIGHT_PER_MIN:
logger.info("Weight limit reached (%d), cooling down 60s", self.used_weight)
time.sleep(60)
self.used_weight = 0
try:
resp = self.session.get(url, params=params, proxies=proxies, timeout=15)
# Обновляем отслеживание веса
weight_header = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight_header:
self.used_weight = int(weight_header)
logger.debug("Used weight: %s", weight_header)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 10))
logger.warning("429 rate limited, waiting %ds", retry_after)
time.sleep(retry_after)
continue
if resp.status_code == 418:
logger.error("418 IP banned! Waiting 120s before retry")
time.sleep(120)
continue
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
wait = 2 ** attempt
logger.warning("Attempt %d failed: %s, retrying in %ds", attempt + 1, e, wait)
time.sleep(wait)
raise RuntimeError(f"Failed after 5 attempts: {path}")
def get_klines(self, symbol: str, interval: str = "1m", limit: int = 100) -> list:
return self._request("/api/v3/klines", {
"symbol": symbol, "interval": interval, "limit": limit
})
def get_depth(self, symbol: str, limit: int = 100) -> dict:
return self._request("/api/v3/depth", {
"symbol": symbol, "limit": limit
})
def get_ticker_24hr(self, symbol: str = None) -> dict | list:
params = {"symbol": symbol} if symbol else {}
return self._request("/api/v3/ticker/24hr", params or None)
def get_price(self, symbol: str = None) -> dict | list:
params = {"symbol": symbol} if symbol else {}
return self._request("/api/v3/ticker/price", params or None)
# Использование
client = ProxyHatBinanceClient(username="user", password="pass", country="US")
# Ротация IP — каждый запрос идёт через новый IP
for sym in ["BTCUSDT", "ETHUSDT", "BNBUSDT"]:
klines = client.get_klines(sym, interval="5m", limit=100)
logger.info("%s: got %d candles", sym, len(klines))
3. Python httpx: асинхронная ротация с контролем параллелизма
Для высокопроизводительного сбора данных используйте httpx с asyncio и семафором для ограничения параллелизма:
import httpx
import asyncio
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("async_binance")
GATE = "gate.proxyhat.com:8080"
BASE = "https://api.binance.com"
MAX_CONCURRENT = 10
async def fetch_klines(client: httpx.AsyncClient, symbol: str, interval: str, limit: int):
"""Получение klines через асинхронный клиент с прокси."""
params = {"symbol": symbol, "interval": interval, "limit": limit}
try:
resp = await client.get(f"{BASE}/api/v3/klines", params=params, timeout=15)
weight = resp.headers.get("x-mbx-used-weight-1m", "?")
logger.info("%s: %d candles, weight=%s", symbol, len(resp.json()), weight)
return resp.json()
except httpx.HTTPStatusError as e:
if e.response.status_code == 429:
retry_after = int(e.response.headers.get("retry-after", 10))
logger.warning("%s: 429, waiting %ds", symbol, retry_after)
await asyncio.sleep(retry_after)
raise
async def main():
symbols = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT", "XRPUSDT"]
proxy_url = "http://user-country-US:pass@gate.proxyhat.com:8080"
semaphore = asyncio.Semaphore(MAX_CONCURRENT)
async with httpx.AsyncClient(
proxy=proxy_url,
limits=httpx.Limits(max_connections=20, max_keepalive_connections=10),
) as client:
async def bounded_fetch(sym):
async with semaphore:
return await fetch_klines(client, sym, "1m", 100)
results = await asyncio.gather(
*[bounded_fetch(s) for s in symbols], return_exceptions=True
)
for sym, res in zip(symbols, results):
if isinstance(res, Exception):
logger.error("%s failed: %s", sym, res)
else:
logger.info("%s: %d data points", sym, len(res))
asyncio.run(main())
4. Python: sticky-сессия для бэкфилла исторических klines
Для пагинации исторических данных используйте sticky-сессию (-session-abc123), чтобы все запросы шли через один IP. Это позволяет использовать непрерывный весовой бюджет одного IP и избегать рассинхронизации:
import requests
import time
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("klines_backfill")
def backfill_klines(symbol: str, interval: str, start_time: int, end_time: int):
"""Бэкфилл исторических свечей через sticky-сессию ProxyHat.
Args:
symbol: Торговая пара, напр. 'BTCUSDT'
interval: Интервал свечей, напр. '1m', '1h', '1d'
start_time: Unix timestamp (ms) начала периода
end_time: Unix timestamp (ms) конца периода
"""
GATE = "gate.proxyhat.com:8080"
SESSION_ID = f"backfill-{symbol}-{interval}"
proxy_url = f"http://user-session-{SESSION_ID}:pass@{GATE}"
proxies = {"http": proxy_url, "https": proxy_url}
all_klines = []
current_start = start_time
max_per_request = 1000 # Binance максимум для klines
used_weight = 0
session = requests.Session()
while current_start < end_time:
# Weight-aware throttle
if used_weight >= 5500:
logger.info("Weight %d, cooling down 60s", used_weight)
time.sleep(60)
used_weight = 0
params = {
"symbol": symbol,
"interval": interval,
"startTime": current_start,
"endTime": end_time,
"limit": max_per_request,
}
for attempt in range(5):
try:
resp = session.get(
"https://api.binance.com/api/v3/klines",
params=params, proxies=proxies, timeout=15
)
weight_h = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight_h:
used_weight = int(weight_h)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 10))
logger.warning("429, waiting %ds", wait)
time.sleep(wait)
continue
resp.raise_for_status()
batch = resp.json()
if not batch:
logger.info("No more data, stopping")
return all_klines
all_klines.extend(batch)
# Сдвигаем startTime на время последней свечи + 1ms
current_start = batch[-1][6] + 1 # index 6 = closeTime
logger.info("Got %d candles (total %d), weight=%s, next=%d",
len(batch), len(all_klines), weight_h, current_start)
break
except requests.RequestException as e:
wait = 2 ** attempt
logger.warning("Retry %d: %s, waiting %ds", attempt + 1, e, wait)
time.sleep(wait)
else:
raise RuntimeError(f"Failed after 5 retries at startTime={current_start}")
# Небольшая задержка между запросами
time.sleep(0.1)
return all_klines
# Бэкфилл BTCUSDT за 1 час (1-минутные свечи = 60 свечей)
data = backfill_klines("BTCUSDT", "1m", 1700000000000, 1700003600000)
logger.info("Total candles collected: %d", len(data))
5. Node.js axios: ротация IP с retry и weight-tracking
Для JavaScript/TypeScript-разработчиков — эквивалент на axios с прокси-агентом:
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent').HttpsProxyAgent;
const logging = require('logging').default;
const logger = logging.getLogger('binance_node');
const GATE = 'gate.proxyhat.com:8080';
const BASE = 'https://api.binance.com';
/**
* Клиент для скрейпинга Binance через ротацию IP ProxyHat.
*/
class BinanceScraper {
constructor(username, password, country = null) {
this.username = username;
this.password = password;
this.country = country;
this.usedWeight = 0;
this.maxWeightPerMin = 5500;
}
getProxyUrl(sessionId = null) {
let user = this.username;
if (this.country) user += `-country-${this.country}`;
if (sessionId) user += `-session-${sessionId}`;
return `http://${user}:${this.password}@${GATE}`;
}
async request(path, params = {}, sessionId = null) {
const proxyUrl = this.getProxyUrl(sessionId);
const agent = new HttpsProxyAgent(proxyUrl);
const url = `${BASE}${path}`;
for (let attempt = 0; attempt < 5; attempt++) {
if (this.usedWeight >= this.maxWeightPerMin) {
logger.info(`Weight ${this.usedWeight}, cooling down 60s`);
await new Promise(r => setTimeout(r, 60000));
this.usedWeight = 0;
}
try {
const resp = await axios.get(url, {
params,
httpsAgent: agent,
timeout: 15000,
});
const weight = resp.headers['x-mbx-used-weight-1m'];
if (weight) this.usedWeight = parseInt(weight);
return resp.data;
} catch (err) {
if (err.response) {
const status = err.response.status;
if (status === 429) {
const retryAfter = parseInt(err.response.headers['retry-after'] || '10');
logger.warn(`429 rate limited, waiting ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
if (status === 418) {
logger.error('418 IP banned, waiting 120s');
await new Promise(r => setTimeout(r, 120000));
continue;
}
}
const wait = Math.pow(2, attempt) * 1000;
logger.warn(`Attempt ${attempt + 1} failed: ${err.message}, retry in ${wait}ms`);
await new Promise(r => setTimeout(r, wait));
}
}
throw new Error(`Failed after 5 attempts: ${path}`);
}
async getKlines(symbol, interval = '1m', limit = 100) {
return this.request('/api/v3/klines', { symbol, interval, limit });
}
async getDepth(symbol, limit = 100) {
return this.request('/api/v3/depth', { symbol, limit });
}
async getTicker24hr(symbol = null) {
const params = symbol ? { symbol } : {};
return this.request('/api/v3/ticker/24hr', params);
}
}
// Использование
(async () => {
const scraper = new BinanceScraper('user', 'pass', 'US');
const symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT'];
for (const sym of symbols) {
const klines = await scraper.getKlines(sym, '5m', 100);
logger.info(`${sym}: ${klines.length} candles`);
}
})();
6. SOCKS5 для высокочастотного поллинга depth
Для поллинга книги ордеров с высокой частотой SOCKS5-прокси может обеспечить меньшую задержку:
import requests
import time
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("depth_poller")
def poll_depth(symbol: str, limit: int = 100, interval_sec: float = 0.5):
"""Поллинг книги ордеров через SOCKS5 ProxyHat с ротацией IP."""
socks_proxy = "socks5://user-country-US:pass@gate.proxyhat.com:1080"
proxies = {"http": socks_proxy, "https": socks_proxy}
session = requests.Session()
used_weight = 0
while True:
if used_weight >= 5500:
logger.info("Weight %d, cooling down", used_weight)
time.sleep(60)
used_weight = 0
try:
resp = session.get(
"https://api.binance.com/api/v3/depth",
params={"symbol": symbol, "limit": limit},
proxies=proxies, timeout=10
)
weight_h = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight_h:
used_weight = int(weight_h)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 10))
logger.warning("429, waiting %ds", wait)
time.sleep(wait)
continue
data = resp.json()
bids_count = len(data.get("bids", []))
asks_count = len(data.get("asks", []))
logger.info("%s depth: %d bids / %d asks, weight=%s",
symbol, bids_count, asks_count, weight_h)
except Exception as e:
logger.error("Error: %s", e)
time.sleep(interval_sec)
# poll_depth("BTCUSDT", limit=100, interval_sec=0.5)
Когда WebSocket лучше REST-поллинга
Binance предоставляет публичные WebSocket-стримы на wss://stream.binance.com:9443/ws для данных в реальном времени. Для непрерывного мониторинга книги ордеров или свечей WebSocket эффективнее REST-поллинга:
- REST-поллинг — подходит для периодического сбора, бэкфилла истории, snapshot-ов. Каждый запрос тратит вес.
- WebSocket — одно соединение, push-обновления без расхода веса. Идеально для real-time orderbook и kline-streams.
Используйте REST через прокси для исторических данных и snapshot-ов, а WebSocket — для live-streaming. Прокси ProxyHat поддерживают как HTTP, так и SOCKS5 для обоих сценариев.
Распространённые ошибки и edge-cases
- Игнорирование заголовка веса — всегда читайте
X-MBX-USED-WEIGHT-1Mи throttling'те до исчерпания лимита, а не после. - Отсутствие retry-логики для 429/418 — без обработки
Retry-Afterвы получите бан 418. - Слишком высокий параллелизм — 100+ одновременных запросов с одного IP исчерпают вес за секунды. Ограничивайте concurrency семафором.
- Геоблок 451 — если целевой эндпоинт возвращает 451, используйте
-country-USили другую страну в имени пользователя ProxyHat. - Смешивание сессий — для пагинации klines используйте одну sticky-сессию, чтобы вес накапливался на одном IP предсказуемо.
Настройка ProxyHat и внутренние ссылки
ProxyHat предоставляет резидентные, мобильные и дата-центр прокси с гибким геотаргетингом. Для скрейпинга Binance API рекомендуем резидентные прокси — они выглядят как реальные пользовательские IP и снижают риск блокировок. Конфигурация проста:
- HTTP-прокси:
http://USERNAME:PASSWORD@gate.proxyhat.com:8080 - SOCKS5-прокси:
socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080 - Геотаргетинг: добавьте
-country-USили-country-DE-city-berlinк имени пользователя - Sticky-сессия: добавьте
-session-abc123для закрепления IP
Подробнее о тарифах — на странице цен ProxyHat. Доступные локации — на странице локаций прокси. Use-case для веб-скрейпинга — веб-скрейпинг через ProxyHat, для SERP-трекинга — SERP-трекинг. Полная техническая документация — на docs.proxyhat.com.
Ключевые выводы
- Вес — ключевой ресурс: Binance даёт ~6000 веса/мин на IP. Отслеживайте
X-MBX-USED-WEIGHT-1Mи throttling'те до лимита.- Ротация IP распределяет вес: N IP-адресов = N × 6000 веса/мин. Резидентные прокси ProxyHat через
gate.proxyhat.com:8080— простое решение.- Sticky-сессии для пагинации: используйте
-session-IDдля бэкфилла klines, чтобы вес накапливался предсказуемо.- Геотаргетинг решает 451:
-country-USобходит геоблок Binance.com в США.- WebSocket для real-time: для live-orderbook и kline-streams используйте
wss://stream.binance.com, а не REST-поллинг.- Retry + backoff обязателен: обработка 429 с
Retry-Afterи экспоненциальная задержка предотвращают бан 418.
FAQ
Что такое скрейпинг Binance REST API через прокси?
Это сбор публичных рыночных данных (свечи, книга ордеров, тикеры) с эндпоинтов api.binance.com/api/v3/ через прокси-серверы, которые распределяют запросы по нескольким IP-адресам. Это позволяет обойти весовое ограничение ~6000 единиц в минуту на один IP и собирать данные в большем масштабе.
Зачем нужны прокси для скрейпинга Binance API?
Binance применяет весовые лимиты на IP. С одного адреса вы ограничены ~6000 веса/мин, а за превышение следует HTTP 429, а затем бан 418. Ротация резидентных прокси через gate.proxyhat.com:8080 распределяет вес по пулу IP, каждый из которых получает свой бюджет. Также геотаргетинг -country-US решает проблему HTTP 451 при доступе из США.
Какой тип прокси лучше для скрейпинга Binance?
Резидентные прокси оптимальны — они используют IP реальных интернет-провайдеров и реже блокируются антибот-системами. Дата-центр прокси быстрее, но чаще распознаются. Для бэкфилла истории с пагинацией используйте sticky-сессии (-session-ID), а для параллельного сбора по множеству символов — ротацию IP без сессии.
Как избежать блокировок при скрейпинге Binance API?
Отслеживайте заголовок X-MBX-USED-WEIGHT-1M и делайте паузу до достижения лимита 6000. Обрабатывайте HTTP 429 с заголовком Retry-After и применяйте экспоненциальную задержку. Ограничивайте параллелизм семафором (10–20 одновременных запросов). Не игнорируйте 429 — многократные нарушения приводят к бану 418 на срок от 2 минут до нескольких дней.
Когда использовать WebSocket вместо REST?
WebSocket (wss://stream.binance.com:9443/ws) эффективнее для непрерывного мониторинга real-time данных — orderbook updates, kline streams. Одно соединение получает push-обновления без расхода веса. REST через прокси оптимален для бэкфилфа истории, snapshot-ов и периодического сбора.






