Raspar a Bybit V5 Market API é tentador para bots de trading e pipelines de dados de cripto: os endpoints públicos de orderbook, tickers e klines não exigem autenticação e respondem em poucas dezenas de milissegundos. O problema aparece quando você passa de algumas requisições de teste para centenas de símbolos em loop — a Bybit impõe limites rigorosos por IP, bloqueia regiões inteiras via CloudFront e pode banir seu endereço por ~10 minutos. Neste guia você vai aprender como raspar a Bybit V5 Market API com proxies rotativos usando o gateway gate.proxyhat.com, com exemplos em Python e Node.js, sessões sticky para paginação de klines e fallback para WebSocket.
Aviso legal: Este guia cobre apenas endpoints públicos de dados de mercado da Bybit (/v5/market/*). Sempre consulte os Termos de Serviço da Bybit e respeiterobots.txtantes de coletar dados em escala. Não incentivamos scraping de endpoints autenticados, de conta ou de trading.
Por que raspar a Bybit V5 Market API com proxies rotativos é necessário
A V5 é a API unificada da Bybit, documentada em docs.bybit.com/v5/market. Ela substitui as antigas v2/v3 e unifica spot, linear e option sob o parâmetro category. O domínio público é api.bybit.com, servido atrás da AWS CloudFront, o que significa que bloqueios geo aparecem como 403 Forbidden antes mesmo de chegar à aplicação.
Existem dois motivos principais para usar proxies ao raspar a Bybit:
- Rate limits por IP: Cada endpoint tem um teto de requisições por segundo por IP de origem. Estourar repetidamente gera
403e uma janela de ban de ~10 minutos. - Geo-restrições: A Bybit bloqueia usuários em regiões restritas (EUA, Reino Unido, Canadá, Singapura, entre outras). Sem um IP de país permitido, a CloudFront devolve 403 sem corpo JSON.
Com proxies residenciais rotativos do ProxyHat, cada requisição (ou cada sessão) sai de um IP diferente localizado em um país permitido, contornando ambos os problemas de forma elegante.
Mapa dos endpoints públicos /v5/market
| Endpoint | Uso | Params obrigatórios |
|---|---|---|
GET /v5/market/orderbook | Snapshot de livro de ofertas | category, symbol |
GET /v5/market/tickers | Cotação atual de um ou todos os símbolos | category |
GET /v5/market/kline | Velas OHLCV históricas | category, symbol, interval |
GET /v5/market/recent-trade | Trades recentes | category, symbol |
Todos retornam o envelope padrão:
{
"retCode": 0,
"retMsg": "OK",
"result": { ... },
"time": 1700000000000
}
retCode: 0 indica sucesso. Qualquer valor diferente (ex.: 10001 para parâmetro inválido, 403 para bloqueio) exige tratamento explícito. Veja a lista oficial de códigos de erro.
Rate limits da Bybit: como o /v5/market/orderbook derruba seu IP
A Bybit aplica limites por endpoint e por IP de origem. Para dados públicos de mercado, os limites típicos são:
- Orderbook: ~50 requisições/seg por IP (categoria spot/linear).
- Tickers: ~10 req/s por IP.
- Kline: ~10 req/s por IP.
- Recent-trade: ~20 req/s por IP.
Esses números mudam — confirme sempre na documentação oficial. O ponto crítico é o orderbook: em bots de market-making que fazem polling agressivo de profundidade, é trivial estourar 50 req/s em um único IP. Quando isso acontece repetidamente, a Bybit retorna 403 e o IP entra numa janela de cooldown de aproximadamente 10 minutos.
Estratégias de mitigação:
- Rate budgeting por endpoint: Use um token bucket ou semáforo para limitar a concorrência real.
- Backoff exponencial: Em
403ou429, espere 1s, 2s, 4s, 8s e reinicie. - Rotação de IP: Cada requisição sai de um IP diferente, efetivamente multiplicando seu teto de throughput.
Geo-restrições da Bybit e como proxies residenciais restauram acesso
A Bybit restringe acesso de residentes em jurisdições como EUA, Reino Unido, Canadá, Singapura, Cuba, Irã, Coreia do Norte e outras. A filtragem acontece na borda da CloudFront: o IP de origem é comparado a uma lista de blocos de ASN/país e, se restrito, a CloudFront devolve 403 sem chegar ao backend da Bybit.
Para bots de dados que rodam em clouds US/EU, isso é fatal: mesmo endpoints públicos de mercado ficam inacessíveis. A solução é rotear o tráfego por um proxy residencial localizado em um país permitido, como Alemanha, Japão ou Brasil.
Com o ProxyHat você controla o país diretamente no username:
http://user-country-DE:pass@gate.proxyhat.com:8080
Para SOCKS5:
socks5://user-country-DE:pass@gate.proxyhat.com:1080
Você também pode segmentar por cidade: user-country-DE-city-berlin. Consulte a lista completa em /pt/locations.
Exemplo 1: curl com proxy bruto (linha de comando)
Antes de qualquer SDK, valide manualmente que o proxy funciona contra a Bybit:
# Orderbook spot BTCUSDT via proxy residencial em DE
curl -x http://user-country-DE:SUA_SENHA@gate.proxyhat.com:8080 \
"https://api.bybit.com/v5/market/orderbook?category=spot&symbol=BTCUSDT&limit=50"
# Tickers linear
url -x http://user-country-DE:SUA_SENHA@gate.proxyhat.com:8080 \
"https://api.bybit.com/v5/market/tickers?category=linear"
# Kline 1m, últimos 200 candles
url -x http://user-country-DE:SUA_SENHA@gate.proxyhat.com:8080 \
"https://api.bybit.com/v5/market/kline?category=linear&symbol=ETHUSDT&interval=1&limit=200"
Se você receber JSON com retCode: 0, o proxy está funcionando. Se receber HTML da CloudFront com 403, troque o país (algumas saídas residenciais podem estar em ASNs bloqueados — rotacione).
Exemplo 2: Python com requests + proxy bruto
import os
import time
import logging
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
BYBIT_BASE = "https://api.bybit.com"
PROXY_USER = os.environ["PROXYHAT_USER"]
PROXY_PASS = os.environ["PROXYHAT_PASS"]
PROXY_URL = f"http://{PROXY_USER}-country-DE:{PROXY_PASS}@gate.proxyhat.com:8080"
session = requests.Session()
retry = Retry(
total=5,
backoff_factor=1.5, # 1.5s, 3s, 6s, 12s, 24s
status_forcelist=(429, 403, 500, 502, 503),
allowed_methods=["GET"],
)
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies.update({"http": PROXY_URL, "https": PROXY_URL})
def fetch_orderbook(symbol: str, category: str = "linear", limit: int = 50):
url = f"{BYBIT_BASE}/v5/market/orderbook"
params = {"category": category, "symbol": symbol, "limit": limit}
r = session.get(url, params=params, timeout=10)
r.raise_for_status()
body = r.json()
if body.get("retCode") != 0:
raise RuntimeError(f"Bybit retCode={body.get('retCode')} msg={body.get('retMsg')}")
return body["result"]
if __name__ == "__main__":
symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "XRPUSDT"]
for sym in symbols:
try:
ob = fetch_orderbook(sym)
bid = float(ob["b"][0][0])
ask = float(ob["a"][0][0])
logging.info("%s bid=%s ask=%s spread=%.4f", sym, bid, ask, ask - bid)
except Exception as e:
logging.error("%s falhou: %s", sym, e)
time.sleep(0.2) # rate budget: 5 req/s
Note o rate budget de 0.2s entre chamadas — mesmo com proxy, respeitar o teto por IP evita desperdício de sessões. O Retry do urllib3 trata 403/429 automaticamente com backoff exponencial.
Exemplo 3: Python com ProxyHat SDK (rotação automática)
O ProxyHat SDK abstrai a rotação de IP, retries e seleção de país. Você não precisa construir URLs de proxy manualmente:
import asyncio
import logging
from proxyhat import ProxyHatClient # pacote: pip install proxyhat
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
client = ProxyHatClient(
username="SUA_SENHA_USER",
password="SUA_SENHA",
country="DE",
rotate=True, # IP novo a cada requisição
max_retries=5,
backoff_factor=1.5,
timeout=10,
)
BYBIT = "https://api.bybit.com/v5/market"
async def fetch_tickers(category: str = "linear"):
async with client.get(f"{BYBIT}/tickers", params={"category": category}) as r:
data = await r.json()
if data["retCode"] != 0:
raise RuntimeError(f"retCode={data['retCode']} msg={data['retMsg']}")
return data["result"]["list"]
async def fetch_orderbook(symbol: str, category: str = "linear", limit: int = 50):
async with client.get(
f"{BYBIT}/orderbook",
params={"category": category, "symbol": symbol, "limit": limit},
) as r:
data = await r.json()
return data["result"]
async def main():
tickers = await fetch_tickers("linear")
logging.info("Recebidos %d tickers linear", len(tickers))
symbols = [t["symbol"] for t in tickers[:10]]
tasks = [fetch_orderbook(s) for s in symbols]
results = await asyncio.gather(*tasks, return_exceptions=True)
for sym, res in zip(symbols, results):
if isinstance(res, Exception):
logging.error("%s: %s", sym, res)
else:
logging.info("%s top bid=%s", sym, res["b"][0][0])
asyncio.run(main())
Aqui usamos asyncio.gather para paralelizar 10 chamadas de orderbook. Como cada uma sai de um IP diferente via rotate=True, o teto de 50 req/s por IP não é um problema.
Exemplo 4: Node.js com axios e rotação de IP
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { ProxyHat } = require('proxyhat');
const PROXYHAT_USER = process.env.PROXYHAT_USER;
const PROXYHAT_PASS = process.env.PROXYHAT_PASS;
// Cliente ProxyHat com rotação por requisição
const ph = new ProxyHat({
username: PROXYHAT_USER,
password: PROXYHAT_PASS,
country: 'DE',
rotate: true,
maxRetries: 5,
backoffFactor: 1.5,
});
const BYBIT = 'https://api.bybit.com/v5/market';
async function fetchOrderbook(symbol, category = 'linear', limit = 50) {
const proxyUrl = await ph.nextProxyUrl(); // http://user-country-DE:pass@gate.proxyhat.com:8080
const agent = new HttpsProxyAgent(proxyUrl);
const { data } = await axios.get(`${BYBIT}/orderbook`, {
params: { category, symbol, limit },
httpsAgent: agent,
timeout: 10000,
});
if (data.retCode !== 0) {
throw new Error(`retCode=${data.retCode} msg=${data.retMsg}`);
}
return data.result;
}
async function main() {
const symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'XRPUSDT', 'DOGEUSDT'];
const results = await Promise.allSettled(
symbols.map((s) => fetchOrderbook(s).catch(async (err) => {
// backoff manual em caso de 403
if (err.response && err.response.status === 403) {
await new Promise((r) => setTimeout(r, 2000));
return fetchOrderbook(s);
}
throw err;
}))
);
results.forEach((r, i) => {
if (r.status === 'fulfilled') {
console.log(symbols[i], 'bid=', r.value.b[0][0], 'ask=', r.value.a[0][0]);
} else {
console.error(symbols[i], 'falhou:', r.reason.message);
}
});
}
main();
O Promise.allSettled garante que uma falha em um símbolo não derruba o batch inteiro. O backoff manual em 403 complementa o retry interno do SDK.
Exemplo 5: Sessões sticky para paginação de klines
O endpoint /v5/market/kline retorna no máximo 1000 candles por chamada. Para construir séries históricas longas, você pagina via start e end (timestamps em ms). O problema: se cada chamada sair de um IP diferente, você pode acabar com lacunas se um IP for banido no meio da sequência.
A solução é uma sessão sticky — o ProxyHat mantém o mesmo IP de saída durante toda a sessão:
import os
import time
import requests
PROXY_USER = os.environ["PROXYHAT_USER"]
PROXY_PASS = os.environ["PROXYHAT_PASS"]
# Sessão sticky: mesmo IP durante toda a coleta de klines
PROXY_URL = f"http://{PROXY_USER}-session-kline-btc-001:{PROXY_PASS}@gate.proxyhat.com:8080"
session = requests.Session()
session.proxies.update({"http": PROXY_URL, "https": PROXY_URL})
def fetch_klines(symbol, category, interval, start_ms, end_ms):
url = "https://api.bybit.com/v5/market/kline"
params = {
"category": category,
"symbol": symbol,
"interval": interval,
"start": start_ms,
"end": end_ms,
"limit": 1000,
}
r = session.get(url, params=params, timeout=10)
r.raise_for_status()
body = r.json()
if body["retCode"] != 0:
raise RuntimeError(f"retCode={body['retCode']} msg={body['retMsg']}")
return body["result"]["list"]
def collect_history(symbol, category, interval, days=30):
end_ms = int(time.time() * 1000)
start_ms = end_ms - days * 24 * 3600 * 1000
all_candles = []
cursor = start_ms
while cursor < end_ms:
batch = fetch_klines(symbol, category, interval, cursor, end_ms)
if not batch:
break
all_candles.extend(batch)
# próximo batch: último timestamp + 1ms
cursor = int(batch[-1][0]) + 1
time.sleep(0.15) # rate budget: ~6 req/s, abaixo do teto de 10
return all_candles
if __name__ == "__main__":
candles = collect_history("BTCUSDT", "linear", "60", days=7)
print(f"Coletados {len(candles)} candles de 1h em 7 dias")
O flag -session-kline-btc-001 no username garante que o ProxyHat atribua um IP residencial fixo para essa string de sessão. Se você rodar múltiplos símbolos em paralelo, use IDs de sessão distintos por símbolo (-session-kline-eth-001, etc.) para distribuir a carga entre IPs.
Exemplo 6: WebSocket público vs REST snapshot
Para orderbook em tempo real, REST polling é ineficiente. A Bybit oferece um stream WebSocket público em wss://stream.bybit.com/v5/public/linear com o canal orderbook.50.BTCUSDT, que entrega snapshots incrementais a ~10ms. Para bots de market-making, essa é a fonte canônica.
import json
import asyncio
import websockets
async def listen_orderbook():
url = "wss://stream.bybit.com/v5/public/linear"
async with websockets.connect(url, ping_interval=20) as ws:
# Snapshot inicial
await ws.send(json.dumps({
"op": "subscribe",
"args": ["orderbook.50.BTCUSDT"]
}))
async for msg in ws:
data = json.loads(msg)
if data.get("topic", "").startswith("orderbook.50"):
ob = data["data"]
top_bid = ob["b"][0][0] if ob["b"] else None
top_ask = ob["a"][0][0] if ob["a"] else None
print(f"type={data['type']} bid={top_bid} ask={top_ask}")
asyncio.run(listen_orderbook())
Comparação rápida:
| Aspecto | REST /v5/market/orderbook | WebSocket orderbook.50 |
|---|---|---|
| Latência | ~50-200ms (com proxy) | ~10-50ms |
| Taxa de atualização | Sob demanda (polling) | Push em tempo real |
| Custo de IP | Alto (rate limit por IP) | Baixo (1 conexão persistente) |
| Ideal para | Snapshot histórico, backfill | Market-making, execução ao vivo |
Nota: o WebSocket público da Bybit também pode sofrer geo-restrição. Se necessário, conecte via socks5://user-country-DE:pass@gate.proxyhat.com:1080.
Erros comuns e edge cases
- 403 sem corpo JSON: É CloudFront bloqueando por geo. Troque o país do proxy.
- retCode 10001: Parâmetro inválido — verifique
category(spot vs linear vs option). - retCode 403 ou 429: Rate limit excedido. Faça backoff e reduza a concorrência.
- IP banido por 10 minutos: Se você insiste após 403, o ban se prolonga. Implemente circuit breaker.
- Lacunas em klines: Use sessões sticky e valide
retCodeem cada batch. - Símbolos deslistados: Sempre consulte
/v5/market/instruments-infoantes de raspar tickers de símbolos que podem ter sido removidos.
Setup no ProxyHat e links úteis
- Crie sua conta em /pt/pricing e escolha um plano residencial adequado ao seu volume.
- Configure as variáveis de ambiente
PROXYHAT_USERePROXYHAT_PASS. - Escolha países permitidos pela Bybit (DE, JP, BR, etc.) consultando /pt/locations.
- Para casos de uso de scraping em geral, veja /pt/use-cases/web-scraping.
- Para tracking de SERP e monitoramento de concorrentes, veja /pt/use-cases/serp-tracking.
- Documentação técnica do ProxyHat: docs.proxyhat.com.
Key Takeaways
- Rate limit por IP é o gargalo número 1. O orderbook tem ~50 req/s por IP; bursts derrubam o IP por ~10 min.
- Geo-restrição vem da CloudFront. 403 sem JSON = bloqueio de país. Use proxy residencial em país permitido.
- Sessões sticky para paginação. Use
-session-xxxno username para manter o mesmo IP em sequências longas de klines. - WebSocket para tempo real, REST para snapshots. Não faça polling de orderbook se você precisa de <50ms de latência.
- Sempre valide retCode. A Bybit não usa HTTP status para erros de aplicação —
retCode != 0exige tratamento.
FAQ
O que é raspar a Bybit V5 Market API com proxies rotativos?
É a prática de coletar dados públicos de mercado da Bybit (orderbook, tickers, klines, trades recentes) roteando cada requisição HTTP por um proxy residencial diferente, evitando rate limits por IP e contornando geo-restrições da CloudFront. O ProxyHat fornece o gateway gate.proxyhat.com:8080 (HTTP) ou :1080 (SOCKS5) com rotação automática e geo-targeting por país.
Por que isso importa para usuários de proxy?
Porque a Bybit impõe limites estritos por IP (~50 req/s no orderbook) e bloqueia regiões inteiras via CloudFront. Sem rotação de IP, um bot de trading que monitora 50 símbolos em polling pode ser banido em minutos. Proxies residenciais rotativos multiplicam o teto efetivo de throughput e restauram o acesso de clouds em países restritos, permitindo coleta de dados em escala.
Qual tipo de proxy funciona melhor para raspar a Bybit V5 Market API?
Proxies residenciais são a melhor escolha porque seus IPs vêm de ASNs de ISPs reais, dificilmente bloqueados pela CloudFront. Datacenter proxies tendem a ser flaggeados como datacenter e bloqueados mais rápido. Para paginação de klines, use sessões sticky (-session-xxx) para manter consistência de IP durante a sequência. Para polling de orderbook em paralelo, use rotação pura.
Como evitar bloqueios ao raspar a Bybit V5 Market API?
Implemente rate budgeting (token bucket por endpoint), backoff exponencial em 403/429, circuit breaker após N falhas consecutivas, e rotação de IP via ProxyHat. Mantenha concorrência abaixo do teto por IP (ex.: 5 req/s para orderbook com 1 IP, ou 50 req/s com 10 IPs rotativos). Sempre valide retCode no envelope de resposta e consulte a documentação oficial para limites atualizados.
WebSocket público da Bybit também precisa de proxy?
Pode precisar. O endpoint WebSocket wss://stream.bybit.com/v5/public/linear também passa pela CloudFront e pode sofrer geo-restrição. Se sua infraestrutura está em país bloqueado, conecte via SOCKS5 (socks5://user-country-DE:pass@gate.proxyhat.com:1080). WebSocket tem vantagem sobre REST: uma única conexão persistente substitui centenas de polls, reduzindo drasticamente o consumo de IPs.






