[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-es-scrapear-api-mercado-bybit-v5-proxies":3},{"success":4,"payload":5,"related":60,"errors":124},true,{"id":6,"slug":7,"title":8,"excerpt":9,"body_html":10,"meta_title":11,"meta_description":12,"canonical_url":13,"canonical_override":14,"og_title":15,"og_description":12,"og_image_url":16,"faq_json":17,"key_takeaways":14,"allow_index":4,"noindex_google_only":27,"allow_follow":4,"published_at":28,"updated_at":28,"content_updated_at":29,"featured_image_url":30,"featured_image_alt":31,"card_image_url":32,"reading_time_minutes":33,"author":34,"categories":38,"tags":45,"alternates":46,"locale_paths":57,"views":59},195,"scrapear-api-mercado-bybit-v5-proxies","Cómo extraer datos de mercado cripto con proxies rotativos: límites, geobloqueos y paginación (ejemplo: API V5 de Bybit)","Guía de desarrollo para extraer datos de mercado de Bybit V5 usando proxies residenciales rotativos. Cubre endpoints, rate limits, geo-bloqueos, sesiones sticky y ejemplos en Python y Node.js con ProxyHat.","\u003Cp>Scrapear la API de Mercado V5 de Bybit con proxies rotativos es una técnica esencial para bots de trading y pipelines de datos que necesitan acceso fiable a \u003Ccode>api.bybit.com\u002Fv5\u002Fmarket\u002F\u003C\u002Fcode> desde regiones restringidas o a alta frecuencia. Esta guía cubre los endpoints públicos, los límites de tasa por IP, las restricciones geográficas y cómo configurar proxies residenciales a través de \u003Ccode>gate.proxyhat.com\u003C\u002Fcode> con rotación de IPs y sesiones sticky.\u003C\u002Fp>\n\n\u003Cblockquote>\u003Cp>\u003Cstrong>Aviso legal:\u003C\u002Fstrong> Los endpoints de mercado públicos de Bybit están documentados oficialmente, pero debes revisar los Términos de Servicio de Bybit antes de cualquier uso comercial. Esta guía se centra en datos públicos de mercado y no promueve el scraping de endpoints autenticados ni el violar restricciones legales locales.\u003C\u002Fp>\u003C\u002Fblockquote>\n\n\u003Ch2>Por qué scrapear la API de Mercado V5 de Bybit con proxies rotativos\u003C\u002Fh2>\n\n\u003Cp>Bybit migró a su API unificada V5 en 2023, consolidando endpoints de spot, derivados lineares y opciones bajo un mismo esquema (\u003Ca href=\"https:\u002F\u002Fbybit-exchange.github.io\u002Fdocs\u002Fv5\u002Fintro\" rel=\"noopener noreferrer\">documentación oficial de Bybit V5\u003C\u002Fa>). Los endpoints de mercado públicos bajo \u003Ccode>\u002Fv5\u002Fmarket\u002F\u003C\u002Fcode> no requieren autenticación, pero sí aplican límites de tasa por IP y restricciones geográficas agresivas.\u003C\u002Fp>\n\n\u003Cp>Si tu infraestructura corre desde EE. UU., el Reino Unido o regiones restringidas, Bybit devuelve un \u003Ccode>403 Forbidden\u003C\u002Fcode> a nivel de CloudFront antes de que la solicitud llegue al backend. Y si haces demasiadas peticiones desde una sola IP, Bybit impone un ban temporal de aproximadamente \u003Cstrong>10 minutos\u003C\u002Fstrong> con un \u003Ccode>retCode\u003C\u002Fcode> de error específico en el cuerpo JSON.\u003C\u002Fp>\n\n\u003Cp>Los \u003Ca href=\"\u002Fes\u002Fproxy\u002Fresidential\">proxies residenciales\u003C\u002Fa> rotativos resuelven ambos problemas: te dan IPs de países permitidos y distribuyen el tráfico para evitar tripar los límites por IP. ProxyHat ofrece acceso a través de \u003Ccode>gate.proxyhat.com:8080\u003C\u002Fcode> (HTTP) y \u003Ccode>gate.proxyhat.com:1080\u003C\u002Fcode> (SOCKS5), con geo-segmentación por país y ciudad en el nombre de usuario.\u003C\u002Fp>\n\n\u003Ch2>Endpoints públicos de \u002Fv5\u002Fmarket\u002F\u003C\u002Fh2>\n\n\u003Cp>La V5 Market API expone cuatro endpoints principales para datos públicos:\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Endpoint\u003C\u002Fth>\u003Cth>Método\u003C\u002Fth>\u003Cth>Parámetros clave\u003C\u002Fth>\u003Cth>Uso típico\u003C\u002Fth>\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>GET\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode> (spot|linear|option), \u003Ccode>symbol\u003C\u002Fcode>, \u003Ccode>limit\u003C\u002Fcode> (depth)\u003C\u002Ftd>\u003Ctd>Snapshot de libro de órdenes\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Ftickers\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>GET\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, \u003Ccode>symbol\u003C\u002Fcode> (opcional)\u003C\u002Ftd>\u003Ctd>Precios y stats de 24h\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Fkline\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>GET\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, \u003Ccode>symbol\u003C\u002Fcode>, \u003Ccode>interval\u003C\u002Fcode>, \u003Ccode>start\u003C\u002Fcode>, \u003Ccode>end\u003C\u002Fcode>, \u003Ccode>limit\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Velas OHLCV históricas\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Frecent-trade\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>GET\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, \u003Ccode>symbol\u003C\u002Fcode>, \u003Ccode>limit\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Trades recientes\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>El parámetro category\u003C\u002Fh3>\n\n\u003Cp>El parámetro \u003Ccode>category\u003C\u002Fcode> es obligatorio en todos los endpoints de mercado. Acepta \u003Ccode>spot\u003C\u002Fcode>, \u003Ccode>linear\u003C\u002Fcode> (futuros perpetuos lineares USDT), \u003Ccode>inverse\u003C\u002Fcode> y \u003Ccode>option\u003C\u002Fcode>. Omitirlo o usar un valor inválido devuelve \u003Ccode>retCode: 10001\u003C\u002Fcode> con un \u003Ccode>retMsg\u003C\u002Fcode> descriptivo.\u003C\u002Fp>\n\n\u003Ch3>El envelope de respuesta retCode\u002FretMsg\u003C\u002Fh3>\n\n\u003Cp>Toda la V5 API usa un envelope JSON consistente:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>{\n  &quot;retCode&quot;: 0,\n  &quot;retMsg&quot;: &quot;OK&quot;,\n  &quot;result&quot;: {\n    &quot;category&quot;: &quot;spot&quot;,\n    &quot;symbol&quot;: &quot;BTCUSDT&quot;,\n    &quot;list&quot;: [...]\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Un \u003Ccode>retCode\u003C\u002Fcode> de \u003Ccode>0\u003C\u002Fcode> indica éxito. Cualquier otro valor señala un error —puede ser de validación de parámetros, rate limit, o de servicio. Es crítico comprobar \u003Ccode>retCode\u003C\u002Fcode> antes de procesar \u003Ccode>result\u003C\u002Fcode>, porque Bybit devuelve HTTP 200 incluso en errores lógicos.\u003C\u002Fp>\n\n\u003Ch2>Límites de tasa por IP y bans temporales\u003C\u002Fh2>\n\n\u003Cp>Bybit aplica límites de tasa por endpoint y por IP. Según la documentación oficial de rate limits de Bybit V5, los endpoints de mercado públicos tienen límites distintos:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fstrong>: límite estricto, típicamente 10–20 requests\u002Fsegundo por IP. Las ráfagas que excedan esto provocan un ban temporal.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u002Fv5\u002Fmarket\u002Ftickers\u003C\u002Fstrong>: más permisivo, ~50 requests\u002Fsegundo por IP.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u002Fv5\u002Fmarket\u002Fkline\u003C\u002Fstrong>: ~10 requests\u002Fsegundo por IP, con un máximo de 1000 velas por request.\u003C\u002Fli>\n\u003Cli>\u002Fv5\u002Fmarket\u002Frecent-trade: ~10 requests\u002Fsegundo por IP, máximo 1000 trades por request.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Cuando una IP excede el límite, Bybit devuelve un \u003Ccode>403\u003C\u002Fcode> HTTP o un \u003Ccode>retCode\u003C\u002Fcode> de rate limit (típicamente \u003Ccode>10001\u003C\u002Fcode> o un código específico de rate limit). El ban dura aproximadamente 10 minutos, durante los cuales todas las peticiones desde esa IP fallan. Esto hace que la rotación de IPs sea crítica para mantener throughput.\u003C\u002Fp>\n\n\u003Ch2>Restricciones geográficas y CloudFront\u003C\u002Fh2>\n\n\u003Cp>Bybit bloquea tráfico desde EE. UU., el Reino Unido, Singapur y otras regiones reguladas. El bloqueo ocurre a nivel de CloudFront: la respuesta es un \u003Ccode>403\u003C\u002Fcode> con un body HTML genérico de CloudFront, no un JSON de Bybit. Esto significa que ni siquiera llegas a la API.\u003C\u002Fp>\n\n\u003Cp>Con proxies residenciales de ProxyHat, puedes enmascarar tu IP de origen con una IP de un país permitido (por ejemplo, Alemania, Japón, Brasil). La geo-segmentación se controla en el nombre de usuario:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode># HTTP proxy con geo-targeting a Alemania\nhttp:\u002F\u002Fuser-country-DE:PASSWORD@gate.proxyhat.com:8080\n\n# SOCKS5 proxy con geo-targeting a Japón\nsocks5:\u002F\u002Fuser-country-JP:PASSWORD@gate.proxyhat.com:1080\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Consulta las \u003Ca href=\"\u002Fes\u002Flocations\">ubicaciones disponibles de ProxyHat\u003C\u002Fa> para ver qué países soporta.\u003C\u002Fp>\n\n\u003Ch2>Implementación: rotación de IPs en Python\u003C\u002Fh2>\n\n\u003Cp>Empezamos con un ejemplo en Python usando \u003Ccode>requests\u003C\u002Fcode> con proxy raw y luego con el patrón de rotación de ProxyHat. El objetivo es obtener tickers de múltiples símbolos rotando IPs en cada request.\u003C\u002Fp>\n\n\u003Ch3>Ejemplo 1: Proxy HTTP raw con requests\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\nimport logging\nfrom requests.exceptions import RequestException\n\nlogging.basicConfig(level=logging.INFO, format=&apos;%(asctime)s %(levelname)s %(message)s&apos;)\nlogger = logging.getLogger(__name__)\n\nBYBIT_BASE = &quot;https:\u002F\u002Fapi.bybit.com&quot;\nPROXY_URL = &quot;http:\u002F\u002Fuser-country-DE:PASSWORD@gate.proxyhat.com:8080&quot;\n\nPROXIES = {&quot;http&quot;: PROXY_URL, &quot;https&quot;: PROXY_URL}\n\nSYMBOLS = [&quot;BTCUSDT&quot;, &quot;ETHUSDT&quot;, &quot;SOLUSDT&quot;, &quot;XRPUSDT&quot;, &quot;DOGEUSDT&quot;]\n\ndef fetch_tickers(category=&quot;spot&quot;, symbol=None):\n    params = {&quot;category&quot;: category}\n    if symbol:\n        params[&quot;symbol&quot;] = symbol\n\n    for attempt in range(5):\n        try:\n            resp = requests.get(\n                f&quot;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Ftickers&quot;,\n                params=params,\n                proxies=PROXIES,\n                timeout=10\n            )\n            data = resp.json()\n            if data.get(&quot;retCode&quot;) != 0:\n                logger.warning(f&quot;Bybit retCode={data.get(&apos;retCode&apos;)} retMsg={data.get(&apos;retMsg&apos;)}&quot;)\n                if data.get(&quot;retCode&quot;) == 10001:\n                    time.sleep(2 ** attempt)\n                    continue\n                return None\n            return data[&quot;result&quot;]\n        except RequestException as e:\n            logger.error(f&quot;Request error: {e}&quot;)\n            time.sleep(2 ** attempt)\n    return None\n\nfor sym in SYMBOLS:\n    result = fetch_tickers(symbol=sym)\n    if result and result.get(&quot;list&quot;):\n        ticker = result[&quot;list&quot;][0]\n        logger.info(f&quot;{sym}: lastPrice={ticker[&apos;lastPrice&apos;]}, volume24h={ticker[&apos;volume24h&apos;]}&quot;)\n    time.sleep(0.1)  # Rate budget: ~10 req\u002Fs per IP\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>Ejemplo 2: Rotación de IPs con proxy URL builder\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\nimport random\nimport string\nimport logging\n\nlogging.basicConfig(level=logging.INFO, format=&apos;%(asctime)s %(levelname)s %(message)s&apos;)\nlogger = logging.getLogger(__name__)\n\nBYBIT_BASE = &quot;https:\u002F\u002Fapi.bybit.com&quot;\nPROXYHAT_USER = &quot;user&quot;\nPROXYHAT_PASS = &quot;PASSWORD&quot;\nCOUNTRIES = [&quot;DE&quot;, &quot;JP&quot;, &quot;BR&quot;, &quot;NL&quot;, &quot;SG&quot;]\n\ndef build_proxy_url(country=None, session_id=None):\n    username = PROXYHAT_USER\n    if country:\n        username += f&quot;-country-{country}&quot;\n    if session_id:\n        username += f&quot;-sid-{session_id}&quot;\n    return f&quot;http:\u002F\u002F{username}:{PROXYHAT_PASS}@gate.proxyhat.com:8080&quot;\n\ndef fetch_with_rotation(url, params, max_retries=5):\n    for attempt in range(max_retries):\n        country = random.choice(COUNTRIES)\n        proxy_url = build_proxy_url(country=country)\n        proxies = {&quot;http&quot;: proxy_url, &quot;https&quot;: proxy_url}\n        try:\n            resp = requests.get(url, params=params, proxies=proxies, timeout=10)\n            if resp.status_code == 403:\n                logger.warning(f&quot;403 from {country}, rotating...&quot;)\n                time.sleep(1 + attempt)\n                continue\n            data = resp.json()\n            if data.get(&quot;retCode&quot;) == 0:\n                return data[&quot;result&quot;]\n            logger.warning(f&quot;retCode={data.get(&apos;retCode&apos;)} msg={data.get(&apos;retMsg&apos;)}&quot;)\n            time.sleep(2 ** attempt)\n        except Exception as e:\n            logger.error(f&quot;Error with {country}: {e}&quot;)\n            time.sleep(2 ** attempt)\n    return None\n\n# Scrapear orderbook de BTCUSDT con rotación\nresult = fetch_with_rotation(\n    f&quot;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Forderbook&quot;,\n    {&quot;category&quot;: &quot;linear&quot;, &quot;symbol&quot;: &quot;BTCUSDT&quot;, &quot;limit&quot;: 50}\n)\nif result:\n    logger.info(f&quot;Bids: {len(result.get(&apos;b&apos;, []))}, Asks: {len(result.get(&apos;a&apos;, []))}&quot;)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Implementación: rotación de IPs en Node.js con axios\u003C\u002Fh2>\n\n\u003Ch3>Ejemplo 3: Proxy raw con axios (HttpsProxyAgent)\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode>const axios = require(&apos;axios&apos;);\nconst { HttpsProxyAgent } = require(&apos;https-proxy-agent&apos;);\nconst logger = require(&apos;pino&apos;)();\n\nconst BYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;;\nconst PROXY_URL = &apos;http:\u002F\u002Fuser-country-DE:PASSWORD@gate.proxyhat.com:8080&apos;;\nconst agent = new HttpsProxyAgent(PROXY_URL);\n\nconst SYMBOLS = [&apos;BTCUSDT&apos;, &apos;ETHUSDT&apos;, &apos;SOLUSDT&apos;, &apos;XRPUSDT&apos;, &apos;DOGEUSDT&apos;];\n\nasync function fetchTickers(symbol, category = &apos;spot&apos;, retries = 5) {\n  for (let attempt = 0; attempt &lt; retries; attempt++) {\n    try {\n      const resp = await axios.get(`${BYBIT_BASE}\u002Fv5\u002Fmarket\u002Ftickers`, {\n        params: { category, symbol },\n        httpsAgent: agent,\n        timeout: 10000\n      });\n      if (resp.data.retCode !== 0) {\n        logger.warn({ retCode: resp.data.retCode, retMsg: resp.data.retMsg });\n        await sleep(Math.pow(2, attempt) * 1000);\n        continue;\n      }\n      return resp.data.result;\n    } catch (err) {\n      logger.error({ err: err.message, symbol });\n      await sleep(Math.pow(2, attempt) * 1000);\n    }\n  }\n  return null;\n}\n\nfunction sleep(ms) { return new Promise(r =&gt; setTimeout(r, ms)); }\n\nasync function main() {\n  for (const sym of SYMBOLS) {\n    const result = await fetchTickers(sym);\n    if (result?.list?.[0]) {\n      const t = result.list[0];\n      logger.info({ sym, lastPrice: t.lastPrice, volume24h: t.volume24h });\n    }\n    await sleep(100); \u002F\u002F Rate budget: ~10 req\u002Fs per IP\n  }\n}\n\nmain().catch(logger.error);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>Ejemplo 4: Rotación de IPs con proxy builder en Node.js\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode>const axios = require(&apos;axios&apos;);\nconst { HttpsProxyAgent } = require(&apos;https-proxy-agent&apos;);\nconst logger = require(&apos;pino&apos;)();\n\nconst BYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;;\nconst PROXYHAT_USER = &apos;user&apos;;\nconst PROXYHAT_PASS = &apos;PASSWORD&apos;;\nconst COUNTRIES = [&apos;DE&apos;, &apos;JP&apos;, &apos;BR&apos;, &apos;NL&apos;, &apos;SG&apos;];\n\nfunction buildProxyUrl(opts = {}) {\n  let username = PROXYHAT_USER;\n  if (opts.country) username += `-country-${opts.country}`;\n  if (opts.session) username += `-sid-${opts.session}`;\n  return `http:\u002F\u002F${username}:${PROXYHAT_PASS}@gate.proxyhat.com:8080`;\n}\n\nfunction randomCountry() {\n  return COUNTRIES[Math.floor(Math.random() * COUNTRIES.length)];\n}\n\nfunction sleep(ms) { return new Promise(r =&gt; setTimeout(r, ms)); }\n\nasync function fetchWithRotation(endpoint, params, retries = 5) {\n  for (let attempt = 0; attempt &lt; retries; attempt++) {\n    const country = randomCountry();\n    const proxyUrl = buildProxyUrl({ country });\n    const agent = new HttpsProxyAgent(proxyUrl);\n    try {\n      const resp = await axios.get(`${BYBIT_BASE}${endpoint}`, {\n        params,\n        httpsAgent: agent,\n        timeout: 10000\n      });\n      if (resp.status === 403) {\n        logger.warn({ country, status: 403 });\n        await sleep(1000 + attempt * 1000);\n        continue;\n      }\n      if (resp.data.retCode === 0) return resp.data.result;\n      logger.warn({ retCode: resp.data.retCode, retMsg: resp.data.retMsg, country });\n      await sleep(Math.pow(2, attempt) * 1000);\n    } catch (err) {\n      logger.error({ err: err.message, country });\n      await sleep(Math.pow(2, attempt) * 1000);\n    }\n  }\n  return null;\n}\n\n\u002F\u002F Scrapear orderbook de múltiples símbolos con rotación\nasync function scrapeOrderbooks(symbols) {\n  for (const sym of symbols) {\n    const result = await fetchWithRotation(&apos;\u002Fv5\u002Fmarket\u002Forderbook&apos;, {\n      category: &apos;linear&apos;,\n      symbol: sym,\n      limit: 50\n    });\n    if (result) {\n      logger.info({\n        sym,\n        bids: result.b?.length,\n        asks: result.a?.length,\n        bestBid: result.b?.[0]?.[0],\n        bestAsk: result.a?.[0]?.[0]\n      });\n    }\n    await sleep(120); \u002F\u002F Rate budget per IP\n  }\n}\n\nscrapeOrderbooks([&apos;BTCUSDT&apos;, &apos;ETHUSDT&apos;, &apos;SOLUSDT&apos;]).catch(logger.error);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Sesiones sticky para paginación de kline\u003C\u002Fh2>\n\n\u003Cp>El endpoint \u003Ccode>\u002Fv5\u002Fmarket\u002Fkline\u003C\u002Fcode> permite un máximo de 1000 velas por request. Para descargar historial largo necesitas paginar usando los parámetros \u003Ccode>start\u003C\u002Fcode> y \u003Ccode>end\u003C\u002Fcode> (timestamps en milisegundos). Es crítico mantener la misma IP durante toda la paginación para evitar inconsistencias y bans intermedios.\u003C\u002Fp>\n\n\u003Cp>ProxyHat soporta sesiones sticky mediante el flag \u003Ccode>-sid-\u003C\u002Fcode> en el nombre de usuario. Mientras la sesión esté activa, todas las peticiones saldrán desde la misma IP.\u003C\u002Fp>\n\n\u003Ch3>Ejemplo 5: Paginación de kline con sesión sticky en Python\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\nimport logging\n\nlogging.basicConfig(level=logging.INFO, format=&apos;%(asctime)s %(levelname)s %(message)s&apos;)\nlogger = logging.getLogger(__name__)\n\nBYBIT_BASE = &quot;https:\u002F\u002Fapi.bybit.com&quot;\nPROXYHAT_USER = &quot;user&quot;\nPROXYHAT_PASS = &quot;PASSWORD&quot;\n\ndef build_sticky_proxy(session_id, country=&quot;DE&quot;):\n    username = f&quot;{PROXYHAT_USER}-country-{country}-sid-{session_id}&quot;\n    return f&quot;http:\u002F\u002F{username}:{PROXYHAT_PASS}@gate.proxyhat.com:8080&quot;\n\ndef fetch_kline_page(symbol, category, interval, start, end, session_id, country=&quot;DE&quot;):\n    proxy_url = build_sticky_proxy(session_id, country)\n    proxies = {&quot;http&quot;: proxy_url, &quot;https&quot;: proxy_url}\n\n    for attempt in range(5):\n        try:\n            resp = requests.get(\n                f&quot;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Fkline&quot;,\n                params={\n                    &quot;category&quot;: category,\n                    &quot;symbol&quot;: symbol,\n                    &quot;interval&quot;: interval,\n                    &quot;start&quot;: start,\n                    &quot;end&quot;: end,\n                    &quot;limit&quot;: 1000\n                },\n                proxies=proxies,\n                timeout=15\n            )\n            if resp.status_code == 403:\n                logger.warning(f&quot;403, switching country...&quot;)\n                return None\n            data = resp.json()\n            if data.get(&quot;retCode&quot;) == 0:\n                return data[&quot;result&quot;][&quot;list&quot;]\n            logger.warning(f&quot;retCode={data.get(&apos;retCode&apos;)} msg={data.get(&apos;retMsg&apos;)}&quot;)\n            time.sleep(2 ** attempt)\n        except Exception as e:\n            logger.error(f&quot;Error: {e}&quot;)\n            time.sleep(2 ** attempt)\n    return None\n\ndef download_kline_history(symbol, category, interval, start_ms, end_ms):\n    session_id = f&quot;kline-{symbol}-{int(time.time())}&quot;\n    all_candles = []\n    cursor = start_ms\n\n    while cursor &lt; end_ms:\n        page_end = min(cursor + 1000 * 60_000, end_ms)  # Ajustar según interval\n        candles = fetch_kline_page(\n            symbol, category, interval, cursor, page_end, session_id\n        )\n        if not candles:\n            logger.error(f&quot;Failed at cursor={cursor}, aborting&quot;)\n            break\n        all_candles.extend(candles)\n        logger.info(f&quot;Got {len(candles)} candles, total={len(all_candles)}&quot;)\n        cursor = page_end + 1\n        time.sleep(0.15)  # Rate budget: ~6-7 req\u002Fs per IP\n\n    return all_candles\n\n# Descargar 1 hora de velas de 1 minuto para BTCUSDT\nnow_ms = int(time.time() * 1000)\nstart_ms = now_ms - 3600 * 1000\ncandles = download_kline_history(&quot;BTCUSDT&quot;, &quot;linear&quot;, &quot;1&quot;, start_ms, now_ms)\nlogger.info(f&quot;Total candles downloaded: {len(candles)}&quot;)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>Ejemplo 6: Sesión sticky en Node.js para kline\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode>const axios = require(&apos;axios&apos;);\nconst { HttpsProxyAgent } = require(&apos;https-proxy-agent&apos;);\nconst logger = require(&apos;pino&apos;)();\n\nconst BYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;;\nconst PROXYHAT_USER = &apos;user&apos;;\nconst PROXYHAT_PASS = &apos;PASSWORD&apos;;\n\nfunction buildStickyProxy(sessionId, country = &apos;DE&apos;) {\n  const username = `${PROXYHAT_USER}-country-${country}-sid-${sessionId}`;\n  return `http:\u002F\u002F${username}:${PROXYHAT_PASS}@gate.proxyhat.com:8080`;\n}\n\nfunction sleep(ms) { return new Promise(r =&gt; setTimeout(r, ms)); }\n\nasync function fetchKlinePage(symbol, category, interval, start, end, sessionId, country = &apos;DE&apos;) {\n  const proxyUrl = buildStickyProxy(sessionId, country);\n  const agent = new HttpsProxyAgent(proxyUrl);\n\n  for (let attempt = 0; attempt &lt; 5; attempt++) {\n    try {\n      const resp = await axios.get(`${BYBIT_BASE}\u002Fv5\u002Fmarket\u002Fkline`, {\n        params: { category, symbol, interval, start, end, limit: 1000 },\n        httpsAgent: agent,\n        timeout: 15000\n      });\n      if (resp.data.retCode === 0) return resp.data.result.list;\n      logger.warn({ retCode: resp.data.retCode, retMsg: resp.data.retMsg });\n      await sleep(Math.pow(2, attempt) * 1000);\n    } catch (err) {\n      logger.error({ err: err.message });\n      await sleep(Math.pow(2, attempt) * 1000);\n    }\n  }\n  return null;\n}\n\nasync function downloadKlineHistory(symbol, category, interval, startMs, endMs) {\n  const sessionId = `kline-${symbol}-${Date.now()}`;\n  const allCandles = [];\n  let cursor = startMs;\n\n  while (cursor &lt; endMs) {\n    const pageEnd = Math.min(cursor + 1000 * 60000, endMs);\n    const candles = await fetchKlinePage(symbol, category, interval, cursor, pageEnd, sessionId);\n    if (!candles) {\n      logger.error({ msg: &apos;Failed, aborting&apos;, cursor });\n      break;\n    }\n    allCandles.push(...candles);\n    logger.info({ got: candles.length, total: allCandles.length });\n    cursor = pageEnd + 1;\n    await sleep(150);\n  }\n  return allCandles;\n}\n\nconst now = Date.now();\nconst start = now - 3600 * 1000;\ndownloadKlineHistory(&apos;BTCUSDT&apos;, &apos;linear&apos;, &apos;1&apos;, start, now)\n  .then(candles =&gt; logger.info({ total: candles.length }))\n  .catch(logger.error);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>curl: prueba rápida de conectividad\u003C\u002Fh2>\n\n\u003Cpre>\u003Ccode># Probar orderbook con proxy HTTP de Alemania\ncurl -x &quot;http:\u002F\u002Fuser-country-DE:PASSWORD@gate.proxyhat.com:8080&quot; \\\n  &quot;https:\u002F\u002Fapi.bybit.com\u002Fv5\u002Fmarket\u002Forderbook?category=linear&amp;symbol=BTCUSDT&amp;limit=50&quot;\n\n# Probar tickers con proxy SOCKS5 de Japón\ncurl -x &quot;socks5:\u002F\u002Fuser-country-JP:PASSWORD@gate.proxyhat.com:1080&quot; \\\n  &quot;https:\u002F\u002Fapi.bybit.com\u002Fv5\u002Fmarket\u002Ftickers?category=spot&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>REST snapshots vs WebSocket orderbook.50\u003C\u002Fh2>\n\n\u003Cp>El endpoint REST \u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode> devuelve un snapshot puntual del libro de órdenes. Para datos en tiempo real, Bybit ofrece un WebSocket público con el stream \u003Ccode>orderbook.50.{symbol}\u003C\u002Fcode> que envía actualizaciones incrementales cada ~50ms.\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Aspecto\u003C\u002Fth>\u003Cth>REST \u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fth>\u003Cth>WebSocket orderbook.50\u003C\u002Fth>\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>Latencia\u003C\u002Ftd>\u003Ctd>~200–500ms por request\u003C\u002Ftd>\u003Ctd>~50–100ms por update\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Rate limit\u003C\u002Ftd>\u003Ctd>10–20 req\u002Fs por IP\u003C\u002Ftd>\u003Ctd>1 conexión por símbolo\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Carga de red\u003C\u002Ftd>\u003Ctd>Alta (snapshot completo)\u003C\u002Ftd>\u003Ctd>Baja (delta incremental)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Complejidad\u003C\u002Ftd>\u003Ctd>Baja (HTTP simple)\u003C\u002Ftd>\u003Ctd>Media (gestión de conexión, reconexión)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Ideal para\u003C\u002Ftd>\u003Ctd>Snapshots puntuales, backtesting\u003C\u002Ftd>\u003Ctd>Trading en vivo, arbitraje\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Para la mayoría de bots de trading en vivo, el WebSocket es superior. Pero para pipelines de datos que necesitan snapshots periódicos (cada minuto, cada hora), el REST con rotación de proxies es más sencillo y suficiente.\u003C\u002Fp>\n\n\u003Ch2>Errores comunes y edge cases\u003C\u002Fh2>\n\n\u003Cul>\n\u003Cli>No comprobar retCode: Bybit devuelve HTTP 200 incluso en errores lógicos. Siempre valida \u003Ccode>retCode == 0\u003C\u002Fcode> antes de procesar \u003Ccode>result\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Rate limit en orderbook: El endpoint de orderbook tiene el límite más estricto. Las ráfagas de más de 20 req\u002Fs por IP provocan bans de 10 minutos.\u003C\u002Fli>\n\u003Cli>Sin backoff exponencial: Reintentar inmediatamente después de un 403 o rate limit empeora el problema. Usa backoff exponencial con jitter.\u003C\u002Fli>\n\u003Cli>Cambio de IP en mitad de paginación: Si rotas IPs durante la paginación de kline, puedes perder velas o duplicarlas. Usa sesiones sticky.\u003C\u002Fli>\n\u003Cli>Ignorar el parámetro category: Es obligatorio. Usar \u003Ccode>spot\u003C\u002Fcode> cuando el símbolo es un perpetuo linear devuelve un error.\u003C\u002Fli>\n\u003Cli>No respetar robots.txt ni ToS: Revisa siempre los términos de servicio de Bybit. Los endpoints públicos están documentados, pero el uso comercial puede tener restricciones.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Configuración de ProxyHat y enlaces útiles\u003C\u002Fh2>\n\n\u003Cp>Para empezar con ProxyHat, consulta la \u003Ca href=\"https:\u002F\u002Fdocs.proxyhat.com\u002F\" rel=\"noopener noreferrer\">documentación oficial de ProxyHat\u003C\u002Fa>. Puedes ver los \u003Ca href=\"\u002Fes\u002Fpricing\">planes de precios\u003C\u002Fa> y elegir entre proxies residenciales, móviles o datacenter.\u003C\u002Fp>\n\n\u003Cp>Para casos de uso de scraping más amplios, revisa nuestra guía de \u003Ca href=\"\u002Fes\u002Fuse-cases\u002Fweb-scraping\">web scraping con proxies\u003C\u002Fa> y de \u003Ca href=\"\u002Fes\u002Fuse-cases\u002Fserp-tracking\">SERP tracking\u003C\u002Fa>.\u003C\u002Fp>\n\n\u003Ch2>Puntos clave\u003C\u002Fh2>\n\n\u003Cul>\n\u003Cli>El envelope retCode\u002FretMsg es obligatorio: Bybit devuelve HTTP 200 incluso en errores. Siempre valida \u003Ccode>retCode == 0\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Rate limits por IP: El endpoint de orderbook es el más restrictivo (~20 req\u002Fs). Los bans duran ~10 minutos.\u003C\u002Fli>\n\u003Cli>Geo-bloqueo a nivel de CloudFront: Usa proxies residenciales con geo-segmentación (\u003Ccode>-country-DE\u003C\u002Fcode>) para evitar 403 de regiones restringidas.\u003C\u002Fli>\n\u003Cli>Sesiones sticky para paginación: Usa \u003Ccode>-sid-abc123\u003C\u002Fcode> en el nombre de usuario para mantener la misma IP durante paginación de kline.\u003C\u002Fli>\n\u003Cli>Backoff exponencial: Esencial para manejar rate limits y bans temporales sin empeorar la situación.\u003C\u002Fli>\n\u003Cli>REST vs WebSocket: Para datos en vivo usa WebSocket \u003Ccode>orderbook.50\u003C\u002Fcode>; para snapshots periódicos, REST con rotación es suficiente.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cblockquote>\u003Cp>¿Listo para scrapear la API de Bybit sin bans ni bloqueos? Configura tus proxies residenciales en \u003Ccode>gate.proxyhat.com:8080\u003C\u002Fcode> y empieza con los ejemplos de esta guía.\u003C\u002Fp>\u003C\u002Fblockquote>","Datos de mercado cripto con proxies rotativos (Bybit V5) | ProxyHat","Guía para scrapear la API de Mercado V5 de Bybit con proxies rotativos. Endpoints, rate limits, geo-bloqueos y código en Python y Node.js con ProxyHat.","\u002Fes\u002Fblog\u002Fscrapear-api-mercado-bybit-v5-proxies",null,"Datos de mercado cripto con proxies rotativos | ProxyHat","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-mercado-bybit-v5-proxies-og.webp",[18,21,24],{"question":19,"answer":20},"¿Por qué importa scrapear la API de Mercado V5 de Bybit con proxies rotativos para usuarios de proxies?","Bybit impone límites de tasa estrictos por IP (10-20 req\u002Fs en orderbook) y bans temporales de ~10 minutos cuando se exceden. También bloquea tráfico de EE. UU., Reino Unido y otras regiones reguladas a nivel de CloudFront. Los proxies rotativos residenciales resuelven ambos problemas distribuyendo el tráfico entre IPs de países permitidos.",{"question":22,"answer":23},"¿Qué tipo de proxy funciona mejor para scrapear la API de Mercado V5 de Bybit?","Los proxies residenciales son los más efectivos porque usan IPs de ISPs reales, lo que reduce la probabilidad de detección y bloqueo. Para scrapear Bybit, se recomienda usar gate.proxyhat.com:8080 (HTTP) o :1080 (SOCKS5) con geo-segmentación a países permitidos como Alemania, Japón o Brasil mediante el flag -country- en el nombre de usuario.",{"question":25,"answer":26},"¿Cómo evitar bloqueos al scrapear la API de Mercado V5 de Bybit con proxies rotativos?","Usa rotación de IPs por petición para distribuir la carga, implementa backoff exponencial con jitter en los reintentos, respeta los límites de tasa por endpoint (especialmente orderbook con ~20 req\u002Fs), usa sesiones sticky (-session-) para paginación de kline, y siempre valida retCode en la respuesta antes de procesar los datos.",false,"2026-08-03T16:09:16+00:00","2026-10-04T01:29:51+00:00","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-mercado-bybit-v5-proxies-master.webp","How to Scrape the Bybit V5 Market API with Rotating Proxies","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-mercado-bybit-v5-proxies-card.webp",12,{"name":35,"slug":36,"bio":37,"avatar_url":14,"website_url":14,"social_links":14},"ProxyHat Team","proxyhat-team","The ProxyHat editorial team covers proxies, web scraping, and data extraction.",[39],{"id":40,"slug":41,"name":42,"description":14,"meta_title":43,"meta_description":44,"parent_id":14},4,"guias-desarrolladores","Guías para Desarrolladores","Guías para Desarrolladores | Blog ProxyHat","Tutoriales de código y SDK para integrar proxies en tus aplicaciones.",[],{"de":47,"fr":48,"pt":49,"ru":50,"zh":51,"pl":52,"ko":53,"it":54,"es":13,"en":55,"ja":56,"x-default":55},"\u002Fde\u002Fblog\u002Fbybit-v5-market-api-mit-rotating-proxies-scrapen","\u002Ffr\u002Fblog\u002Fscraper-api-bybit-v5-proxys-rotatifs","\u002Fpt\u002Fblog\u002Fcomo-raspar-bybit-v5-market-api-proxies","\u002Fru\u002Fblog\u002Fkak-skrejpit-bybit-v5-market-api","\u002Fcn\u002Fblog\u002Fscrape-bybit-v5-market-api-proxy","\u002Fpl\u002Fblog\u002Fjak-scrapowac-bybit-v5-api-proxy","\u002Fko\u002Fblog\u002Fbybit-v5-market-api-proxy-scraping","\u002Fit\u002Fblog\u002Fscrape-bybit-v5-market-api-proxy","\u002Fblog\u002Fscrape-bybit-v5-market-api-rotating-proxies","\u002Fja\u002Fblog\u002Fbybit-v5-market-api-proxy-scraping",{"de":47,"fr":48,"pt":49,"ru":50,"zh":51,"pl":52,"ko":53,"it":54,"es":13,"en":55,"ja":56,"tr":58},"\u002Ftr\u002Fblog\u002Fbybit-v5-market-api-dondurulen-proxy",87,[61,77,93,109],{"id":62,"slug":63,"title":64,"excerpt":65,"featured_image_url":66,"featured_image_alt":67,"card_image_url":68,"og_image_url":69,"published_at":70,"updated_at":70,"content_updated_at":71,"reading_time_minutes":33,"is_featured":27,"indexable":27,"views":72,"author":73,"categories":74,"tags":76},176,"guia-got-scraping-nodejs-proxy-residencial","Guía de got-scraping en Node.js con proxies residenciales","Una guía práctica para desarrolladores sobre cómo usar got-scraping en Node.js: generador de cabeceras, HTTP\u002F2, proxies residenciales y patrones de producción con reintentos, concurrencia y Crawlee.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fguia-got-scraping-nodejs-proxy-residencial-master.webp","got-scraping in Node.js: A Developer's Guide with Residential Proxies","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fguia-got-scraping-nodejs-proxy-residencial-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fguia-got-scraping-nodejs-proxy-residencial-og.webp","2026-08-04T16:05:06+00:00","2026-10-03T08:29:58+00:00",121,{"name":35,"slug":36,"bio":37,"avatar_url":14,"website_url":14,"social_links":14},[75],{"id":40,"slug":41,"name":42,"description":14,"meta_title":43,"meta_description":44,"parent_id":14},[],{"id":78,"slug":79,"title":80,"excerpt":81,"featured_image_url":82,"featured_image_alt":83,"card_image_url":84,"og_image_url":85,"published_at":86,"updated_at":86,"content_updated_at":87,"reading_time_minutes":33,"is_featured":27,"indexable":27,"views":88,"author":89,"categories":90,"tags":92},175,"rotacion-proxies-colly-golang-1","Rotación de proxies en Colly: guía completa para scraping en Go","Aprende a integrar rotación de proxies residenciales en Colly con RoundRobinProxySwitcher, switchers personalizados y patrones de producción para scraping en Go.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Frotacion-proxies-colly-golang-1-master.webp","Rotating Proxies in Colly: A Framework-Idiomatic Guide for Go Developers","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Frotacion-proxies-colly-golang-1-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Frotacion-proxies-colly-golang-1-og.webp","2026-08-04T10:05:34+00:00","2026-10-03T23:53:00+00:00",71,{"name":35,"slug":36,"bio":37,"avatar_url":14,"website_url":14,"social_links":14},[91],{"id":40,"slug":41,"name":42,"description":14,"meta_title":43,"meta_description":44,"parent_id":14},[],{"id":94,"slug":95,"title":96,"excerpt":97,"featured_image_url":98,"featured_image_alt":99,"card_image_url":100,"og_image_url":101,"published_at":102,"updated_at":102,"content_updated_at":103,"reading_time_minutes":33,"is_featured":27,"indexable":27,"views":104,"author":105,"categories":106,"tags":108},194,"scrapear-api-binance-proxies","Cómo scrapear la API REST de Binance con proxies: guía para desarrolladores","Guía técnica con código ejecutable para extraer datos públicos de mercado de la API REST de Binance a escala usando proxies residenciales rotativos. Cubre endpoints, rate limits, weight headers y estrategias de rotación de IP.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-binance-proxies-master.webp","How to Scrape the Binance REST API with Proxies: A Developer Guide","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-binance-proxies-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-binance-proxies-og.webp","2026-08-03T10:04:04+00:00","2026-10-03T08:21:32+00:00",82,{"name":35,"slug":36,"bio":37,"avatar_url":14,"website_url":14,"social_links":14},[107],{"id":40,"slug":41,"name":42,"description":14,"meta_title":43,"meta_description":44,"parent_id":14},[],{"id":110,"slug":111,"title":112,"excerpt":113,"featured_image_url":114,"featured_image_alt":115,"card_image_url":116,"og_image_url":117,"published_at":118,"updated_at":118,"content_updated_at":87,"reading_time_minutes":33,"is_featured":27,"indexable":27,"views":119,"author":120,"categories":121,"tags":123},172,"usar-proxies-en-swift-urlsession-1","Usar proxies en Swift con URLSession: guía práctica para iOS y macOS","Guía completa para configurar proxies residenciales en Swift usando URLSession, con ejemplos de código async\u002Fawait, autenticación, SOCKS5 y mejores prácticas de producción.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fusar-proxies-en-swift-urlsession-1-master.webp","Using Proxies in Swift: A Code-First Guide to URLSession, SOCKS5 & Residential IPs","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fusar-proxies-en-swift-urlsession-1-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fusar-proxies-en-swift-urlsession-1-og.webp","2026-07-26T16:03:44+00:00",230,{"name":35,"slug":36,"bio":37,"avatar_url":14,"website_url":14,"social_links":14},[122],{"id":40,"slug":41,"name":42,"description":14,"meta_title":43,"meta_description":44,"parent_id":14},[],[]]