[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-es-scrapear-api-binance-proxies":3},{"success":4,"payload":5,"related":59,"errors":123},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":11,"og_description":12,"og_image_url":15,"faq_json":16,"key_takeaways":14,"allow_index":26,"noindex_google_only":26,"allow_follow":4,"published_at":27,"updated_at":27,"content_updated_at":28,"featured_image_url":29,"featured_image_alt":30,"card_image_url":31,"reading_time_minutes":32,"author":33,"categories":37,"tags":44,"alternates":45,"locale_paths":52,"views":58},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.","\u003Cp>Si estás construyendo un pipeline de datos de criptomonedas, cómo scrapear la API REST de Binance con proxies es probablemente una de las primeras preguntas que te haces. Binance expone endpoints públicos de mercado que no requieren autenticación, pero aplica un sistema de rate limiting basado en pesos por IP que puede bloquear tu scraper en minutos si no gestionas correctamente la rotación de IPs.\u003C\u002Fp>\n\n\u003Cblockquote>\u003Cp>\u003Cstrong>Aviso de cumplimiento:\u003C\u002Fstrong> Esta guía cubre únicamente \u003Cem>datos públicos de mercado\u003C\u002Fem> (precios, velas, order book). No aborda endpoints autenticados ni trading. Respeta los pesos de la API, los términos de servicio de Binance y las leyes locales aplicables. Donde los términos de Binance requieran acceso oficial via API, úsalo. Consulta los \u003Ca href=\"https:\u002F\u002Fwww.binance.com\u002Fen\u002Fterms\" rel=\"noopener noreferrer\">Términos de Uso de Binance\u003C\u002Fa> antes de implementar cualquier recolección de datos.\u003C\u002Fp>\u003C\u002Fblockquote>\n\n\u003Ch2>Endpoints públicos de mercado y sistema de pesos de Binance\u003C\u002Fh2>\n\n\u003Cp>La API REST de Binance publica datos de mercado bajo \u003Ccode>\u002Fapi\u002Fv3\u002F\u003C\u002Fcode>. Los cuatro endpoints clave para scraping de datos públicos son:\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Endpoint\u003C\u002Fth>\u003Cth>Descripción\u003C\u002Fth>\u003Cth>Peso (request weight)\u003C\u002Fth>\u003Cth>Parámetros clave\u003C\u002Fth>\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>\u003Ccode>GET \u002Fapi\u002Fv3\u002Fklines\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Velas OHLCV (candlesticks)\u003C\u002Ftd>\u003Ctd>1–2 (según limit)\u003C\u002Ftd>\u003Ctd>symbol, interval, limit (max 1000)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Ccode>GET \u002Fapi\u002Fv3\u002Fdepth\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Order book (libro de órdenes)\u003C\u002Ftd>\u003Ctd>5–20 (según limit)\u003C\u002Ftd>\u003Ctd>symbol, limit (5, 10, 20, 50, 100, 500, 1000)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Ccode>GET \u002Fapi\u002Fv3\u002Fticker\u002F24hr\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Estadísticas 24h por símbolo\u003C\u002Ftd>\u003Ctd>1–80 (según parámetros)\u003C\u002Ftd>\u003Ctd>symbol (opcional)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Ccode>GET \u002Fapi\u002Fv3\u002Fticker\u002Fprice\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Precio actual de un símbolo\u003C\u002Ftd>\u003Ctd>1–2\u003C\u002Ftd>\u003Ctd>symbol (opcional)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Cada respuesta incluye el header \u003Ccode>X-MBX-USED-WEIGHT-1M\u003C\u002Fcode>, que indica cuánto peso has consumido en el último minuto desde esa IP. Este header es tu principal mecanismo de feedback para ajustar la velocidad de scraping. Puedes consultar los detalles oficiales en la documentación oficial de la API de Binance.\u003C\u002Fp>\n\n\u003Ch3>¿Por qué el polling de depth agota el presupuesto rápidamente?\u003C\u002Fh3>\n\n\u003Cp>Binance asigna un presupuesto de aproximadamente \u003Cstrong>6000 pesos por minuto por IP\u003C\u002Fstrong>. Si haces polling de \u003Ccode>\u002Fapi\u002Fv3\u002Fdepth\u003C\u002Fcode> con \u003Ccode>limit=100\u003C\u002Fcode> (peso 10) cada segundo, consumes 600 pesos\u002Fminuto — solo 10% del presupuesto. Pero si necesitas \u003Ccode>limit=1000\u003C\u002Fcode> (peso 20) a 5 requests\u002Fsegundo, consumes 6000 pesos\u002Fminuto en un solo símbolo. Con varios símbolos en paralelo, agotas el presupuesto en segundos.\u003C\u002Fp>\n\n\u003Ch2>Rate limiting: pesos, HTTP 429, Retry-After y baneos IP\u003C\u002Fh2>\n\n\u003Cp>El sistema de rate limiting de Binance funciona en tres niveles:\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>\u003Cstrong>HTTP 429 (Too Many Requests):\u003C\u002Fstrong> Tu IP superó el límite de peso. Debes respetar el header \u003Ccode>Retry-After\u003C\u002Fcode> antes de reintentar. Según \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FReference\u002FStatus\u002F429\" rel=\"noopener noreferrer\">MDN Web Docs\u003C\u002Fa>, este header indica los segundos que el cliente debe esperar.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>HTTP 418 (I'm a teapot):\u003C\u002Fstrong> Binance usa este código cuando una IP recibe múltiples 429 sin respetar \u003Ccode>Retry-After\u003C\u002Fcode>. Implica un baneo temporal, típicamente de 2 a 5 minutos, que puede escalar a baneos permanentes si el patrón persiste.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>HTTP 451 (Unavailable For Legal Reasons):\u003C\u002Fstrong> Binance.com bloquea accesos desde EE.UU. por restricciones regulatorias. Esto se explica en el \u003Ca href=\"https:\u002F\u002Fdatatracker.ietf.org\u002Fdoc\u002Fhtml\u002Frfc7725\" rel=\"noopener noreferrer\">RFC 7725\u003C\u002Fa> que define este código de estado.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>La estrategia para evitar estos bloqueos es distribuir el peso de los requests a través de múltiples IPs usando proxies residenciales rotativos. Cada IP recibe su propio presupuesto de 6000 pesos\u002Fminuto, por lo que con 10 IPs efectivamente tienes 60,000 pesos\u002Fminuto de capacidad.\u003C\u002Fp>\n\n\u003Ch2>Rotación de proxies con ProxyHat para Binance\u003C\u002Fh2>\n\n\u003Cp>ProxyHat ofrece proxies residenciales, móviles y datacenter accesibles vía \u003Ccode>gate.proxyhat.com\u003C\u002Fcode>. Para scraping de Binance, los proxies residenciales son ideales porque cada IP parece un usuario legítimo de ISP, reduciendo el riesgo de bloqueos heurísticos.\u003C\u002Fp>\n\n\u003Ch3>Configuración básica con curl\u003C\u002Fh3>\n\n\u003Cp>Empecemos con un ejemplo simple en curl usando el proxy HTTP de ProxyHat:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode># Request básico a \u002Fapi\u002Fv3\u002Fticker\u002Fprice vía ProxyHat (HTTP, puerto 8080)\ncurl -x http:\u002F\u002Fuser-country-US:pass@gate.proxyhat.com:8080 \\\n  &quot;https:\u002F\u002Fapi.binance.com\u002Fapi\u002Fv3\u002Fticker\u002Fprice?symbol=BTCUSDT&quot; \\\n  -H &quot;Accept: application\u002Fjson&quot; \\\n  -D -\n\n# El header X-MBX-USED-WEIGHT-1M aparecerá en la respuesta\n# Para SOCKS5, usa el puerto 1080:\ncurl -x socks5:\u002F\u002Fuser-country-US:pass@gate.proxyhat.com:1080 \\\n  &quot;https:\u002F\u002Fapi.binance.com\u002Fapi\u002Fv3\u002Fticker\u002Fprice?symbol=BTCUSDT&quot;\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>El flag \u003Ccode>-country-US\u003C\u002Fcode> en el username geo-orienta la IP de salida a Estados Unidos. Si necesitas acceder a Binance.com desde una región restringida, puedes cambiar a otro país: \u003Ccode>-country-DE\u003C\u002Fcode>, \u003Ccode>-country-JP\u003C\u002Fcode>, etc. Consulta las \u003Ca href=\"\u002Fes\u002Flocations\">ubicaciones disponibles de ProxyHat\u003C\u002Fa> para ver todos los países soportados.\u003C\u002Fp>\n\n\u003Ch2>Ejemplo 1: Python con requests y proxy raw\u003C\u002Fh2>\n\n\u003Cp>Aquí tienes un scraper básico que rota IPs por request usando el proxy HTTP de ProxyHat directamente:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\nimport random\nimport logging\n\nlogging.basicConfig(level=logging.INFO, format=&apos;%(asctime)s %(levelname)s %(message)s&apos;)\nlogger = logging.getLogger(__name__)\n\nPROXY_BASE = &quot;http:\u002F\u002Fuser:pass@gate.proxyhat.com:8080&quot;\nBINANCE_BASE = &quot;https:\u002F\u002Fapi.binance.com&quot;\n\n# Lista de símbolos a scrapear\nSYMBOLS = [&quot;BTCUSDT&quot;, &quot;ETHUSDT&quot;, &quot;BNBUSDT&quot;, &quot;SOLUSDT&quot;, &quot;XRPUSDT&quot;]\n\ndef get_proxy_url(country=None):\n    &quot;&quot;&quot;Genera URL de proxy con geo-targeting opcional.&quot;&quot;&quot;\n    if country:\n        username = f&quot;user-country-{country}&quot;\n    else:\n        username = &quot;user&quot;\n    return f&quot;http:\u002F\u002F{username}:pass@gate.proxyhat.com:8080&quot;\n\ndef fetch_ticker_price(symbol, max_retries=3):\n    &quot;&quot;&quot;Obtiene el precio actual de un símbolo con reintentos.&quot;&quot;&quot;\n    url = f&quot;{BINANCE_BASE}\u002Fapi\u002Fv3\u002Fticker\u002Fprice&quot;\n    params = {&quot;symbol&quot;: symbol}\n    \n    for attempt in range(max_retries):\n        proxy_url = get_proxy_url()\n        proxies = {&quot;http&quot;: proxy_url, &quot;https&quot;: proxy_url}\n        \n        try:\n            resp = requests.get(url, params=params, proxies=proxies, timeout=10)\n            \n            # Verificar peso consumido\n            used_weight = resp.headers.get(&quot;X-MBX-USED-WEIGHT-1M&quot;, &quot;0&quot;)\n            logger.info(f&quot;{symbol} - Status: {resp.status_code}, Weight used: {used_weight}&quot;)\n            \n            if resp.status_code == 429:\n                retry_after = int(resp.headers.get(&quot;Retry-After&quot;, 5))\n                logger.warning(f&quot;Rate limited. Waiting {retry_after}s...&quot;)\n                time.sleep(retry_after)\n                continue\n            \n            if resp.status_code == 418:\n                logger.error(&quot;IP banned (418). Backing off 120s.&quot;)\n                time.sleep(120)\n                continue\n            \n            resp.raise_for_status()\n            return resp.json()\n            \n        except requests.exceptions.RequestException as e:\n            wait = (2 ** attempt) + random.uniform(0, 1)\n            logger.warning(f&quot;Attempt {attempt+1} failed: {e}. Retrying in {wait:.1f}s&quot;)\n            time.sleep(wait)\n    \n    return None\n\n# Ejecutar scraping de precios\nfor symbol in SYMBOLS:\n    data = fetch_ticker_price(symbol)\n    if data:\n        print(f&quot;{data[&apos;symbol&apos;]}: ${float(data[&apos;price&apos;]):,.2f}&quot;)\n    time.sleep(0.1)  # Throttle entre requests\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Ejemplo 2: Python con ProxyHat SDK y rotación automática\u003C\u002Fh2>\n\n\u003Cp>El SDK de ProxyHat simplifica la gestión de rotación de IPs, sesiones sticky y geo-targeting. Aquí tienes el mismo scraper usando el SDK:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>from proxyhat import ProxyHatClient\nimport requests\nimport time\nimport logging\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n# Inicializar cliente del SDK\nclient = ProxyHatClient(\n    gateway=&quot;gate.proxyhat.com&quot;,\n    http_port=8080,\n    socks5_port=1080,\n    username=&quot;user&quot;,\n    password=&quot;pass&quot;\n)\n\nBINANCE_BASE = &quot;https:\u002F\u002Fapi.binance.com&quot;\nSYMBOLS = [&quot;BTCUSDT&quot;, &quot;ETHUSDT&quot;, &quot;BNBUSDT&quot;, &quot;SOLUSDT&quot;, &quot;XRPUSDT&quot;]\n\ndef fetch_with_sdk(symbol, max_retries=3):\n    &quot;&quot;&quot;Usa el SDK de ProxyHat para rotar IP por request.&quot;&quot;&quot;\n    url = f&quot;{BINANCE_BASE}\u002Fapi\u002Fv3\u002Fticker\u002Fprice&quot;\n    params = {&quot;symbol&quot;: symbol}\n    \n    for attempt in range(max_retries):\n        # El SDK genera automáticamente una nueva IP por request\n        proxy_dict = client.get_http_proxy(country=&quot;US&quot;)\n        proxies = {\n            &quot;http&quot;: proxy_dict[&quot;url&quot;],\n            &quot;https&quot;: proxy_dict[&quot;url&quot;]\n        }\n        \n        try:\n            resp = requests.get(\n                url, params=params, proxies=proxies, timeout=10,\n                headers={&quot;Accept&quot;: &quot;application\u002Fjson&quot;}\n            )\n            \n            used_weight = resp.headers.get(&quot;X-MBX-USED-WEIGHT-1M&quot;, &quot;0&quot;)\n            logger.info(f&quot;{symbol} via IP {proxy_dict[&apos;ip&apos;]} - Weight: {used_weight}&quot;)\n            \n            if resp.status_code == 429:\n                retry_after = int(resp.headers.get(&quot;Retry-After&quot;, 5))\n                logger.warning(f&quot;429 on {proxy_dict[&apos;ip&apos;]}. Rotating IP, waiting {retry_after}s&quot;)\n                time.sleep(retry_after)\n                continue\n            \n            if resp.status_code == 418:\n                logger.error(f&quot;IP {proxy_dict[&apos;ip&apos;]} banned. Rotating immediately.&quot;)\n                client.rotate()\n                time.sleep(60)\n                continue\n            \n            resp.raise_for_status()\n            return resp.json()\n            \n        except Exception as e:\n            wait = (2 ** attempt) * 0.5\n            logger.warning(f&quot;Error: {e}. Retry in {wait}s&quot;)\n            time.sleep(wait)\n    \n    return None\n\nfor symbol in SYMBOLS:\n    result = fetch_with_sdk(symbol)\n    if result:\n        print(f&quot;{result[&apos;symbol&apos;]}: ${float(result[&apos;price&apos;]):,.2f}&quot;)\n    time.sleep(0.05)\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Ejemplo 3: Backfill de klines con sesión sticky (httpx async)\u003C\u002Fh2>\n\n\u003Cp>Para backfills históricos de klines necesitas paginar con \u003Ccode>endTime\u003C\u002Fcode>. Una sesión sticky mantiene la misma IP durante toda la sesión, evitando que Binance vea saltos de IP sospechosos en medio de una secuencia de paginación:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import httpx\nimport asyncio\nimport logging\nfrom datetime import datetime, timezone\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\nBINANCE_BASE = &quot;https:\u002F\u002Fapi.binance.com&quot;\nSESSION_ID = &quot;klines-backfill-btc-001&quot;\n\n# Proxy con sesión sticky: misma IP durante toda la sesión\nPROXY_URL = f&quot;http:\u002F\u002Fuser-sid-{SESSION_ID}-country-DE:pass@gate.proxyhat.com:8080&quot;\n\nasync def fetch_klines_page(symbol, interval, end_time, limit=1000):\n    &quot;&quot;&quot;Obtiene una página de klines usando sesión sticky.&quot;&quot;&quot;\n    url = f&quot;{BINANCE_BASE}\u002Fapi\u002Fv3\u002Fklines&quot;\n    params = {\n        &quot;symbol&quot;: symbol,\n        &quot;interval&quot;: interval,\n        &quot;endTime&quot;: end_time,\n        &quot;limit&quot;: limit\n    }\n    \n    async with httpx.AsyncClient(\n        proxy=PROXY_URL,\n        timeout=15.0,\n        headers={&quot;Accept&quot;: &quot;application\u002Fjson&quot;}\n    ) as client:\n        for attempt in range(4):\n            try:\n                resp = await client.get(url, params=params)\n                \n                weight = resp.headers.get(&quot;X-MBX-USED-WEIGHT-1M&quot;, &quot;0&quot;)\n                logger.info(f&quot;Klines {symbol} - Status: {resp.status_code}, Weight: {weight}&quot;)\n                \n                if resp.status_code == 429:\n                    retry_after = int(resp.headers.get(&quot;Retry-After&quot;, 5))\n                    logger.warning(f&quot;429. Waiting {retry_after}s (sticky session preserved)&quot;)\n                    await asyncio.sleep(retry_after)\n                    continue\n                \n                resp.raise_for_status()\n                return resp.json()\n                \n            except (httpx.HTTPError, httpx.HTTPStatusError) as e:\n                wait = (2 ** attempt) * 0.5\n                logger.warning(f&quot;Attempt {attempt+1}: {e}. Retry in {wait}s&quot;)\n                await asyncio.sleep(wait)\n    \n    return []\n\nasync def backfill_klines(symbol, interval, start_ts, end_ts):\n    &quot;&quot;&quot;Backfill completo de klines con paginación.&quot;&quot;&quot;\n    all_klines = []\n    current_end = end_ts\n    \n    while current_end &gt; start_ts:\n        klines = await fetch_klines_page(symbol, interval, current_end)\n        \n        if not klines:\n            logger.warning(&quot;No data returned, stopping.&quot;)\n            break\n        \n        all_klines.extend(klines)\n        # El endTime de la próxima página es el openTime del primer kline - 1\n        current_end = klines[0][0] - 1\n        \n        logger.info(f&quot;Fetched {len(klines)} klines. Total: {len(all_klines)}&quot;)\n        \n        # Weight-aware throttle: si weight &gt; 4000, ralentizamos\n        # (el header se loguea dentro de fetch_klines_page)\n        await asyncio.sleep(0.2)\n    \n    return all_klines\n\n# Ejecutar backfill de BTCUSDT 1h desde 2024-01-01\nstart = int(datetime(2024, 1, 1, tzinfo=timezone.utc).timestamp() * 1000)\nend = int(datetime(2024, 6, 1, tzinfo=timezone.utc).timestamp() * 1000)\n\ndata = asyncio.run(backfill_klines(&quot;BTCUSDT&quot;, &quot;1h&quot;, start, end))\nprint(f&quot;Total klines descargados: {len(data)}&quot;)\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>La sesión sticky (\u003Ccode>-sid-klines-backfill-btc-001\u003C\u002Fcode>) garantiza que Binance vea todos los requests de paginación desde la misma IP, lo que es más natural y reduce el riesgo de bloqueos. Para más casos de uso de scraping con proxies, visita nuestra guía de \u003Ca href=\"\u002Fes\u002Fuse-cases\u002Fweb-scraping\">web scraping con ProxyHat\u003C\u002Fa>.\u003C\u002Fp>\n\n\u003Ch2>Ejemplo 4: Node.js con axios y rotación de IPs\u003C\u002Fh2>\n\n\u003Cp>Para equipos de JavaScript\u002FTypeScript, aquí tienes un ejemplo equivalente con axios:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>const axios = require(&apos;axios&apos;);\nconst HttpsProxyAgent = require(&apos;https-proxy-agent&apos;);\n\nconst BINANCE_BASE = &apos;https:\u002F\u002Fapi.binance.com&apos;;\nconst PROXY_HOST = &apos;gate.proxyhat.com&apos;;\nconst PROXY_PORT = 8080;\n\nconst SYMBOLS = [&apos;BTCUSDT&apos;, &apos;ETHUSDT&apos;, &apos;BNBUSDT&apos;, &apos;SOLUSDT&apos;, &apos;XRPUSDT&apos;];\n\n\u002F\u002F Pool de proxies con geo-targeting\nconst COUNTRIES = [&apos;US&apos;, &apos;DE&apos;, &apos;JP&apos;, &apos;GB&apos;, &apos;SG&apos;];\nlet countryIndex = 0;\n\nfunction getProxyAgent() {\n  const country = COUNTRIES[countryIndex % COUNTRIES.length];\n  countryIndex++;\n  \n  const proxyUrl = `http:\u002F\u002Fuser-country-${country}:pass@${PROXY_HOST}:${PROXY_PORT}`;\n  return {\n    agent: new HttpsProxyAgent(proxyUrl),\n    country\n  };\n}\n\nasync function fetchTicker24hr(symbol, maxRetries = 3) {\n  const url = `${BINANCE_BASE}\u002Fapi\u002Fv3\u002Fticker\u002F24hr`;\n  \n  for (let attempt = 0; attempt &lt; maxRetries; attempt++) {\n    const { agent, country } = getProxyAgent();\n    \n    try {\n      const resp = await axios.get(url, {\n        params: { symbol },\n        httpsAgent: agent,\n        timeout: 10000,\n        headers: { &apos;Accept&apos;: &apos;application\u002Fjson&apos; }\n      });\n      \n      const weight = resp.headers[&apos;x-mbx-used-weight-1m&apos;] || &apos;0&apos;;\n      console.log(`${symbol} [${country}] - Weight: ${weight}`);\n      \n      return resp.data;\n    } catch (err) {\n      if (err.response) {\n        const status = err.response.status;\n        const retryAfter = err.response.headers[&apos;retry-after&apos;];\n        \n        if (status === 429) {\n          const wait = parseInt(retryAfter || &apos;5&apos;);\n          console.warn(`429 [${country}] - Waiting ${wait}s`);\n          await new Promise(r =&gt; setTimeout(r, wait * 1000));\n          continue;\n        }\n        \n        if (status === 418) {\n          console.error(`418 IP ban [${country}] - Backing off 120s`);\n          await new Promise(r =&gt; setTimeout(r, 120000));\n          continue;\n        }\n      }\n      \n      const wait = Math.pow(2, attempt) * 500;\n      console.warn(`Attempt ${attempt + 1} failed: ${err.message}. Retry in ${wait}ms`);\n      await new Promise(r =&gt; setTimeout(r, wait));\n    }\n  }\n  return null;\n}\n\n\u002F\u002F Ejecutar con concurrencia limitada\nasync function main() {\n  const CONCURRENCY = 3;\n  const results = [];\n  \n  for (let i = 0; i &lt; SYMBOLS.length; i += CONCURRENCY) {\n    const batch = SYMBOLS.slice(i, i + CONCURRENCY);\n    const batchResults = await Promise.all(\n      batch.map(s =&gt; fetchTicker24hr(s))\n    );\n    results.push(...batchResults);\n    \n    \u002F\u002F Throttle entre batches\n    await new Promise(r =&gt; setTimeout(r, 200));\n  }\n  \n  results.filter(Boolean).forEach(d =&gt; {\n    console.log(`${d.symbol}: ${parseFloat(d.lastPrice).toFixed(2)} USDT`);\n  });\n}\n\nmain();\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Ejemplo 5: Throttle consciente del peso con circuit breaker\u003C\u002Fh2>\n\n\u003Cp>Un patrón de producción combina el monitoreo de \u003Ccode>X-MBX-USED-WEIGHT-1M\u003C\u002Fcode> con un circuit breaker que pausa el scraping cuando el peso se acerca al límite:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import httpx\nimport asyncio\nimport logging\nimport random\nfrom dataclasses import dataclass\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n@dataclass\nclass WeightTracker:\n    current_weight: int = 0\n    max_weight: int = 5500  # Margen de seguridad bajo 6000\n    cooldown_until: float = 0.0\n\nclass BinanceScraper:\n    def __init__(self, proxy_url, max_concurrency=5):\n        self.proxy_url = proxy_url\n        self.tracker = WeightTracker()\n        self.semaphore = asyncio.Semaphore(max_concurrency)\n        self.base_url = &quot;https:\u002F\u002Fapi.binance.com&quot;\n    \n    async def _request(self, endpoint, params):\n        async with self.semaphore:\n            # Circuit breaker: si estamos en cooldown, esperar\n            now = asyncio.get_event_loop().time()\n            if now &lt; self.tracker.cooldown_until:\n                wait = self.tracker.cooldown_until - now\n                logger.info(f&quot;Circuit breaker active. Waiting {wait:.1f}s&quot;)\n                await asyncio.sleep(wait)\n            \n            url = f&quot;{self.base_url}{endpoint}&quot;\n            \n            async with httpx.AsyncClient(\n                proxy=self.proxy_url, timeout=10.0\n            ) as client:\n                for attempt in range(4):\n                    try:\n                        resp = await client.get(url, params=params)\n                        \n                        # Actualizar peso consumido\n                        weight_header = resp.headers.get(&quot;X-MBX-USED-WEIGHT-1M&quot;)\n                        if weight_header:\n                            self.tracker.current_weight = int(weight_header)\n                            \n                            # Si weight &gt; 80% del max, activar cooldown\n                            if self.tracker.current_weight &gt; self.tracker.max_weight * 0.8:\n                                cooldown = 10 + random.uniform(0, 5)\n                                self.tracker.cooldown_until = now + cooldown\n                                logger.warning(\n                                    f&quot;Weight {self.tracker.current_weight}\u002F6000. &quot;\n                                    f&quot;Cooldown {cooldown:.1f}s&quot;\n                                )\n                        \n                        if resp.status_code == 429:\n                            retry_after = int(resp.headers.get(&quot;Retry-After&quot;, 5))\n                            self.tracker.cooldown_until = now + retry_after\n                            await asyncio.sleep(retry_after)\n                            continue\n                        \n                        if resp.status_code == 418:\n                            self.tracker.cooldown_until = now + 120\n                            await asyncio.sleep(120)\n                            continue\n                        \n                        resp.raise_for_status()\n                        return resp.json()\n                        \n                    except httpx.HTTPError as e:\n                        wait = (2 ** attempt) * 0.5 + random.uniform(0, 0.5)\n                        logger.warning(f&quot;Retry {attempt+1}: {e}. Wait {wait:.1f}s&quot;)\n                        await asyncio.sleep(wait)\n                \n                return None\n    \n    async def fetch_depth(self, symbol, limit=100):\n        return await self._request(\n            &quot;\u002Fapi\u002Fv3\u002Fdepth&quot;,\n            {&quot;symbol&quot;: symbol, &quot;limit&quot;: limit}\n        )\n    \n    async def fetch_klines(self, symbol, interval=&quot;1m&quot;, limit=500):\n        return await self._request(\n            &quot;\u002Fapi\u002Fv3\u002Fklines&quot;,\n            {&quot;symbol&quot;: symbol, &quot;interval&quot;: interval, &quot;limit&quot;: limit}\n        )\n\n# Uso con sesión sticky para datos consistentes\nproxy = &quot;http:\u002F\u002Fuser-sid-scraping-001-country-DE:pass@gate.proxyhat.com:8080&quot;\nscraper = BinanceScraper(proxy, max_concurrency=3)\n\nasync def main():\n    symbols = [&quot;BTCUSDT&quot;, &quot;ETHUSDT&quot;, &quot;BNBUSDT&quot;]\n    \n    for symbol in symbols:\n        depth = await scraper.fetch_depth(symbol, limit=100)\n        klines = await scraper.fetch_klines(symbol, interval=&quot;1m&quot;, limit=500)\n        \n        if depth and klines:\n            print(f&quot;{symbol}: {len(depth[&apos;bids&apos;])} bids, {len(klines)} klines&quot;)\n        \n        await asyncio.sleep(0.3)\n\nasyncio.run(main())\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>¿Cuándo usar WebSocket en lugar de REST polling?\u003C\u002Fh2>\n\n\u003Cp>Para datos en tiempo real, los WebSocket streams de Binance (\u003Ccode>wss:\u002F\u002Fstream.binance.com:9443\u003C\u002Fcode>) son superiores al polling REST en la mayoría de los casos:\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Criterio\u003C\u002Fth>\u003Cth>REST Polling\u003C\u002Fth>\u003Cth>WebSocket Streams\u003C\u002Fth>\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>Latencia\u003C\u002Ftd>\u003Ctd>50–200ms por request\u003C\u002Ftd>\u003Ctd>&lt;50ms (push en tiempo real)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Consumo de peso\u003C\u002Ftd>\u003Ctd>1–20 por request\u003C\u002Ftd>\u003Ctd>0 (no aplica rate limit de peso)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Datos históricos\u003C\u002Ftd>\u003Ctd>Ideal para backfills\u003C\u002Ftd>\u003Ctd>No soporta históricos\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Order book depth\u003C\u002Ftd>\u003Ctd>Snapshot puntual\u003C\u002Ftd>\u003Ctd>Actualizaciones incrementales (diff)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Complejidad\u003C\u002Ftd>\u003Ctd>Baja (HTTP estándar)\u003C\u002Ftd>\u003Ctd>Media (gestión de reconexión, ping\u002Fpong)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Con proxies\u003C\u002Ftd>\u003Ctd>Funciona con HTTP\u002FSOCKS5 proxy\u003C\u002Ftd>\u003Ctd>Requiere proxy que soporte WSS\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>La regla general: usa REST con proxies para backfills históricos y snapshots puntuales. Usa WebSocket para streaming en tiempo real donde necesitas baja latencia. Si necesitas tracking de SERPs u otros datos además de Binance, consulta nuestra guía de \u003Ca href=\"\u002Fes\u002Fuse-cases\u002Fserp-tracking\">SERP tracking con proxies\u003C\u002Fa>.\u003C\u002Fp>\n\n\u003Ch2>Errores comunes y edge cases\u003C\u002Fh2>\n\n\u003Cul>\n\u003Cli>Ignorar X-MBX-USED-WEIGHT-1M: Sin monitorear este header, no sabes cuándo estás cerca del límite. Léalo en cada response.\u003C\u002Fli>\n\u003Cli>No respetar Retry-After en 429: Reintentar inmediatamente después de un 429 casi garantiza un 418 (baneo de IP).\u003C\u002Fli>\n\u003Cli>Usar proxies datacenter para Binance: Binance detecta IPs de datacenter conocidas. Los proxies residenciales tienen mayor tasa de éxito.\u003C\u002Fli>\n\u003Cli>Concurrencia sin límite: Aunque rotes IPs, demasiados requests concurrentes desde la misma sesión pueden disparar heurísticas anti-bot.\u003C\u002Fli>\n\u003Cli>No manejar HTTP 451: Si accedes a Binance.com desde EE.UU. sin geo-targeting apropiado, recibirás 451. Usa \u003Ccode>-country-DE\u003C\u002Fcode> o \u003Ccode>-country-JP\u003C\u002Fcode> para evitarlo.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>Configuración de ProxyHat para Binance\u003C\u002Fh2>\n\n\u003Cp>Para empezar a scrapear Binance con ProxyHat:\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>Crea una cuenta en \u003Ca href=\"\u002Fes\u002Fpricing\">ProxyHat\u003C\u002Fa> y obtén tus credenciales.\u003C\u002Fli>\n\u003Cli>Configura el gateway: \u003Ccode>gate.proxyhat.com:8080\u003C\u002Fcode> (HTTP) o \u003Ccode>:1080\u003C\u002Fcode> (SOCKS5).\u003C\u002Fli>\n\u003Cli>Usa geo-targeting para evitar el bloqueo regional: \u003Ccode>user-country-DE:pass@gate.proxyhat.com:8080\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Para backfills, usa sesiones sticky: \u003Ccode>user-sid-{id}-country-DE:pass@...\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Para scraping en tiempo real, rota IPs por request omitiendo el flag de sesión.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>Consulta la \u003Ca href=\"https:\u002F\u002Fdocs.proxyhat.com\u002F\" rel=\"noopener noreferrer\">documentación oficial de ProxyHat\u003C\u002Fa> para más detalles sobre el SDK y configuración avanzada.\u003C\u002Fp>\n\n\u003Ch2>Puntos clave\u003C\u002Fh2>\n\n\u003Cblockquote>\n\u003Cul>\n\u003Cli>Monitorea X-MBX-USED-WEIGHT-1M en cada response para ajustar tu velocidad de scraping dinámicamente.\u003C\u002Fli>\n\u003Cli>Usa proxies residenciales rotativos para distribuir el peso de rate limiting entre múltiples IPs (6000 pesos\u002Fmin por IP).\u003C\u002Fli>\n\u003Cli>Sesiones sticky (\u003Ccode>-sid-{id}\u003C\u002Fcode>) para backfills paginados de klines; rotación por request para snapshots.\u003C\u002Fli>\n\u003Cli>Respeta 429 y Retry-After para evitar escalar a baneos 418 que pueden durar de 2 a 5 minutos.\u003C\u002Fli>\n\u003Cli>Geo-targeting con \u003Ccode>-country-{code}\u003C\u002Fcode> para evitar HTTP 451 en regiones restringidas.\u003C\u002Fli>\n\u003Cli>Prefiere WebSocket para streaming en tiempo real; usa REST con proxies para históricos.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fblockquote>","Scrapear API REST de Binance con proxies | ProxyHat","Guía con código para scrapear la API REST de Binance con proxies: endpoints, rate limits, weight headers, rotación de IPs y ejemplos en Python y Node.js.","\u002Fes\u002Fblog\u002Fscrapear-api-binance-proxies",null,"https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-binance-proxies-og.webp",[17,20,23],{"question":18,"answer":19},"¿Por qué importa scrapear la API REST de Binance con proxies para usuarios de proxies?","Porque Binance usa un sistema de rate limiting basado en peso por IP, no por cuenta. Sin rotación de proxies, un scraper que hace polling intensivo de order book o klines agota los 6000 pesos\u002Fminuto en segundos y recibe HTTP 429. Los proxies residenciales rotativos permiten distribuir el consumo entre muchas IPs, multiplicando la capacidad efectiva y evitando baneos.",{"question":21,"answer":22},"¿Cómo evitas bloqueos al scrapear la API REST de Binance con proxies?","Para evitar bloqueos: (1) monitorea el header X-MBX-USED-WEIGHT-1M y reduce la velocidad cuando se acerque a 5500; (2) respeta el header Retry-After en cada HTTP 429 antes de reintentar; (3) rota IPs por request para distribuir el peso; (4) usa sesiones sticky para secuencias de paginación que se vean naturales; (5) implementa backoff exponencial con jitter; (6) limita la concurrencia por sesión a 3-5 requests paralelos.",{"question":24,"answer":25},"¿Cuándo debo usar WebSocket en lugar de REST polling para Binance?","Usa WebSocket streams cuando necesitas datos en tiempo real con baja latencia (menos de 50ms) y no estás haciendo backfills históricos. Los WebSockets no consumen peso de rate limiting, pero requieren gestionar reconexiones y mensajes ping\u002Fpong. Para backfills históricos o snapshots puntuales, el REST polling con proxies sigue siendo la mejor opción.",false,"2026-08-03T10:04:04+00:00","2026-10-03T08:21:32+00:00","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",12,{"name":34,"slug":35,"bio":36,"avatar_url":14,"website_url":14,"social_links":14},"ProxyHat Team","proxyhat-team","The ProxyHat editorial team covers proxies, web scraping, and data extraction.",[38],{"id":39,"slug":40,"name":41,"description":14,"meta_title":42,"meta_description":43,"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.",[],{"fr":46,"en":47,"pt":48,"ko":49,"ru":50,"zh":51,"x-default":47},"\u002Ffr\u002Fblog\u002Fcomment-scraper-api-rest-binance-proxies","\u002Fblog\u002Fscrape-binance-rest-api-proxies","\u002Fpt\u002Fblog\u002Fcomo-raspar-api-binance-proxies","\u002Fko\u002Fblog\u002Fbinance-rest-api-proxy-scraping","\u002Fru\u002Fblog\u002Fkak-skrejpit-binance-rest-api-proksi","\u002Fcn\u002Fblog\u002Fbinance-rest-api-proxy-scraping-guide",{"tr":53,"fr":46,"en":47,"ja":54,"pt":48,"ko":49,"pl":55,"es":13,"ru":50,"zh":51,"it":56,"de":57},"\u002Ftr\u002Fblog\u002Fbinance-rest-api-proxy-tarama","\u002Fja\u002Fblog\u002Fbinance-api-scraping-proxy","\u002Fpl\u002Fblog\u002Fjak-scrapowac-rest-api-binance-proxy","\u002Fit\u002Fblog\u002Fscraping-binance-rest-api-con-proxy","\u002Fde\u002Fblog\u002Fbinance-rest-api-mit-proxies-scrapen",81,[60,76,92,108],{"id":61,"slug":62,"title":63,"excerpt":64,"featured_image_url":65,"featured_image_alt":66,"card_image_url":67,"og_image_url":68,"published_at":69,"updated_at":69,"content_updated_at":70,"reading_time_minutes":32,"is_featured":26,"indexable":26,"views":71,"author":72,"categories":73,"tags":75},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",120,{"name":34,"slug":35,"bio":36,"avatar_url":14,"website_url":14,"social_links":14},[74],{"id":39,"slug":40,"name":41,"description":14,"meta_title":42,"meta_description":43,"parent_id":14},[],{"id":77,"slug":78,"title":79,"excerpt":80,"featured_image_url":81,"featured_image_alt":82,"card_image_url":83,"og_image_url":84,"published_at":85,"updated_at":85,"content_updated_at":86,"reading_time_minutes":32,"is_featured":26,"indexable":26,"views":87,"author":88,"categories":89,"tags":91},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":34,"slug":35,"bio":36,"avatar_url":14,"website_url":14,"social_links":14},[90],{"id":39,"slug":40,"name":41,"description":14,"meta_title":42,"meta_description":43,"parent_id":14},[],{"id":93,"slug":94,"title":95,"excerpt":96,"featured_image_url":97,"featured_image_alt":98,"card_image_url":99,"og_image_url":100,"published_at":101,"updated_at":101,"content_updated_at":102,"reading_time_minutes":32,"is_featured":26,"indexable":4,"views":103,"author":104,"categories":105,"tags":107},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.","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","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fes\u002Fscrapear-api-mercado-bybit-v5-proxies-og.webp","2026-08-03T16:09:16+00:00","2026-10-04T01:29:51+00:00",86,{"name":34,"slug":35,"bio":36,"avatar_url":14,"website_url":14,"social_links":14},[106],{"id":39,"slug":40,"name":41,"description":14,"meta_title":42,"meta_description":43,"parent_id":14},[],{"id":109,"slug":110,"title":111,"excerpt":112,"featured_image_url":113,"featured_image_alt":114,"card_image_url":115,"og_image_url":116,"published_at":117,"updated_at":117,"content_updated_at":86,"reading_time_minutes":32,"is_featured":26,"indexable":26,"views":118,"author":119,"categories":120,"tags":122},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":34,"slug":35,"bio":36,"avatar_url":14,"website_url":14,"social_links":14},[121],{"id":39,"slug":40,"name":41,"description":14,"meta_title":42,"meta_description":43,"parent_id":14},[],[]]