Se stai costruendo un bot di trading o un sistema di monitoraggio dei dati di mercato su Bybit, prima o poi ti scontri con i limiti di frequenza IP e i blocchi geografici di CloudFront. In questa guida spieghiamo come scrapeare la Bybit V5 Market API con proxy rotanti, coprendo gli endpoint pubblici /v5/market/, il modello di rate limiting di Bybit e come i proxy residenziali ProxyHat ripristinano l'accesso da paesi consentiti.
Avviso legale: gli endpoint pubblici di mercato di Bybit restituiscono dati di dominio pubblico, ma devi rispettare i Termini di Servizio (ToS) di Bybit e il file robots.txt. Non fare scraping di dati privati o autenticati senza autorizzazione. Consulta i Termini di Servizio di Bybit prima di avviare qualsiasi progetto.
Come Scrapeare la Bybit V5 Market API con Proxy Rotanti: panoramica
La V5 Market API di Bybit è l'interfaccia REST pubblica per orderbook, ticker, candele (kline) e trade recenti. È un'API unificata che copre spot, derivati lineari, opzioni e futures inversi tramite il parametro category. A differenza delle API private, non richiede chiavi API, ma è soggetta a rigidi rate limit per IP e a restrizioni geografiche.
Scrapearla in modo affidabile richiede tre cose: capire la struttura della risposta retCode/retMsg, gestire i limiti di frequenza per-endpoint e usare proxy rotanti per distribuire le richieste su più IP. I proxy residenziali di ProxyHat (accessibili via gate.proxyhat.com:8080 per HTTP e :1080 per SOCKS5) sono particolarmente efficaci perché usano IP ISP reali, riducendo il rischio di blocchi CloudFront.
Endpoint pubblici sotto /v5/market/
La V5 Market API espone quattro endpoint pubblici principali. Ognuno accetta il parametro category (spot, linear, inverse, option) e restituisce un envelope JSON standard con i campi retCode e retMsg.
| Endpoint | Scopo | Parametri chiave | Rate limit tipico |
|---|---|---|---|
GET /v5/market/orderbook | Snapshot dell'orderbook | category, symbol, limit (depth) | ~50 req/s per IP |
GET /v5/market/tickers | Ticker di mercato (prezzo, volume, funding) | category, symbol (opzionale) | ~10 req/s per IP |
GET /v5/market/kline | Candele storiche | category, symbol, interval, start, end, limit | ~10 req/s per IP |
GET /v5/market/recent-trade | Trade recenti | category, symbol, limit | ~20 req/s per IP |
L'envelope di risposta retCode/retMsg
Ogni risposta V5 segue lo stesso schema:
{
"retCode": 0,
"retMsg": "OK",
"result": { ... },
"time": 1700000000000
}
Un retCode pari a 0 indica successo. Qualsiasi altro valore indica un errore: 10001 per parametri invalidi, 10010 per rate limit, 403- stile per blocchi regionali. Il campo retMsg fornisce una descrizione leggibile. Il tuo codice deve sempre controllare retCode prima di processare result.
Esempio base con curl (senza proxy)
curl -s 'https://api.bybit.com/v5/market/orderbook?category=spot&symbol=BTCUSDT&limit=50' | jq .
Rate limit IP e finestra di ban di 10 minuti
Bybit applica rate limit per-endpoint su base IP. Quando superi il limite, ricevi un HTTP 403 o un retCode di errore. Il problema critico è che Bybit può mettere al bando un IP per circa 10 minuti se i burst sono troppo aggressivi, specialmente su /v5/market/orderbook che ha un limite più alto (~50 req/s) ma è il più colpito dai bot.
Secondo la documentazione ufficiale sui rate limit di Bybit V5, i limiti sono dinamici e possono cambiare in base al carico. Una strategia di scraping robusta deve:
- Rispettare un budget per-endpoint inferiore al limite dichiarato (es. 80% del limite).
- Usare backoff esponenziale sui 403/429.
- Rotare gli IP per distribuire il carico e ridurre il rischio di ban.
Geo-restrizioni: blocchi 403 e CloudFront
Bybit blocca l'accesso da Stati Uniti, Regno Unito e altre regioni regolamentate. Se il tuo IP è in una zona bloccata, ricevi un 403 da CloudFront o una pagina di blocco invece del JSON. Questo è un problema serio per sviluppatori in US/UK che vogliono accedere ai dati di mercato pubblici.
I proxy residenziali ProxyHat risolvono questo problema instradando le richieste tramite IP ISP di paesi consentiti (es. Germania, Singapore, Giappone). Usando il geo-targeting nel username con il formato -country-XX, puoi forzare l'uscita da un paese specifico:
curl -x http://user-country-DE:pass@gate.proxyhat.com:8080 \
'https://api.bybit.com/v5/market/tickers?category=spot'
Consulta la pagina delle locazioni ProxyHat per la lista completa di paesi supportati.
Setup ProxyHat: proxy raw vs SDK
ProxyHat espone un gateway HTTP su gate.proxyhat.com:8080 e SOCKS5 su :1080. Le opzioni di rotazione e geo-targeting vanno nel username. Ecco i formati:
- Rotazione per-request:
http://USERNAME:PASSWORD@gate.proxyhat.com:8080 - Geo-targeting paese:
http://USERNAME-country-DE:PASSWORD@gate.proxyhat.com:8080 - Sticky session:
http://USERNAME-session-abc123:PASSWORD@gate.proxyhat.com:8080 - SOCKS5:
socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080
Per dettagli completi, consulta la documentazione ProxyHat.
Esempio Node.js (axios): rotazione IP su lista di symbol
Questo esempio usa il proxy raw di ProxyHat per scrapeare i ticker di una lista di symbol, con rotazione IP per-request, retry con backoff e budget di rate.
const axios = require('axios');
const PROXY_USER = 'user';
const PROXY_PASS = 'pass';
const PROXY_HOST = 'gate.proxyhat.com';
const PROXY_PORT = 8080;
const symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'XRPUSDT', 'DOGEUSDT'];
const BASE_URL = 'https://api.bybit.com';
// Genera un session ID unico per rotazione per-request
function randomSession() {
return Math.random().toString(36).substring(2, 12);
}
async function fetchTicker(symbol, attempt = 1) {
const sessionId = randomSession();
const proxyUrl = `http://${PROXY_USER}-country-DE-session-${sessionId}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}`;
try {
const res = await axios.get(`${BASE_URL}/v5/market/tickers`, {
params: { category: 'spot', symbol },
proxy: { host: PROXY_HOST, port: PROXY_PORT, auth: { username: `${PROXY_USER}-country-DE-session-${sessionId}`, password: PROXY_PASS } },
timeout: 10000,
});
if (res.data.retCode !== 0) {
throw new Error(`Bybit error ${res.data.retCode}: ${res.data.retMsg}`);
}
return res.data.result;
} catch (err) {
if (attempt < 4) {
const backoff = Math.pow(2, attempt) * 500;
console.error(`Retry ${symbol} in ${backoff}ms: ${err.message}`);
await new Promise(r => setTimeout(r, backoff));
return fetchTicker(symbol, attempt + 1);
}
throw err;
}
}
async function main() {
const results = [];
// Rate budget: 8 req/s (sotto il limite ~10 req/s)
for (const sym of symbols) {
const data = await fetchTicker(sym);
results.push(data);
await new Promise(r => setTimeout(r, 125)); // 125ms = 8 req/s
}
console.log(JSON.stringify(results, null, 2));
}
main().catch(console.error);
Versione con ProxyHat SDK (Node.js)
const { ProxyHatClient } = require('@proxyhat/sdk');
const axios = require('axios');
const ph = new ProxyHatClient({
username: 'user',
password: 'pass',
country: 'DE',
rotate: true, // rotazione per-request
});
async function fetchTickerSDK(symbol, attempt = 1) {
const proxyConfig = await ph.getProxyConfig(); // {host, port, auth}
try {
const res = await axios.get('https://api.bybit.com/v5/market/tickers', {
params: { category: 'spot', symbol },
proxy: { host: proxyConfig.host, port: proxyConfig.port, auth: proxyConfig.auth },
timeout: 10000,
});
if (res.data.retCode !== 0) {
throw new Error(`Bybit ${res.data.retCode}: ${res.data.retMsg}`);
}
return res.data.result;
} catch (err) {
if (attempt < 4) {
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 500));
return fetchTickerSDK(symbol, attempt + 1);
}
throw err;
}
}
async function main() {
const symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT'];
for (const s of symbols) {
const r = await fetchTickerSDK(s);
console.log(s, r.list[0].lastPrice);
await new Promise(r => setTimeout(r, 125));
}
}
main();
Esempio Python: orderbook con proxy rotanti
import requests
import time
import random
import string
PROXY_USER = 'user'
PROXY_PASS = 'pass'
PROXY_HOST = 'gate.proxyhat.com'
PROXY_PORT = 8080
BASE_URL = 'https://api.bybit.com'
def random_session():
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
def fetch_orderbook(symbol, category='linear', limit=50, attempt=1):
session_id = random_session()
proxy_url = f'http://{PROXY_USER}-country-DE-session-{session_id}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}'
proxies = {'http': proxy_url, 'https': proxy_url}
try:
res = requests.get(
f'{BASE_URL}/v5/market/orderbook',
params={'category': category, 'symbol': symbol, 'limit': limit},
proxies=proxies,
timeout=10,
)
res.raise_for_status()
data = res.json()
if data['retCode'] != 0:
raise Exception(f"Bybit error {data['retCode']}: {data['retMsg']}")
return data['result']
except Exception as e:
if attempt < 4:
backoff = 2 ** attempt * 0.5
print(f'Retry {symbol} in {backoff}s: {e}')
time.sleep(backoff)
return fetch_orderbook(symbol, category, limit, attempt + 1)
raise
def main():
symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT']
for sym in symbols:
ob = fetch_orderbook(sym)
print(f'{sym}: bid={ob["b"][0][0]}, ask={ob["a"][0][0]}')
time.sleep(0.05) # 20 req/s budget, sotto il limite ~50
if __name__ == '__main__':
main()
Versione Python con ProxyHat SDK
from proxyhat import ProxyHatClient
import requests
import time
ph = ProxyHatClient(username='user', password='pass', country='DE', rotate=True)
def fetch_orderbook_sdk(symbol, category='linear', limit=50, attempt=1):
proxy_cfg = ph.get_proxy_config() # {'host':..., 'port':..., 'auth':...}
proxy_url = f"http://{proxy_cfg['auth']['username']}:{proxy_cfg['auth']['password']}@{proxy_cfg['host']}:{proxy_cfg['port']}"
proxies = {'http': proxy_url, 'https': proxy_url}
try:
res = requests.get(
'https://api.bybit.com/v5/market/orderbook',
params={'category': category, 'symbol': symbol, 'limit': limit},
proxies=proxies,
timeout=10,
)
data = res.json()
if data['retCode'] != 0:
raise Exception(f"Bybit {data['retCode']}: {data['retMsg']}")
return data['result']
except Exception as e:
if attempt < 4:
time.sleep(2 ** attempt * 0.5)
return fetch_orderbook_sdk(symbol, category, limit, attempt + 1)
raise
for sym in ['BTCUSDT', 'ETHUSDT']:
ob = fetch_orderbook_sdk(sym)
print(sym, ob['b'][0][0], ob['a'][0][0])
time.sleep(0.05)
Sticky session per paginazione kline
Quando scarichi dati kline storici in pagine successive, conviene mantenere lo stesso IP per tutta la sessione per evitare di saltare intervalli a causa di rotazioni. Usa il flag -session-abc123 nel username ProxyHat.
import requests
import time
PROXY_USER = 'user'
PROXY_PASS = 'pass'
SESSION_ID = 'kline-btc-001'
proxy_url = f'http://{PROXY_USER}-country-DE-session-{SESSION_ID}:{PROXY_PASS}@gate.proxyhat.com:8080'
proxies = {'http': proxy_url, 'https': proxy_url}
def fetch_kline(symbol, interval='60', start=None, end=None, limit=200):
params = {'category': 'linear', 'symbol': symbol, 'interval': interval, 'limit': limit}
if start:
params['start'] = start
if end:
params['end'] = end
res = requests.get(
'https://api.bybit.com/v5/market/kline',
params=params,
proxies=proxies,
timeout=10,
)
data = res.json()
if data['retCode'] != 0:
raise Exception(f"Bybit {data['retCode']}: {data['retMsg']}")
return data['result']
def scrape_all_klines(symbol, interval='60', total_candles=1000):
all_candles = []
batch = 200
end = None
while len(all_candles) < total_candles:
result = fetch_kline(symbol, interval=interval, end=end, limit=batch)
candles = result['list']
if not candles:
break
all_candles.extend(candles)
# Usa il timestamp più vecchio come nuovo 'end'
end = int(candles[-1][0]) - 1
time.sleep(0.1) # 10 req/s budget
return all_candles
candles = scrape_all_klines('BTCUSDT', interval='60', total_candles=1000)
print(f'Recuperate {len(candles)} candele')
REST snapshot vs WebSocket orderbook.50
L'endpoint REST /v5/market/orderbook fornisce uno snapshot istantaneo, ma per dati live ad alta frequenza conviene usare il WebSocket pubblico di Bybit con il topic orderbook.50.{symbol}, che invia aggiornamenti incrementali a ~50ms di latenza.
| Aspetto | REST orderbook | WebSocket orderbook.50 |
|---|---|---|
| Latenza | ~100-300ms per richiesta | ~50ms push |
| Rate limit | ~50 req/s per IP | 1 connessione, push continuo |
| Uso tipico | Snapshot periodici, backfill | Feed live per bot HFT |
| Proxy necessario? | Sì, per geo-sbypass | Sì, anche WS passa per proxy |
Per il WebSocket, puoi configurare il proxy a livello di client (es. HttpsProxyAgent in Node.js o websockets con proxy in Python). Il gateway ProxyHat supporta connessioni WebSocket tunneling via HTTP CONNECT.
Errori comuni e edge case
- Non controllare retCode: una risposta HTTP 200 con
retCode != 0è comunque un errore. - Burst su orderbook: anche se il limite è ~50 req/s, burst continui possono innescare ban di 10 minuti.
- Geo-blocco silenzioso: CloudFront può restituire 403 senza un corpo JSON leggibile. Controlla il Content-Type.
- Parametro category mancante: senza
category, l'API restituisce errore. Ogni endpoint lo richiede. - Timestamp in ms: Bybit usa timestamp in millisecondi, non secondi. Per paginazione kline, usa ms.
Configurazione ProxyHat e link utili
Per iniziare con ProxyHat, consulta la pagina dei prezzi e i casi d'uso per il web scraping e il SERP tracking. Per la lista dei paesi supportati per il geo-targeting, visita locazioni.
Punti chiave (Key Takeaways)
- La V5 Market API di Bybit usa un envelope
retCode/retMsg: controlla sempreretCode == 0prima di processareresult. - I rate limit sono per-endpoint e per-IP: l'orderbook ha il limite più alto (~50 req/s) ma i burst possono causare ban di ~10 minuti.
- Bybit blocca US/UK e altre regioni via CloudFront: i proxy residenziali ProxyHat con geo-targeting
-country-XXripristinano l'accesso. - Usa sticky session (
-session-abc123) per operazioni che richiedono stato (paginazione kline) e rotazione per-request per scraping distribuito. - Per dati live ad alta frequenza, preferisci il WebSocket
orderbook.50al REST polling. - Mantieni un budget di rate dell'80% del limite dichiarato e usa backoff esponenziale sui 403/429.
FAQ
Come si scrapearla la Bybit V5 Market API con proxy rotanti?
Si inviano richieste agli endpoint pubblici /v5/market/ (orderbook, tickers, kline, recent-trade) tramite un gateway proxy rotante come ProxyHat, che assegna un IP residenziale diverso per ogni richiesta o sessione. Il proxy va configurato nel client HTTP (axios, requests) usando l'URL http://USERNAME:PASSWORD@gate.proxyhat.com:8080, con geo-targeting via -country-XX per evitare blocchi regionali.
Perché i proxy rotanti importano per il scraping della Bybit V5 Market API?
Bybit applica rate limit rigorosi per IP e ban di circa 10 minuti per burst eccessivi. Senza rotazione, un singolo IP viene rapidamente bloccato, interrompendo la raccolta dati. I proxy rotanti distribuiscono le richieste su centinaia di IP residenziali, riducendo il carico per-IP sotto la soglia di rate limit e mantenendo throughput elevato.
Quale tipo di proxy funziona meglio per scrapearla Bybit V5 Market API?
I proxy residenziali sono i più efficaci perché usano IP ISP reali, meno inclini a blocchi CloudFront e filtri anti-bot rispetto ai datacenter. Per il geo-sbypass (US/UK), il geo-targeting paese (-country-DE) è essenziale. I proxy datacenter possono funzionare per carichi leggeri ma hanno tassi di blocco più alti su Bybit.
Come evitare i blocchi nello scraping della Bybit V5 Market API?
Mantieni un budget di rate sotto il limite dichiarato (es. 80%), usa backoff esponenziale sui 403/429, ruota gli IP per-request con ProxyHat, implementa sticky session per operazioni con stato, e monitora il campo retCode per rilevare errori di rate limit prima che diventino ban. Evita burst continui su /v5/market/orderbook, l'endpoint più sensibile.
Qual è la differenza tra REST orderbook e WebSocket orderbook.50?
L'endpoint REST /v5/market/orderbook fornisce snapshot su richiesta con latenza ~100-300ms, ideale per backfill e polling periodico. Il WebSocket orderbook.50.{symbol} invia aggiornamenti incrementali push a ~50ms, ideale per bot live e HFT. Per dati real-time, il WebSocket è superiore; per snapshot storici, il REST è più semplice.






