Yasal Uyarı: Bu rehber yalnızca halka açık piyasa verisi (public market data) toplamak içindir. Binance API kullanım koşullarını inceleyin, weight limitlerine saygı gösterin ve resmi API erişiminin gerekli olduğu durumlarda onu tercih edin. Kişisel hesap verilerine veya kimlik doğrulama gerektiren endpoint'lere erişim bu kapsamın dışındadır.
Binance REST API'sini proxy ile tarama, quant geliştiriciler ve kripto veri mühendisleri için kritik bir beceridir. Mum verileri (klines), emir defteri (order book) ve 24 saatlik ticker verilerini büyük ölçekte toplarken Binance'in weight-based rate limiting sistemi hızla IP yasaklarına yol açar. Bu rehberde, gate.proxyhat.com üzerinden dönen residential proxy'ler kullanarak weight bütçesini IP'ler arası dağıtmayı, geo-engelleri aşmayı ve üretim-grade veri toplama hatları kurmayı öğreneceksiniz.
Binance REST API'sini Proxy ile Tarama: Endpoint Haritası ve Weight'ler
Binance'in public REST API'si api.binance.com üzerinde çalışır ve weight-based bir rate limiting sistemi kullanır. Her endpoint'in bir weight değeri vardır ve her IP için dakikada yaklaşık 6000 weight bütçesi sınırı vardır. X-MBX-USED-WEIGHT-1M response header'ı, son 1 dakika içinde tüketilen toplam weight'i gösterir.
İşte en sık kullanılan public market-data endpoint'leri ve weight değerleri:
| Endpoint | Weight (Tek Sembol) | Weight (Tümü) | Tipik Kullanım |
|---|---|---|---|
GET /api/v3/klines |
1-2 | — | OHLCV mum verileri |
GET /api/v3/depth (limit=100) |
5 | — | Emir defteri anlık görüntüsü |
GET /api/v3/depth (limit=1000) |
20 | — | Derin emir defteri |
GET /api/v3/ticker/24hr |
1 | 40 | 24 saatlik fiyat istatistikleri |
GET /api/v3/ticker/price |
1 | 2 | Güncel fiyat |
Örneğin, /api/v3/depth?symbol=BTCUSDT&limit=1000 endpoint'i her istekte 20 weight tüketir. 6000 weight bütçesiyle tek bir IP'den dakikada yalnızca 300 derin emir defteri isteği yapabilirsiniz. /api/v3/ticker/24hr'in tüm semboller için weight'i 40'tır, yani dakikada 150 istek yapabilirsiniz.
Weight-Based Rate Limiting ve IP Yasakları
Binance'in rate limiting sistemi üç aşamalıdır. Weight bütçesi aşıldığında sırasıyla şu yanıtlar gelir:
- HTTP 429 (Too Many Requests): Weight limiti aşıldığında döner.
Retry-Afterheader'ı, bekleme süresini saniye cinsinden belirtir. İstekleri yavaşlatmanız gerekir. - HTTP 418 (I'm a teapot): 429 yanıtlarını görmezden gelmeye devam ederseniz, IP otomatik olarak banlanır.
Retry-Afterheader'ı ban süresini belirtir (genellikle 2 dakika ile 3 gün arası). - HTTP 451 (Unavailable For Legal Reasons): Coğrafi engellemeler için kullanılır. Binance.com, ABD kullanıcılarını engeller ve bu yanıtı döndürür.
Detaylı bilgi için MDN HTTP 429 dokümantasyonuna bakabilirsiniz.
Bir senaryo düşünelim: 50 sembol için limit=1000 ile depth polling yapıyorsunuz. Her istek 20 weight, toplam dakikada 50 × 20 = 1000 weight. Bu tek IP ile sorun değil. Ancak 200 sembol için her 2 saniyede bir poll yaparsanız: 200 × 20 × 30 = 12000 weight/dakika — bütçenin iki katı. 2-3 dakika içinde 429, ardından 418 alırsınız.
X-MBX-USED-WEIGHT-1M header'ını her yanıttan okumak, weight bütçenizi gerçek zamanlı izlemenin tek güvenilir yoludur. Bu değeri 5000'e yaklaşırken görüyorsanız, throttle uygulamanız veya IP rotasyonuna geçmeniz gerekir.
Proxy Rotasyonu: Weight'i IP'ler Arası Dağıtma
Binance rate limiting'i IP başına uygular. Yani her rotating proxy IP'si ayrı bir 6000 weight bütçesine sahiptir. 10 IP'lik bir proxy havuzuyla teorik olarak dakikada 60.000 weight tüketebilirsiniz.
ProxyHat'ın residential proxy'leri gate.proxyhat.com:8080 (HTTP) ve :1080 (SOCKS5) üzerinden çalışır. Username parametre sistemi sayesinde her istekte farklı bir IP alabilir veya belirli bir ülke/şehir hedefleyebilirsiniz:
| Proxy Tipi | Binance Uygunluğu | Weight Dağıtımı | Geo-Targeting | Tespit Riski |
|---|---|---|---|---|
| Residential | ★★★★★ | Yüksek | Ülke / Şehir | Çok Düşük |
| Datacenter | ★★☆☆☆ | Orta | Ülke | Yüksek |
| Mobile | ★★★★☆ | Yüksek | Ülke | Düşük |
Residential proxy'ler, gerçek ISP IP'leri kullandığı için Binance'in anti-bot sistemlerine yakalanma riski en düşüktür. ProxyHat lokasyonları arasında 190+ ülke mevcuttur. Fiyatlandırma sayfasından ihtiyacınız olan proxy paketini seçebilirsiniz.
Binance.com vs Binance.US geo-split'i için -country-US flag'ini kullanarak ABD IP'leri elde edebilir ve HTTP 451 engelini aşabilirsiniz:
curl -x http://user-country-US:pass@gate.proxyhat.com:8080 \
"https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
Python ile Raw Proxy ve ProxyHat Yaklaşımı
İki yaklaşımı yan yana gösterelim: (1) manuel proxy listesi ile raw rotasyon, (2) ProxyHat username parametre sistemi ile otomatik rotasyon ve geo-targeting.
Önce raw proxy yaklaşımı — her istekte farklı bir proxy URL'si seçerek:
import requests
import itertools
import time
PROXIES = [
"http://user1:pass@proxy1.example.com:8080",
"http://user2:pass@proxy2.example.com:8080",
"http://user3:pass@proxy3.example.com:8080",
]
proxy_cycle = itertools.cycle(PROXIES)
def fetch_klines_raw(symbol, interval="1m", limit=500):
url = f"https://api.binance.com/api/v3/klines"
params = {"symbol": symbol, "interval": interval, "limit": limit}
proxy = next(proxy_cycle)
for attempt in range(5):
try:
resp = requests.get(url, params=params, proxies={"http": proxy, "https": proxy}, timeout=10)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 5))
time.sleep(retry_after)
continue
resp.raise_for_status()
used_weight = resp.headers.get("X-MBX-USED-WEIGHT-1M", "?")
print(f"[{symbol}] weight={used_weight} proxy={proxy.split('@')[1]}")
return resp.json()
except requests.RequestException as e:
wait = 2 ** attempt
print(f"Retry {attempt+1}: {e}, waiting {wait}s")
time.sleep(wait)
return None
data = fetch_klines_raw("BTCUSDT")
print(f"Got {len(data) if data else 0} candles")
Şimdi ProxyHat yaklaşımı — username parametreleri ile otomatik rotasyon ve geo-targeting. ProxyHat dokümantasyonunda detaylı username parametre referansı bulunur:
import requests
import time
import random
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
class ProxyHatClient:
"""ProxyHat gate.proxyhat.com için lightweight SDK wrapper."""
BASE = "http://gate.proxyhat.com:8080"
def __init__(self, username, password):
self.username = username
self.password = password
def proxy_url(self, country=None, city=None, session=None):
"""ProxyHat username parametre sistemi ile proxy URL üret."""
user = self.username
if country:
user += f"-country-{country}"
if city:
user += f"-city-{city}"
if session:
user += f"-session-{session}"
return f"http://{user}:{self.password}@gate.proxyhat.com:8080"
def get(self, url, params=None, country=None, session=None, max_retries=5):
"""Weight-aware istek with exponential backoff."""
proxy = self.proxy_url(country=country, session=session)
proxies = {"http": proxy, "https": proxy}
for attempt in range(max_retries):
try:
resp = requests.get(url, params=params, proxies=proxies, timeout=15)
# Weight tracking
weight = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight:
w = int(weight)
if w > 5000:
logging.warning(f"Weight high: {w}/6000, throttling...")
time.sleep(2)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 10))
logging.warning(f"429 received, waiting {retry_after}s")
time.sleep(retry_after)
# Yeni IP ile tekrar dene
proxy = self.proxy_url(country=country)
proxies = {"http": proxy, "https": proxy}
continue
if resp.status_code == 451:
logging.error("Geo-blocked (451), switching to US proxy")
proxy = self.proxy_url(country="US")
proxies = {"http": proxy, "https": proxy}
continue
resp.raise_for_status()
logging.info(f"OK weight={weight}")
return resp.json()
except requests.RequestException as e:
wait = min(2 ** attempt + random.uniform(0, 1), 30)
logging.error(f"Attempt {attempt+1} failed: {e}, waiting {wait:.1f}s")
time.sleep(wait)
return None
client = ProxyHatClient("user", "pass")
data = client.get(
"https://api.binance.com/api/v3/klines",
params={"symbol": "ETHUSDT", "interval": "5m", "limit": 1000}
)
print(f"Got {len(data) if data else 0} candles")
httpx ile Async Concurrency ve Weight-Aware Throttling
Yüksek hacimli veri toplama için async/await ile concurrency şarttır. httpx ve asyncio.Semaphore kullanarak eşzamanlı istek sayısını kontrol altında tutabilirsiniz:
import httpx
import asyncio
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
PROXYHAT_HTTP = "http://user:pass@gate.proxyhat.com:8080"
MAX_CONCURRENT = 10
WEIGHT_THRESHOLD = 5000
async def fetch_ticker(client, symbol, sem):
async with sem:
url = f"https://api.binance.com/api/v3/ticker/24hr?symbol={symbol}"
proxy = f"http://user-country-US:pass@gate.proxyhat.com:8080"
for attempt in range(5):
try:
resp = await client.get(url, proxy=proxy, timeout=15)
weight = resp.headers.get("X-MBX-USED-WEIGHT-1M")
if weight and int(weight) > WEIGHT_THRESHOLD:
logging.warning(f"Weight {weight}, pausing 3s")
await asyncio.sleep(3)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 10))
logging.warning(f"429 for {symbol}, wait {retry_after}s")
await asyncio.sleep(retry_after)
continue
resp.raise_for_status()
data = resp.json()
logging.info(f"{symbol}: ${float(data['lastPrice']):.2f} w={weight}")
return data
except Exception as e:
wait = min(2 ** attempt, 20)
logging.error(f"{symbol} attempt {attempt+1}: {e}, wait {wait}s")
await asyncio.sleep(wait)
return None
async def main():
symbols = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT", "XRPUSDT",
"ADAUSDT", "DOGEUSDT", "AVAXUSDT", "DOTUSDT", "MATICUSDT"]
sem = asyncio.Semaphore(MAX_CONCURRENT)
async with httpx.AsyncClient() as client:
results = await asyncio.gather(
*[fetch_ticker(client, s, sem) for s in symbols]
)
valid = [r for r in results if r]
print(f"Collected {len(valid)}/{len(symbols)} tickers")
asyncio.run(main())
Sticky Session ile Klines Backfill
Tarihsel mum verisi backfill yaparken, binlerce klines isteği arka arkaya gönderilir. Her istekte IP değiştirmek yerine, -session-abc123 flag'i ile sticky session kullanarak aynı IP'de kalabilirsiniz. Bu, weight bütçesini tek IP'de biriktirirken tutarlı bir bağlantı sağlar ve pagination sırasında IP değişimi kaynaklı kesintileri önler.
import requests
import time
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
def backfill_klines(symbol, interval, start_time, end_time, session_id="backfill-btc-001"):
"""Sticky session ile tarihsel klines backfill.
Aynı session ID = aynı IP = aynı weight bütçesi.
Weight 5000'i geçince yeni session (yeni IP) ile devam et.
"""
url = "https://api.binance.com/api/v3/klines"
base_proxy = f"http://user-session-{session_id}:pass@gate.proxyhat.com:8080"
proxies = {"http": base_proxy, "https": base_proxy}
all_candles = []
current = start_time
session_counter = 0
while current < end_time:
params = {
"symbol": symbol,
"interval": interval,
"startTime": current,
"endTime": end_time,
"limit": 1000,
}
for attempt in range(5):
try:
resp = requests.get(url, params=params, proxies=proxies, timeout=15)
weight = int(resp.headers.get("X-MBX-USED-WEIGHT-1M", 0))
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 10))
logging.warning(f"429, waiting {retry_after}s")
time.sleep(retry_after)
continue
resp.raise_for_status()
candles = resp.json()
if not candles:
logging.info("No more data")
return all_candles
all_candles.extend(candles)
current = candles[-1][0] + 1 # Open time + 1ms
logging.info(f"{symbol} fetched {len(candles)} candles, "
f"total={len(all_candles)}, weight={weight}/6000")
# Weight 5000'i geçince yeni session = yeni IP
if weight > 5000:
session_counter += 1
new_session = f"backfill-{symbol}-{session_counter:03d}"
base_proxy = f"http://user-session-{new_session}:pass@gate.proxyhat.com:8080"
proxies = {"http": base_proxy, "https": base_proxy}
logging.info(f"Switching to new session: {new_session}")
time.sleep(1) # Brief cooldown
break
except requests.RequestException as e:
wait = min(2 ** attempt, 30)
logging.error(f"Attempt {attempt+1}: {e}, wait {wait}s")
time.sleep(wait)
else:
logging.error(f"Failed at startTime={current}")
break
time.sleep(0.1) # Rate guard
return all_candles
# 2024 Ocak ayı BTCUSDT 1m klines backfill
import datetime
t_start = int(datetime.datetime(2024, 1, 1).timestamp() * 1000)
t_end = int(datetime.datetime(2024, 1, 2).timestamp() * 1000)
candles = backfill_klines("BTCUSDT", "1m", t_start, t_end)
print(f"Total candles: {len(candles)}")
Node.js (axios) ile Concurrency
JavaScript/TypeScript ekosisteminde axios ile proxy-aware istekler gönderebilirsiniz. p-limit veya manuel semaphore ile concurrency'i kontrol altında tutun:
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
const PROXYHAT_BASE = 'http://user:pass@gate.proxyhat.com:8080';
const MAX_CONCURRENT = 8;
let active = 0;
const queue = [];
function buildProxy(country, session) {
let user = 'user';
if (country) user += `-country-${country}`;
if (session) user += `-session-${session}`;
return `http://${user}:pass@gate.proxyhat.com:8080`;
}
async function fetchDepth(symbol, limit = 100) {
while (active >= MAX_CONCURRENT) {
await new Promise(r => queue.push(r));
}
active++;
const proxyUrl = buildProxy('US');
const agent = new HttpsProxyAgent.HttpsProxyAgent(proxyUrl);
const url = `https://api.binance.com/api/v3/depth?symbol=${symbol}&limit=${limit}`;
for (let attempt = 0; attempt < 5; attempt++) {
try {
const resp = await axios.get(url, {
httpsAgent: agent,
timeout: 15000,
});
const weight = resp.headers['x-mbx-used-weight-1m'];
if (weight && parseInt(weight) > 5000) {
console.warn(`Weight ${weight}/6000, throttling 2s`);
await new Promise(r => setTimeout(r, 2000));
}
console.log(`${symbol}: bids=${resp.data.bids.length}, weight=${weight}`);
active--;
if (queue.length) queue.shift()();
return resp.data;
} catch (err) {
if (err.response && err.response.status === 429) {
const retryAfter = parseInt(err.response.headers['retry-after'] || '10');
console.warn(`429 for ${symbol}, wait ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
const wait = Math.min(2 ** attempt, 30);
console.error(`${symbol} attempt ${attempt+1}: ${err.message}, wait ${wait}s`);
await new Promise(r => setTimeout(r, wait * 1000));
}
}
active--;
if (queue.length) queue.shift()();
return null;
}
async function main() {
const symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT',
'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT'];
const results = await Promise.all(symbols.map(s => fetchDepth(s)));
console.log(`Collected ${results.filter(r => r).length}/${symbols.length} order books`);
}
main();
WebSocket vs REST Polling: Ne Zaman Hangisi?
Binance, public market data için wss://stream.binance.com:9443 üzerinden WebSocket akışları sunar. WebSocket, sürekli güncellenen veri için REST polling'den çok daha verimlidir:
| Özellik | REST Polling | WebSocket Stream |
|---|---|---|
| Weight Tüketimi | Her istek weight harcar | Sıfır weight (bağlantı başına 5 weight, sonra limitsiz) |
| Latency | d>100-500ms (poll aralığına bağlı)10-50ms (real-time push) | |
| Veri Bütünlüğü | Anlık görüntü (snapshot) | Artımlı güncellemeler (diff) |
| Uygun Use Case | Tarihsel backfill, tek seferlik sorgu | Canlı emir defteri, real-time fiyat |
WebSocket kullanırken proxy gerekip gerekmediği, bağlantı sayısına ve coğrafi konuma bağlıdır. Tek bir WebSocket bağlantısı weight tüketmez, ancak Binance bağlantı başına 5 weight uygular ve maksimum bağlantı sayısı sınırlıdır. ABD'den erişim için proxy yine gerekli olabilir:
import websockets
import asyncio
import json
async def stream_klines(symbol, interval="1m"):
"""Binance WebSocket kline stream via ProxyHat SOCKS5."""
# SOCKS5 proxy for WebSocket
ws_url = f"wss://stream.binance.com:9443/ws/{symbol.lower()}@kline_{interval}"
# Note: websockets kütüphanesi SOCKS5 proxy için ek paket gerektirir
# Basitlik için doğrudan bağlantı örneği:
async with websockets.connect(ws_url) as ws:
print(f"Connected to {symbol} {interval} stream")
count = 0
async for msg in ws:
data = json.loads(msg)
kline = data['k']
print(f"{symbol} O={kline['o']} H={kline['h']} L={kline['l']} C={kline['c']} closed={kline['x']}")
count += 1
if count >= 100:
break
asyncio.run(stream_klines("BTCUSDT", "1m"))
Canlı veri için WebSocket, tarihsel veri için REST + proxy rotasyonu en iyi kombinasyondur.
Yaygın Hatalar ve Production Tips
- X-MBX-USED-WEIGHT-1M'i okumamak: En sık hata. Weight'i izlemeden polling yaparsanız, 429 ve ardından 418 gelir. Her yanıttan bu header'ı okuyun ve 5000 threshold'unda throttle uygulayın.
- Retry-After'ı görmezden gelmek: 429 aldığınızda
Retry-Afterheader'ı size kaç saniye beklemeniz gerektiğini söyler. Bunu görmezden gelip yeni istek göndermek, 418 ban'ını tetikler. - Tüm semboller için /api/v3/ticker/24hr çağırmak: Tek sembol için weight 1, tüm semboller için weight 40. Sadece ihtiyacınız olan sembolleri sorgulayın.
- Datacenter proxy kullanmak: Binance, datacenter IP'lerini tespit etmede agresiftir. Residential proxy'ler çok daha güvenlidir. Web scraping use case sayfamızda proxy tipi seçimi hakkında daha fazla bilgi bulabilirsiniz.
- Concurrency sınırı koymamak: 100+ concurrent request açmak, hem yerel makineyi hem de proxy gateway'i zorlar.
Semaphoreveyap-limitile 8-16 arası concurrency tutun. - Circuit breaker eksikliği: Üst üste 3-5 hata alırsanız, istekleri durdurun ve bir cooldown uygulayın. Sonsuz retry, IP ban süresini uzatabilir.
Key Takeaways
- Binance rate limiting IP başına 6000 weight/dakika'dır.
X-MBX-USED-WEIGHT-1Mheader'ını her yanıttan okuyun.- Rotating residential proxy'ler weight'i IP'ler arası dağıtır — 10 IP ile 60.000 weight/dakika kapasitesi.
gate.proxyhat.com:8080(HTTP) veya:1080(SOCKS5) üzerinden ProxyHat username parametreleri ile ülke hedefleme (-country-US) ve sticky session (-session-abc123) yapın.- 429 = yavaşlayın, 418 = banlandınız, 451 = geo-engel. Her durumda
Retry-Afterheader'ına saygı gösterin.- Canlı veri için WebSocket, tarihsel backfill için REST + proxy rotasyonu kullanın.
- Exponential backoff, weight-aware throttling ve concurrency limit'leri production'da zorunludur.
Daha fazla scraping tekniği için SERP tracking use case sayfamızı inceleyebilir veya ProxyHat fiyatlandırması ile ihtiyacınız olan proxy paketini seçebilirsiniz.






