Zastrzeżenie prawne: Publiczne endpointy /v5/market/ są dostępne bez uwierzytelnienia, ale musisz przestrzegać warunków korzystania z API Bybit oraz lokalnych przepisów. Ten przewodnik dotyczy wyłącznie publicznych danych rynkowych i nie zachęca do naruszania ToS.
Jeśli tworzysz bota handlowego lub potok danych rynkowych, prawdopodobnie spotkałeś się z błędem 403 z api.bybit.com/v5/market/orderbook po kilku minutach intensywnego scrapowania. Bybit V5 Market API jest szybkie i dobrze udokumentowane, ale ma limity IP, geo-blokady i CDN CloudFront, które mogą zablokować Twoje żądania. W tym przewodniku pokazujemy, jak scrapować API rynku Bybit V5 z rotującymi proxy — z gotowymi przykładami w Node.js i Python, obsługą limitów, geo-blokad i sesji sticky.
Dlaczego scrapowanie API rynku Bybit V5 z rotującymi proxy wymaga proxy
Bybit V5 Market API udostępnia publiczne dane rynkowe pod api.bybit.com/v5/market/. Wszystkie odpowiedzi mają spójną kopertę:
{
"retCode": 0,
"retMsg": "OK",
"result": { ... },
"time": 1700000000000
}
Gdy retCode wynosi 0, żądanie się powiodło. Każda inna wartość — np. 10001 (parametr wymagany) lub 10010 (przekroczony limit) — oznacza błąd, a retMsg zawiera opis. Musisz sprawdzać retCode przy każdym żądaniu, inaczej Twoja aplikacja cicho przetwarza puste dane.
Główne endpointy, które scrapujemy:
- /v5/market/orderbook — książka zleceń; parametr
categoryprzyjmujespotlublinear, alimit(głębokość) od 1 do 200. - /v5/market/tickers — bieżące tickery dla kategorii; zwraca listę z lastPrice, high24h, volume24h.
- /v5/market/kline — świece OHLCV; parametr
intervalod 1 do D,limitdo 1000, paginacja przezcursor. - /v5/market/recent-trade — ostatnie transakcje;
category+symbol+ opcjonalnylimit.
Parametr category jest kluczowy: spot to rynek spot, linear to perp/futures USDT, a inverse to kontrakty odwrotne. Mieszanie kategorii to częsty błąd.
Limity IP w Bybit V5 i dlaczego orderbook jest wrażliwy
Bybit stosuje limity per-endpoint i per-IP. Według dokumentacji limitów Bybit V5, endpoint /v5/market/orderbook ma osobny budżet, a /v5/market/tickers inny. Kiedy przekroczysz limit, otrzymujesz HTTP 403 z retCode wskazującym rate limit, a Twoje IP może trafić na listę banów na około 10 minut.
Orderbook jest szczególnie wrażliwy, ponieważ typowo odpytujesz go z wysoką częstotliwością (np. co 100–200 ms na symbol). Przy 10 symbolach to 50–100 żądań/s z jednego IP — wystarczy, by szybko wyczerpać budżet. Rotujące proxy rozpraszają te żądania na wiele adresów IP, więc każdy IP utrzymuje się poniżej progu.
Typowe objawy wyczerpania limitu:
- HTTP 403 z
retMsgzawierającym "rate limit" lub "too many requests". - Nagły spadek sukcesu z 99% do 0% na wszystkich żądaniach.
- Ban trwający ~10 minut, po którym ruch wraca bez interwencji.
Geo-blokady Bybit i CloudFront
Bybit ogranicza dostęp z niektórych regionów (m.in. USA, UK, części UE) ze względów regulacyjnych. Żądania z zablokowanych IP otrzymują HTTP 403 lub są odrzucane przez CDN CloudFront przed dotarciem do API. Jeśli Twój serwer datacenter znajduje się w zablokowanym regionie, nie dostaniesz się do /v5/market/ bez proxy z dozwolonego kraju.
Rozwiązaniem są proxy residential z geo-targetingiem. ProxyHat pozwala wskazać kraj w nazwie użytkownika, np. user-country-DE kieruje ruch przez niemiecki adres IP ISP. To wystarczy, aby Bybit widział żądanie z dozwolonego regionu.
Konfiguracja ProxyHat: HTTP 8080 i SOCKS5 1080
ProxyHat używa jednej bramy dla wszystkich typów proxy. Parametry geo i sesji przekazujesz w nazwie użytkownika.
| Typ | URL proxy | Użycie |
|---|---|---|
| HTTP | http://user-country-DE:pass@gate.proxyhat.com:8080 | Domyślny, rotacja per-żądanie |
| SOCKS5 | socks5://user-country-DE:pass@gate.proxyhat.com:1080 | Niskolatencyjny, dobre dla WebSocket |
| Sticky HTTP | http://user-session-abc123-country-DE:pass@gate.proxyhat.com:8080 | Paginacja kline, spójny IP |
Sprawdź dostępne lokalizacje na stronie /pl/locations i cennik na /pl/pricing. Więcej o zastosowaniach scrapowania znajdziesz w przypadku użycia web scraping i SERP tracking.
Przykład 1: curl z surowym proxy
Najprostszy test — orderbook spot BTCUSDT przez niemiecki IP:
curl -x http://user-country-DE:pass@gate.proxyhat.com:8080 \
"https://api.bybit.com/v5/market/orderbook?category=spot&symbol=BTCUSDT&limit=200"
Odpowiedź zawiera result.b (bids) i result.a (asks) jako listę par [cena, wielkość]. Jeśli retCode ≠ 0, sprawdź retMsg.
Przykład 2: Node.js (axios) z ProxyHat SDK — rotacja IP po liście symboli
const axios = require('axios');
const pLimit = require('p-limit');
const PROXYHAT = {
host: 'gate.proxyhat.com',
port: 8080,
auth: { username: 'user-country-DE', password: 'pass' }
};
const limit = pLimit(5); // max 5 współbieżnych
async function scrapeOrderbooks(symbols) {
const results = [];
await Promise.all(symbols.map(sym =>
limit(async () => {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const { data } = await axios.get(
'https://api.bybit.com/v5/market/orderbook',
{
params: { category: 'spot', symbol: sym, limit: 50 },
proxy: PROXYHAT,
timeout: 8000
}
);
if (data.retCode !== 0) throw new Error(data.retMsg);
results.push({ symbol: sym, bids: data.result.b.slice(0, 5) });
return;
} catch (err) {
if (attempt === 2) {
results.push({ symbol: sym, error: err.message });
return;
}
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
}
}
})
));
return results;
}
scrapeOrderbooks(['BTCUSDT', 'ETHUSDT', 'SOLUSDT']).then(console.log);
Każde żądanie przechodzi przez bramę ProxyHat. Parametr country-DE w nazwie użytkownika utrzymuje stałą lokalizację, ale IP rotuje między żądaniami (chyba że dodasz session-...). Limit współbieżności 5 zapobiega burstom.
Przykład 3: Python (requests) z surowym proxy i retry
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
PROXY = "http://user-country-DE:pass@gate.proxyhat.com:8080"
session = requests.Session()
session.proxies = {"http": PROXY, "https": PROXY}
retry = Retry(
total=3,
backoff_factor=1.5,
status_forcelist=[429, 500, 502, 503],
allowed_methods=["GET"]
)
session.mount("https://", HTTPAdapter(max_retries=retry))
def fetch_tickers(category="linear"):
resp = session.get(
"https://api.bybit.com/v5/market/tickers",
params={"category": category},
timeout=10
)
resp.raise_for_status()
payload = resp.json()
if payload["retCode"] != 0:
raise RuntimeError(f"Bybit error {payload['retCode']}: {payload['retMsg']}")
return payload["result"]["tickerList"]
tickers = fetch_tickers("linear")
print(f"Pobrano {len(tickers)} tickerów")
print(tickers[0]["symbol"], tickers[0]["lastPrice"])
Retry z backoff_factor=1.5 odczekuje 1.5s, 3s, 4.5s przed kolejnymi próbami. To wystarczy, aby przeczekać chwilowe 429 bez banu.
Przykład 4: Python (aiohttp) — sticky session dla paginacji kline
Paginacja kline wymaga spójnego IP, aby uniknąć resetu kursora. Sticky session utrzymuje ten sam adres IP przez cały ciąg żądań.
import asyncio
import aiohttp
async def fetch_klines(symbol, category="linear", interval="60", max_pages=10):
session_id = "kline-btc-001"
proxy = (
f"http://user-session-{session_id}-country-DE:pass"
"@gate.proxyhat.com:8080"
)
all_candles = []
connector = aiohttp.TCPConnector(limit=20)
async with aiohttp.ClientSession(connector=connector) as http:
cursor = ""
for page in range(max_pages):
params = {
"category": category,
"symbol": symbol,
"interval": interval,
"limit": "1000"
}
if cursor:
params["cursor"] = cursor
async with http.get(
"https://api.bybit.com/v5/market/kline",
params=params,
proxy=proxy,
timeout=aiohttp.ClientTimeout(total=15)
) as r:
data = await r.json()
if data["retCode"] != 0:
raise RuntimeError(f"{data['retCode']}: {data['retMsg']}")
all_candles.extend(data["result"]["list"])
cursor = data["result"].get("nextPageCursor", "")
if not cursor:
break
await asyncio.sleep(0.25) # szanuj limit per-endpoint
return all_candles
candles = asyncio.run(fetch_klines("BTCUSDT"))
print(f"Pobrano {len(candles)} świec")
Flaga session-kline-btc-001 przywiązuje wszystkie żądania w funkcji do jednego IP. Bez niej każde żądanie dostałoby nowy IP, a kursor paginacji mógłby się nie zachować prawidłowo po stronie Bybit.
Przykład 5: Node.js — budget per-endpoint z backoffem
Gdy scrapujesz wiele endpointów jednocześnie, każdy potrzebuje własnego budżetu. Poniższy przykład używa token bucket per-endpoint.
const axios = require('axios');
class RateBudget {
constructor(maxPerSec) {
this.maxPerSec = maxPerSec;
this.tokens = maxPerSec;
this.lastRefill = Date.now();
}
async take() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.maxPerSec, this.tokens + elapsed * this.maxPerSec);
this.lastRefill = now;
if (this.tokens < 1) {
await new Promise(r => setTimeout(r, (1 - this.tokens) / this.maxPerSec * 1000));
this.tokens = 0;
} else {
this.tokens -= 1;
}
}
}
const budgets = {
orderbook: new RateBudget(8), // ~8 req/s na IP
tickers: new RateBudget(5),
kline: new RateBudget(3)
};
async function callBybit(endpoint, params, budgetKey) {
const budget = budgets[budgetKey];
for (let attempt = 0; attempt < 4; attempt++) {
await budget.take();
try {
const { data } = await axios.get(`https://api.bybit.com${endpoint}`, {
params,
proxy: {
host: 'gate.proxyhat.com',
port: 8080,
auth: { username: 'user-country-DE', password: 'pass' }
},
timeout: 8000
});
if (data.retCode !== 0) throw new Error(data.retMsg);
return data.result;
} catch (err) {
if (attempt === 3) throw err;
await new Promise(r => setTimeout(r, 500 * Math.pow(2, attempt)));
}
}
}
// użycie
const ob = await callBybit('/v5/market/orderbook',
{ category: 'spot', symbol: 'BTCUSDT', limit: 50 }, 'orderbook');
console.log('top bid:', ob.b[0]);
Budżet 8 req/s dla orderbook zostawia margines pod limit Bybit. Backoff wykładniczy (0.5s, 1s, 2s) chroni przed kaskadowymi 403.
Przykład 6: WebSocket orderbook.50 vs REST snapshot
REST /v5/market/orderbook zwraca snapshot — idealny do punktowego odczytu. Do live depth lepiej użyć publicznego WebSocket wss://stream.bybit.com/v5/public/spot i strumienia orderbook.50.{symbol}.
const WebSocket = require('ws');
const ws = new WebSocket('wss://stream.bybit.com/v5/public/spot');
ws.on('open', () => {
ws.send(JSON.stringify({
op: 'subscribe',
args: ['orderbook.50.BTCUSDT']
}));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.topic && msg.topic.startsWith('orderbook.50')) {
const { b, a, u } = msg.data;
console.log(`update seq=${u} bids=${b.length} asks=${a.length}`);
}
});
ws.on('error', (err) => console.error('WS error:', err.message));
// reconnect z backoffem w produkcji
setInterval(() => {
if (ws.readyState === WebSocket.CLOSED) {
ws.close();
// ponowne połączenie...
}
}, 5000);
WebSocket nie wymaga proxy dla samych limitów IP (połączenie jest długotrwałe), ale jeśli jesteś w zablokowanym regionie, kieruj WebSocket przez SOCKS5 ProxyHat na porcie 1080. Większość bibliotek WS wspiera agenta SOCKS5 przez socks-proxy-agent.
Najczęstsze błędy i przypadki brzegowe
- Ignorowanie retCode. HTTP 200 z
retCode=10010to nadal błąd limitu. Zawsze sprawdzaj kopertę. - Mieszanie kategorii.
category=spotdla symbolu linear zwróci błąd lub puste dane. - Zbyt wysoka współbieżność. 50 współbieżnych żądań orderbook z jednego IP = gwarantowany ban. Utrzymuj ≤10.
- Brak sticky session przy paginacji. Kursor kline może się zachowywać niestabilnie przy zmianie IP między stronami.
- Geo-blokada datacenter. Proxy datacenter w zablokowanym kraju nie pomogą — użyj residential z
country-DElub innym dozwolonym. - Brak timeoutów. Zawieszone żądania blokują pulę połączeń. Ustaw timeout 8–15s.
Najlepsze praktyki produkcyjne
- Rejestruj retCode. Loguj każdy niezerowy retCode z timestampem i symbolem — to ujawnia wzorce limitów.
- Circuit breaker. Jeśli >20% żądań zwraca 403 w oknie 60s, wstrzymaj scrap na 5 minut zamiast nakręcać ban.
- Rotacja per-żądanie vs sticky. Rotacja dla tickers/orderbook; sticky dla kline paginacji i długich ciągów.
- Monitoruj sukces. Celuj w >95% success rate. Spadek poniżej 80% oznacza zbliżający się ban lub geo-blokadę.
- Szanuj ToS. Publiczne dane rynkowe są dozwolone, ale respektuj robots.txt i dokumentację API. Nie próbuj omijać limitów autoryzowanych endpointów prywatnych.
Pełną dokumentację ProxyHat znajdziesz na docs.proxyhat.com.
Najważniejsze wnioski
- Sprawdzaj
retCodeprzy każdym żądaniu — HTTP 200 nie oznacza sukcesu.- Orderbook ma najsurowsze limity IP; utrzymuj ≤8 req/s na IP.
- Geo-blokady (US/UK) wymagają proxy residential z
country-XXw nazwie użytkownika.- Sticky session (
session-abc123) jest kluczowa dla paginacji kline.- WebSocket
orderbook.50jest lepszy do live depth; REST do snapshotów.- Backoff wykładniczy + circuit breaker zapobiegają kaskadowym banom.
Scrapowanie API rynku Bybit V5 z rotującymi proxy to kwestia szacowania limitów, geo-targetingu i spójności sesji. ProxyHat daje Ci jedną bramę (gate.proxyhat.com:8080) z elastyczną konfiguracją w nazwie użytkownika — wystarczy, by utrzymać stabilny potok danych rynkowych. Zacznij od cennika i wybierz pakiet residential pasujący do Twojego throughput.






