[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-en-scrape-bybit-v5-market-api-rotating-proxies":3},{"success":4,"payload":5,"related":67,"errors":156},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":16,"og_image_url":17,"faq_json":18,"key_takeaways":14,"allow_index":4,"noindex_google_only":34,"allow_follow":4,"published_at":35,"updated_at":35,"content_updated_at":36,"featured_image_url":37,"featured_image_alt":38,"card_image_url":39,"reading_time_minutes":40,"author":41,"categories":45,"tags":52,"alternates":53,"locale_paths":64,"views":66},195,"scrape-bybit-v5-market-api-rotating-proxies","Unified Crypto Market Data APIs with Rotating Proxies: Rate Limits, Geo-Blocks and Pagination (Bybit V5 Example)","A developer-first guide to scraping Bybit's unified V5 Market API endpoints with rotating residential proxies, rate-limit budgeting, retries, and sticky-session pagination.","\u003Cp>\u003Cstrong>Disclaimer:\u003C\u002Fstrong> Public market data from Bybit is generally accessible under their API Terms of Service, but you are responsible for reviewing and complying with \u003Ca href=\"https:\u002F\u002Fwww.bybit.com\u002Fen-US\u002Fhelp\u002FbybitIntro\" rel=\"noopener noreferrer\">Bybit's Terms of Service\u003C\u002Fa> and any applicable regional regulations before scraping. This guide covers only \u003Cem>public\u003C\u002Fem> market endpoints — not authenticated private account or trading endpoints.\u003C\u002Fp>\n\n\u003Cp>If you are building a crypto trading bot, a market-data pipeline, or an analytics dashboard, you will eventually hit Bybit's unified V5 Market API. The good news is that the V5 interface consolidates spot, linear derivatives, inverse contracts, and options behind a single set of public endpoints. The bad news is that Bybit enforces strict per-IP rate limits and applies geo-restrictions that can return \u003Ccode>403\u003C\u002Fcode> responses without warning. Learning how to scrape the Bybit V5 Market API with rotating proxies is the difference between a reliable data feed and a banned IP address.\u003C\u002Fp>\n\n\u003Cp>This guide maps every public endpoint under \u003Ccode>\u002Fv5\u002Fmarket\u002F\u003C\u002Fcode>, explains Bybit's rate-limit and geo-block behavior, and provides runnable Node.js and Python examples using both raw proxy URLs and the ProxyHat SDK against \u003Ccode>gate.proxyhat.com:8080\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch2>Bybit V5 Market API Endpoints Explained\u003C\u002Fh2>\n\n\u003Cp>Bybit's V5 API is documented under the official V5 reference. All public market-data endpoints share the base URL \u003Ccode>https:\u002F\u002Fapi.bybit.com\u003C\u002Fcode> and return a consistent JSON envelope:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>{\n  &quot;retCode&quot;: 0,\n  &quot;retMsg&quot;: &quot;OK&quot;,\n  &quot;result&quot;: { ... },\n  &quot;time&quot;: 1700000000000\n}\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>A non-zero \u003Ccode>retCode\u003C\u002Fcode> means the request was processed but returned an application-level error (invalid symbol, bad category, etc.). A \u003Ccode>403\u003C\u002Fcode> HTTP status, on the other hand, typically means an IP-level block — either rate-limit or geo-restriction. Your client must handle both layers.\u003C\u002Fp>\n\n\u003Ch3>The Four Core Public Market Endpoints\u003C\u002Fh3>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Endpoint\u003C\u002Fth>\u003Cth>Path\u003C\u002Fth>\u003Cth>Key Params\u003C\u002Fth>\u003Cth>Use Case\u003C\u002Fth>\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>Order Book\u003C\u002Ftd>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, \u003Ccode>symbol\u003C\u002Fcode>, \u003Ccode>limit\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Depth snapshots (bids\u002Fasks)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Tickers\u003C\u002Ftd>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Ftickers\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, optional \u003Ccode>symbol\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Latest price, 24h volume\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Kline (Candles)\u003C\u002Ftd>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Fkline\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, \u003Ccode>symbol\u003C\u002Fcode>, \u003Ccode>interval\u003C\u002Fcode>, \u003Ccode>start\u003C\u002Fcode>, \u003Ccode>end\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>OHLCV historical bars\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Recent Trades\u003C\u002Ftd>\u003Ctd>\u003Ccode>\u002Fv5\u002Fmarket\u002Frecent-trade\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>\u003Ccode>category\u003C\u002Fcode>, \u003Ccode>symbol\u003C\u002Fcode>, \u003Ccode>limit\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>Latest executed trades\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>The \u003Ccode>category\u003C\u002Fcode> parameter is central to the V5 design. It accepts values like \u003Ccode>spot\u003C\u002Fcode>, \u003Ccode>linear\u003C\u002Fcode>, \u003Ccode>inverse\u003C\u002Fcode>, and \u003Ccode>option\u003C\u002Fcode>. The same endpoint path serves all product types — you just change the category. For example, requesting \u003Ccode>api.bybit.com\u002Fv5\u002Fmarket\u002Forderbook?category=spot&symbol=BTCUSDT&limit=50\u003C\u002Fcode> returns the spot order book, while \u003Ccode>category=linear\u003C\u002Fcode> returns the USDT perpetual order book for the same symbol.\u003C\u002Fp>\n\n\u003Cp>The \u003Ccode>limit\u003C\u002Fcode> parameter on \u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode> controls depth: valid values are 1, 25, 50, 100, 500, 1000. Larger depths cost more in response payload size and are more likely to trip rate limits if you poll aggressively.\u003C\u002Fp>\n\n\u003Ch2>Bybit's IP-Based Rate Limits and the ~10-Minute Ban Window\u003C\u002Fh2>\n\n\u003Cp>Bybit applies per-endpoint rate limits at the IP level. According to Bybit's official rate-limit documentation, public market endpoints typically allow around \u003Cstrong>200 requests per second\u003C\u002Fstrong> per IP for some endpoints, but stricter caps apply to specific paths. The \u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode> endpoint is one of the most aggressively limited because it is heavily polled by high-frequency bots.\u003C\u002Fp>\n\n\u003Cp>When you exceed the per-IP rate limit, Bybit returns an HTTP \u003Ccode>403\u003C\u002Fcode> with a JSON body containing a \u003Ccode>retCode\u003C\u002Fcode> and \u003Ccode>retMsg\u003C\u002Fcode> indicating rate-limit violation. Once an IP is flagged, it may be temporarily banned for approximately \u003Cstrong>10 minutes\u003C\u002Fstrong>. During that window, every request from that IP returns \u003Ccode>403\u003C\u002Fcode> — there is no partial throttling or retry-after header in most cases.\u003C\u002Fp>\n\n\u003Cp>This is why bursts on \u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode> are dangerous. If your scraper polls order books for 50 symbols in a tight loop without rate budgeting, you can exhaust the per-IP allowance in under a second and earn a 10-minute ban. Rotating \u003Ca href=\"\u002Fproxy\u002Fresidential\">residential proxies\u003C\u002Fa> solve this by distributing requests across many IPs, each with its own rate-limit budget.\u003C\u002Fp>\n\n\u003Ch2>Geo-Restrictions: 403s and CloudFront Blocks\u003C\u002Fh2>\n\n\u003Cp>Bybit restricts access from certain jurisdictions, including the United States, the United Kingdom, and other regions listed in their terms. When a request originates from a restricted IP range, Bybit (or its CloudFront CDN layer) may return a \u003Ccode>403\u003C\u002Fcode> or a CloudFront error page before the request even reaches the API backend. This is distinct from a rate-limit \u003Ccode>403\u003C\u002Fcode> — the response body will not contain the standard \u003Ccode>retCode\u003C\u002Fcode> envelope.\u003C\u002Fp>\n\n\u003Cp>Residential proxies solve this by routing your requests through IPs in allowed countries. With ProxyHat, you can geo-target the exit IP using the \u003Ccode>-country-\u003C\u002Fcode> flag in the username. For example, \u003Ccode>user-country-DE\u003C\u002Fcode> routes through a German residential IP, which is not on Bybit's restricted list.\u003C\u002Fp>\n\n\u003Cp>This is also relevant for data consistency: if you need market data that is identical to what a trader in Japan sees, you can use \u003Ccode>user-country-JP\u003C\u002Fcode> to ensure your requests exit from a Japanese IP.\u003C\u002Fp>\n\n\u003Ch2>Raw Proxy Usage vs ProxyHat SDK\u003C\u002Fh2>\n\n\u003Cp>ProxyHat supports two integration patterns. The first is raw proxy URL usage — you pass the proxy string directly to any HTTP client. The second is the ProxyHat SDK, which handles rotation, retries, and session management for you. Below, every example shows both patterns side by side.\u003C\u002Fp>\n\n\u003Ch3>ProxyHat Connection Details\u003C\u002Fh3>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Protocol\u003C\u002Fth>\u003Cth>URL Format\u003C\u002Fth>\u003Cth>Port\u003C\u002Fth>\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>HTTP\u003C\u002Ftd>\u003Ctd>\u003Ccode>http:\u002F\u002FUSERNAME:PASSWORD@gate.proxyhat.com:8080\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>8080\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>SOCKS5\u003C\u002Ftd>\u003Ctd>\u003Ccode>socks5:\u002F\u002FUSERNAME:PASSWORD@gate.proxyhat.com:1080\u003C\u002Fcode>\u003C\u002Ftd>\u003Ctd>1080\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Geo-targeting and session flags go in the username field, separated by hyphens:\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Ccode>user-country-DE:pass\u003C\u002Fcode> — German residential exit IP\u003C\u002Fli>\n\u003Cli>\u003Ccode>user-country-US-state-california:pass\u003C\u002Fcode> — US state-level targeting\u003C\u002Fli>\n\u003Cli>\u003Ccode>user-sid-abc123:pass\u003C\u002Fcode> — sticky session (same IP for the session lifetime)\u003C\u002Fli>\n\u003Cli>\u003Ccode>user-country-DE-sid-kline01:pass\u003C\u002Fcode> — country + sticky session combined\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>For Bybit scraping, we recommend residential proxies from an allowed country (e.g., \u003Ccode>DE\u003C\u002Fcode>, \u003Ccode>JP\u003C\u002Fcode>, \u003Ccode>SG\u003C\u002Fcode>) with per-request rotation for order-book polling, and sticky sessions for kline pagination. See our \u003Ca href=\"\u002Flocations\">proxy locations page\u003C\u002Fa> for the full list of supported countries.\u003C\u002Fp>\n\n\u003Ch2>Example 1: curl — Fetch Order Book via Raw HTTP Proxy\u003C\u002Fh2>\n\n\u003Cp>The simplest way to verify your proxy works with Bybit is a single \u003Ccode>curl\u003C\u002Fcode> request:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode># Raw HTTP proxy with German geo-targeting\ncurl -x &quot;http:\u002F\u002Fuser-country-DE:pass@gate.proxyhat.com:8080&quot; \\\n  &quot;https:\u002F\u002Fapi.bybit.com\u002Fv5\u002Fmarket\u002Forderbook?category=spot&amp;symbol=BTCUSDT&amp;limit=50&quot;\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cpre>\u003Ccode># SOCKS5 proxy variant\ncurl -x &quot;socks5:\u002F\u002Fuser-country-DE:pass@gate.proxyhat.com:1080&quot; \\\n  &quot;https:\u002F\u002Fapi.bybit.com\u002Fv5\u002Fmarket\u002Ftickers?category=linear&amp;symbol=ETHUSDT&quot;\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>If the response contains \u003Ccode>\"retCode\": 0\u003C\u002Fcode>, your proxy is working and the exit IP is not geo-restricted. If you get an HTML CloudFront error, your exit IP may be in a restricted region — change the country flag.\u003C\u002Fp>\n\n\u003Ch2>Example 2: Node.js (axios) — Rotating IPs Across a Symbol List\u003C\u002Fh2>\n\n\u003Cp>This example fetches order books for multiple symbols, rotating the proxy IP on each request. It shows both raw proxy URL construction and the ProxyHat SDK pattern. It includes retry logic with exponential backoff and per-endpoint rate budgeting.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import axios from &apos;axios&apos;;\nimport { ProxyHat } from &apos;@proxyhat\u002Fsdk&apos;;\n\nconst BYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;;\nconst symbols = [&apos;BTCUSDT&apos;, &apos;ETHUSDT&apos;, &apos;SOLUSDT&apos;, &apos;XRPUSDT&apos;, &apos;DOGEUSDT&apos;];\n\n\u002F\u002F --- Raw proxy URL builder ---\nfunction buildProxyUrl(country, session) {\n  let username = `user-country-${country}`;\n  if (session) username += `-sid-${session}`;\n  return `http:\u002F\u002F${username}:pass@gate.proxyhat.com:8080`;\n}\n\n\u002F\u002F --- ProxyHat SDK client ---\nconst phat = new ProxyHat({\n  gateway: &apos;gate.proxyhat.com&apos;,\n  port: 8080,\n  protocol: &apos;http&apos;,\n  username: &apos;user&apos;,\n  password: &apos;pass&apos;,\n  defaultCountry: &apos;DE&apos;,\n  rotate: true, \u002F\u002F new IP per request\n});\n\nasync function fetchOrderbookRaw(symbol, retries = 3) {\n  const url = `${BYBIT_BASE}\u002Fv5\u002Fmarket\u002Forderbook?category=spot&amp;symbol=${symbol}&amp;limit=50`;\n  for (let attempt = 0; attempt &lt; retries; attempt++) {\n    try {\n      const proxyUrl = buildProxyUrl(&apos;DE&apos;);\n      const res = await axios.get(url, {\n        proxy: {\n          host: &apos;gate.proxyhat.com&apos;,\n          port: 8080,\n          auth: { username: &apos;user-country-DE&apos;, password: &apos;pass&apos; },\n          protocol: &apos;http&apos;,\n        },\n        timeout: 10000,\n      });\n      if (res.data.retCode === 0) return res.data.result;\n      throw new Error(`retCode ${res.data.retCode}: ${res.data.retMsg}`);\n    } catch (err) {\n      if (err.response &amp;&amp; err.response.status === 403) {\n        console.warn(`[${symbol}] 403 on attempt ${attempt + 1}, backing off`);\n        await new Promise(r =&gt; setTimeout(r, 2 ** attempt * 1000));\n        continue;\n      }\n      throw err;\n    }\n  }\n  throw new Error(`Failed after ${retries} retries: ${symbol}`);\n}\n\nasync function fetchOrderbookSDK(symbol, retries = 3) {\n  const url = `${BYBIT_BASE}\u002Fv5\u002Fmarket\u002Forderbook?category=spot&amp;symbol=${symbol}&amp;limit=50`;\n  for (let attempt = 0; attempt &lt; retries; attempt++) {\n    try {\n      const proxyAgent = await phat.getProxyAgent(); \u002F\u002F auto-rotates IP\n      const res = await axios.get(url, {\n        httpsAgent: proxyAgent,\n        timeout: 10000,\n      });\n      if (res.data.retCode === 0) return res.data.result;\n      throw new Error(`retCode ${res.data.retCode}: ${res.data.retMsg}`);\n    } catch (err) {\n      console.warn(`[${symbol}] retry ${attempt + 1}: ${err.message}`);\n      await new Promise(r =&gt; setTimeout(r, 2 ** attempt * 1000));\n    }\n  }\n  throw new Error(`Failed after ${retries} retries: ${symbol}`);\n}\n\n\u002F\u002F Rate budget: max 5 requests\u002Fsec across all symbols\nconst MIN_INTERVAL_MS = 200;\n\nasync function scrapeAll() {\n  for (const symbol of symbols) {\n    const t0 = Date.now();\n    try {\n      \u002F\u002F Use either fetchOrderbookRaw or fetchOrderbookSDK here:\n      const book = await fetchOrderbookSDK(symbol);\n      const bestBid = book.bids?.[0]?.[0] ?? &apos;N\u002FA&apos;;\n      console.log(`${symbol} best bid: ${bestBid}`);\n    } catch (err) {\n      console.error(`${symbol} failed: ${err.message}`);\n    }\n    const elapsed = Date.now() - t0;\n    if (elapsed &lt; MIN_INTERVAL_MS) {\n      await new Promise(r =&gt; setTimeout(r, MIN_INTERVAL_MS - elapsed));\n    }\n  }\n}\n\nscrapeAll();\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Example 3: Python (requests) — Order Book with Raw Proxy and SDK\u003C\u002Fh2>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\nfrom proxyhat import ProxyHat  # ProxyHat Python SDK\n\nBYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;\nSYMBOLS = [&apos;BTCUSDT&apos;, &apos;ETHUSDT&apos;, &apos;SOLUSDT&apos;, &apos;XRPUSDT&apos;, &apos;DOGEUSDT&apos;]\n\n# --- Raw proxy dict for requests ---\ndef raw_proxy(country=&apos;DE&apos;, session=None):\n    username = f&apos;user-country-{country}&apos;\n    if session:\n        username += f&apos;-sid-{session}&apos;\n    return {\n        &apos;http&apos;: f&apos;http:\u002F\u002F{username}:pass@gate.proxyhat.com:8080&apos;,\n        &apos;https&apos;: f&apos;http:\u002F\u002F{username}:pass@gate.proxyhat.com:8080&apos;,\n    }\n\n# --- ProxyHat SDK ---\nphat = ProxyHat(\n    gateway=&apos;gate.proxyhat.com&apos;,\n    port=8080,\n    username=&apos;user&apos;,\n    password=&apos;pass&apos;,\n    country=&apos;DE&apos;,\n    rotate=True,\n)\n\ndef fetch_orderbook_raw(symbol, retries=3):\n    url = f&apos;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Forderbook&apos;\n    params = {&apos;category&apos;: &apos;spot&apos;, &apos;symbol&apos;: symbol, &apos;limit&apos;: 50}\n    for attempt in range(retries):\n        try:\n            res = requests.get(url, params=params, proxies=raw_proxy(&apos;DE&apos;),\n                               timeout=10)\n            if res.status_code == 403:\n                print(f&apos;[{symbol}] 403, retry {attempt+1}&apos;)\n                time.sleep(2 ** attempt)\n                continue\n            data = res.json()\n            if data.get(&apos;retCode&apos;) == 0:\n                return data[&apos;result&apos;]\n            raise Exception(f&quot;retCode {data.get(&apos;retCode&apos;)}: {data.get(&apos;retMsg&apos;)}&quot;)\n        except requests.RequestException as e:\n            print(f&apos;[{symbol}] {e}, retry {attempt+1}&apos;)\n            time.sleep(2 ** attempt)\n    raise Exception(f&apos;Failed after {retries} retries: {symbol}&apos;)\n\ndef fetch_orderbook_sdk(symbol, retries=3):\n    url = f&apos;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Forderbook&apos;\n    params = {&apos;category&apos;: &apos;spot&apos;, &apos;symbol&apos;: symbol, &apos;limit&apos;: 50}\n    for attempt in range(retries):\n        try:\n            proxy_url = phat.get_proxy_url()  # auto-rotates\n            res = requests.get(url, params=params,\n                               proxies={&apos;https&apos;: proxy_url, &apos;http&apos;: proxy_url},\n                               timeout=10)\n            data = res.json()\n            if data.get(&apos;retCode&apos;) == 0:\n                return data[&apos;result&apos;]\n            raise Exception(f&quot;retCode {data.get(&apos;retCode&apos;)}: {data.get(&apos;retMsg&apos;)}&quot;)\n        except Exception as e:\n            print(f&apos;[{symbol}] {e}, retry {attempt+1}&apos;)\n            time.sleep(2 ** attempt)\n    raise Exception(f&apos;Failed after {retries} retries: {symbol}&apos;)\n\n# Rate budget: 5 req\u002Fsec\nfor sym in SYMBOLS:\n    t0 = time.time()\n    try:\n        book = fetch_orderbook_sdk(sym)\n        print(f&quot;{sym} best bid: {book[&apos;bids&apos;][0][0]}&quot;)\n    except Exception as e:\n        print(f&quot;{sym} failed: {e}&quot;)\n    elapsed = time.time() - t0\n    if elapsed &lt; 0.2:\n        time.sleep(0.2 - elapsed)\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Example 4: Python — Sticky Sessions for Kline Pagination\u003C\u002Fh2>\n\n\u003Cp>Kline (candlestick) data is paginated by time range. If you page through historical data with \u003Ccode>start\u003C\u002Fcode> and \u003Ccode>end\u003C\u002Fcode> timestamps, rotating IPs on every request can cause inconsistent gaps. A sticky session keeps the same exit IP for a logical pagination sequence, ensuring consistent rate-limit accounting and avoiding mid-pagination IP bans.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\n\nBYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;\n\ndef fetch_klines_paginated(symbol, category=&apos;linear&apos;, interval=&apos;60&apos;,\n                           start_ts=None, end_ts=None, max_pages=50):\n    &quot;&quot;&quot;Paginate kline data using a sticky proxy session.\n    \n    Uses user-sid-kline01 to keep the same exit IP across\n    all pagination requests for this symbol.\n    &quot;&quot;&quot;\n    session_id = f&apos;kline-{symbol}&apos;\n    proxy_user = f&apos;user-country-DE-sid-{session_id}&apos;\n    proxies = {\n        &apos;http&apos;: f&apos;http:\u002F\u002F{proxy_user}:pass@gate.proxyhat.com:8080&apos;,\n        &apos;https&apos;: f&apos;http:\u002F\u002F{proxy_user}:pass@gate.proxyhat.com:8080&apos;,\n    }\n    \n    all_candles = []\n    cursor = start_ts\n    url = f&apos;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Fkline&apos;\n    \n    for page in range(max_pages):\n        params = {\n            &apos;category&apos;: category,\n            &apos;symbol&apos;: symbol,\n            &apos;interval&apos;: interval,\n            &apos;start&apos;: cursor,\n            &apos;end&apos;: end_ts,\n            &apos;limit&apos;: 1000,  # max candles per request\n        }\n        \n        for attempt in range(3):\n            try:\n                res = requests.get(url, params=params, proxies=proxies,\n                                   timeout=15)\n                if res.status_code == 403:\n                    print(f&apos;403 on page {page}, retry {attempt+1}&apos;)\n                    time.sleep(2 ** attempt)\n                    continue\n                data = res.json()\n                if data.get(&apos;retCode&apos;) != 0:\n                    raise Exception(f&quot;retCode {data[&apos;retCode&apos;]}: {data[&apos;retMsg&apos;]}&quot;)\n                candles = data[&apos;result&apos;][&apos;list&apos;]\n                if not candles:\n                    return all_candles\n                all_candles.extend(candles)\n                # Move cursor forward: last candle&apos;s timestamp + 1ms\n                cursor = int(candles[-1][0]) + 1\n                if cursor &gt;= end_ts:\n                    return all_candles\n                break\n            except requests.RequestException as e:\n                print(f&apos;Page {page} error: {e}, retry {attempt+1}&apos;)\n                time.sleep(2 ** attempt)\n        else:\n            raise Exception(f&apos;Failed page {page} after 3 retries&apos;)\n        \n        # Rate budget: 2 req\u002Fsec for kline endpoint\n        time.sleep(0.5)\n    \n    return all_candles\n\n# Usage: fetch 1-hour candles for BTCUSDT over a time range\ncandles = fetch_klines_paginated(\n    symbol=&apos;BTCUSDT&apos;,\n    category=&apos;linear&apos;,\n    interval=&apos;60&apos;,\n    start_ts=1700000000000,\n    end_ts=1700600000000,\n)\nprint(f&apos;Fetched {len(candles)} candles&apos;)\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Example 5: Node.js — Concurrent Ticker Fetch with Rate Budget and Circuit Breaker\u003C\u002Fh2>\n\n\u003Cp>For production scrapers, you need a circuit breaker that stops sending requests when the failure rate exceeds a threshold. This prevents cascading failures when Bybit's API or your proxy network is degraded.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import axios from &apos;axios&apos;;\n\nconst BYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;;\nconst SYMBOLS = [&apos;BTCUSDT&apos;, &apos;ETHUSDT&apos;, &apos;SOLUSDT&apos;, &apos;XRPUSDT&apos;, &apos;DOGEUSDT&apos;,\n  &apos;ADAUSDT&apos;, &apos;AVAXUSDT&apos;, &apos;LINKUSDT&apos;, &apos;MATICUSDT&apos;, &apos;DOTUSDT&apos;];\n\n\u002F\u002F Simple circuit breaker\nclass CircuitBreaker {\n  constructor(threshold = 5, resetMs = 60000) {\n    this.failures = 0;\n    this.threshold = threshold;\n    this.resetMs = resetMs;\n    this.openUntil = 0;\n  }\n  recordFailure() {\n    this.failures++;\n    if (this.failures &gt;= this.threshold) {\n      this.openUntil = Date.now() + this.resetMs;\n      console.warn(`Circuit open until ${new Date(this.openUntil).toISOString()}`);\n    }\n  }\n  recordSuccess() { this.failures = 0; }\n  isOpen() { return Date.now() &lt; this.openUntil; }\n}\n\nconst breaker = new CircuitBreaker(5, 60000);\n\nfunction proxyConfig(country = &apos;DE&apos;) {\n  return {\n    host: &apos;gate.proxyhat.com&apos;,\n    port: 8080,\n    auth: { username: `user-country-${country}`, password: &apos;pass&apos; },\n    protocol: &apos;http&apos;,\n  };\n}\n\nasync function fetchTicker(symbol) {\n  if (breaker.isOpen()) {\n    throw new Error(&apos;Circuit breaker open&apos;);\n  }\n  const url = `${BYBIT_BASE}\u002Fv5\u002Fmarket\u002Ftickers?category=spot&amp;symbol=${symbol}`;\n  for (let attempt = 0; attempt &lt; 3; attempt++) {\n    try {\n      const res = await axios.get(url, {\n        proxy: proxyConfig(&apos;DE&apos;),\n        timeout: 10000,\n      });\n      if (res.data.retCode === 0) {\n        breaker.recordSuccess();\n        return { symbol, ...res.data.result.list[0] };\n      }\n      throw new Error(`retCode ${res.data.retCode}`);\n    } catch (err) {\n      if (err.response?.status === 403) breaker.recordFailure();\n      if (attempt &lt; 2) await new Promise(r =&gt; setTimeout(r, 2 ** attempt * 500));\n      else throw err;\n    }\n  }\n}\n\n\u002F\u002F Process in batches of 3 with 250ms spacing (max ~12 req\u002Fsec)\nconst BATCH_SIZE = 3;\nconst BATCH_DELAY_MS = 250;\n\nasync function scrapeTickers() {\n  for (let i = 0; i &lt; SYMBOLS.length; i += BATCH_SIZE) {\n    const batch = SYMBOLS.slice(i, i + BATCH_SIZE);\n    const results = await Promise.allSettled(\n      batch.map(sym =&gt; fetchTicker(sym))\n    );\n    for (const r of results) {\n      if (r.status === &apos;fulfilled&apos;) {\n        console.log(`${r.value.symbol}: $${r.value.lastPrice}`);\n      } else {\n        console.error(`Error: ${r.reason.message}`);\n      }\n    }\n    await new Promise(r =&gt; setTimeout(r, BATCH_DELAY_MS));\n  }\n}\n\nscrapeTickers();\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Example 6: Python — Recent Trades with SOCKS5 Proxy\u003C\u002Fh2>\n\n\u003Cp>Some network environments require SOCKS5 instead of HTTP proxying. ProxyHat supports SOCKS5 on port \u003Ccode>1080\u003C\u002Fcode>. In Python, you need the \u003Ccode>requests[socks]\u003C\u002Fcode> package installed (\u003Ccode>pip install requests[socks]\u003C\u002Fcode>).\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>import requests\nimport time\n\nBYBIT_BASE = &apos;https:\u002F\u002Fapi.bybit.com&apos;\n\ndef fetch_recent_trades(symbol, category=&apos;spot&apos;, limit=60, retries=3):\n    url = f&apos;{BYBIT_BASE}\u002Fv5\u002Fmarket\u002Frecent-trade&apos;\n    params = {&apos;category&apos;: category, &apos;symbol&apos;: symbol, &apos;limit&apos;: limit}\n    \n    # SOCKS5 proxy with Japanese geo-targeting\n    socks_proxy = {\n        &apos;http&apos;: &apos;socks5:\u002F\u002Fuser-country-JP:pass@gate.proxyhat.com:1080&apos;,\n        &apos;https&apos;: &apos;socks5:\u002F\u002Fuser-country-JP:pass@gate.proxyhat.com:1080&apos;,\n    }\n    \n    for attempt in range(retries):\n        try:\n            res = requests.get(url, params=params, proxies=socks_proxy,\n                               timeout=15)\n            if res.status_code == 403:\n                print(f&apos;[{symbol}] 403, retry {attempt+1}&apos;)\n                time.sleep(2 ** attempt)\n                continue\n            data = res.json()\n            if data.get(&apos;retCode&apos;) == 0:\n                return data[&apos;result&apos;][&apos;list&apos;]\n            raise Exception(f&quot;retCode {data[&apos;retCode&apos;]}: {data[&apos;retMsg&apos;]}&quot;)\n        except Exception as e:\n            print(f&apos;[{symbol}] {e}, retry {attempt+1}&apos;)\n            time.sleep(2 ** attempt)\n    raise Exception(f&apos;Failed: {symbol}&apos;)\n\ntrades = fetch_recent_trades(&apos;BTCUSDT&apos;)\nprint(f&apos;Got {len(trades)} recent trades&apos;)\nfor t in trades[:5]:\n    print(f&quot;  price={t[&apos;price&apos;]} size={t[&apos;size&apos;]} side={t[&apos;isBuyerMaker&apos;]}&quot;)\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>REST Snapshots vs WebSocket Orderbook.50 Stream\u003C\u002Fh2>\n\n\u003Cp>Polling \u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode> via REST gives you point-in-time snapshots. For live order-book monitoring — especially for HFT-style strategies or real-time spread analysis — Bybit offers a public WebSocket feed. The \u003Ccode>orderbook.50.{symbol}\u003C\u002Fcode> topic pushes depth updates at \u003Cstrong>10ms\u003C\u002Fstrong> intervals for the top 50 levels, which is far more efficient than polling REST every 100ms.\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\u003Cth>Aspect\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>Latency\u003C\u002Ftd>\u003Ctd>~50–200ms per poll\u003C\u002Ftd>\u003Ctd>~10ms push interval\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Rate limit impact\u003C\u002Ftd>\u003Ctd>Consumes per-IP budget\u003C\u002Ftd>\u003Ctd>Single connection, no polling\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Data type\u003C\u002Ftd>\u003Ctd>Full snapshot\u003C\u002Ftd>\u003Ctd>Incremental updates (delta)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Best for\u003C\u002Ftd>\u003Ctd>Periodic snapshots, backfill\u003C\u002Ftd>\u003Ctd>Real-time depth monitoring\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Proxy requirement\u003C\u002Ftd>\u003Ctd>HTTP\u002FSOCKS5 proxy per request\u003C\u002Ftd>\u003Ctd>WS proxy (SOCKS5 recommended)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>For WebSocket connections, SOCKS5 on \u003Ccode>gate.proxyhat.com:1080\u003C\u002Fcode> is the recommended transport. Most WebSocket client libraries support SOCKS5 proxies natively or via an agent wrapper. Keep in mind that a single WebSocket connection uses one IP for its lifetime — use a sticky session if you need to reconnect without changing IPs.\u003C\u002Fp>\n\n\u003Ch2>Common Mistakes and Edge Cases\u003C\u002Fh2>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>Ignoring the retCode envelope:\u003C\u002Fstrong> A \u003Ccode>200\u003C\u002Fcode> HTTP status with \u003Ccode>retCode &gt; 0\u003C\u002Fcode> is still an error. Always check \u003Ccode>retCode === 0\u003C\u002Fcode> before processing \u003Ccode>result\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Polling orderbook in a tight loop: This is the fastest way to get a 10-minute IP ban. Always implement rate budgeting — even with rotating proxies, each individual IP has its own limit.\u003C\u002Fli>\n\u003Cli>Using datacenter proxies for geo-restricted access: Datacenter IP ranges are more likely to be flagged or blocked. Residential proxies provide better access from allowed regions.\u003C\u002Fli>\n\u003Cli>Not handling CloudFront 403s: A CloudFront \u003Ccode>403\u003C\u002Fcode> returns HTML, not JSON. Your JSON parser will throw — catch this and treat it as a geo-block, not a rate limit.\u003C\u002Fli>\n\u003Cli>Rotating IPs during kline pagination: This can cause duplicate or missing candles if Bybit's internal sharding differs by IP. Use sticky sessions (\u003Ccode>-sid-\u003C\u002Fcode>) for pagination sequences.\u003C\u002Fli>\n\u003Cli>Assuming category defaults: The \u003Ccode>category\u003C\u002Fcode> parameter is required on most V5 market endpoints. Omitting it returns an error, not a default category.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>ProxyHat Setup and Pricing\u003C\u002Fh2>\n\n\u003Cp>Getting started with ProxyHat for Bybit scraping is straightforward. Create an account at \u003Ca href=\"https:\u002F\u002Fdashboard.proxyhat.com\">dashboard.proxyhat.com\u003C\u002Fa>, choose a residential proxy plan, and use the credentials provided with the gateway details above. See our \u003Ca href=\"\u002Fpricing\">pricing page\u003C\u002Fa> for current plans and bandwidth options.\u003C\u002Fp>\n\n\u003Cp>For more scraping use cases and patterns, check out our \u003Ca href=\"\u002Fuse-cases\u002Fweb-scraping\">web scraping use case guide\u003C\u002Fa> and \u003Ca href=\"\u002Fuse-cases\u002Fserp-tracking\">SERP tracking guide\u003C\u002Fa>. Full API and SDK documentation is available at \u003Ca href=\"https:\u002F\u002Fdocs.proxyhat.com\u002F\" rel=\"noopener noreferrer\">docs.proxyhat.com\u003C\u002Fa>.\u003C\u002Fp>\n\n\u003Ch2>Key Takeaways\u003C\u002Fh2>\n\n\u003Cblockquote>\n\u003Cul>\n\u003Cli>Bybit's V5 Market API consolidates spot, linear, inverse, and options behind four public endpoints with a \u003Ccode>category\u003C\u002Fcode> parameter and a \u003Ccode>retCode\u002FretMsg\u003C\u002Fcode> envelope.\u003C\u002Fli>\n\u003Cli>Rate limits are per-IP and violations trigger a ~10-minute ban. The \u003Ccode>\u002Fv5\u002Fmarket\u002Forderbook\u003C\u002Fcode> endpoint is the most aggressively limited.\u003C\u002Fli>\n\u003Cli>Geo-restrictions return \u003Ccode>403\u003C\u002Fcode> or CloudFront errors. Residential proxies with country targeting (e.g., \u003Ccode>user-country-DE\u003C\u002Fcode>) bypass these blocks.\u003C\u002Fli>\n\u003Cli>Use per-request rotation for order-book polling and sticky sessions (\u003Ccode>-sid-\u003C\u002Fcode>) for kline pagination.\u003C\u002Fli>\n\u003Cli>Always implement retries with backoff, rate budgeting, and a circuit breaker in production scrapers.\u003C\u002Fli>\n\u003Cli>Prefer WebSocket \u003Ccode>orderbook.50\u003C\u002Fcode> for real-time depth; use REST for snapshots and historical backfill.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fblockquote>","Crypto Market Data with Rotating Proxies (Bybit V5) | ProxyHat","Learn how to scrape the Bybit V5 Market API with rotating residential proxies. Code examples in Node.js and Python for orderbook, tickers, kline, and recent-trade endpoints.","\u002Fblog\u002Fscrape-bybit-v5-market-api-rotating-proxies",null,"Unified Crypto Market Data APIs with Rotating Proxies | ProxyHat","Developer guide to scraping Bybit V5 Market API endpoints with rotating residential proxies, rate-limit budgeting, retries, and sticky sessions.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fscrape-bybit-v5-market-api-rotating-proxies-og.webp",[19,22,25,28,31],{"question":20,"answer":21},"What is the Bybit V5 Market API and why do I need proxies to scrape it?","The Bybit V5 Market API is a set of public REST endpoints (\u002Fv5\u002Fmarket\u002Forderbook, \u002Fv5\u002Fmarket\u002Ftickers, \u002Fv5\u002Fmarket\u002Fkline, \u002Fv5\u002Fmarket\u002Frecent-trade) that provide real-time and historical market data for spot, derivatives, and options. You need proxies because Bybit enforces strict per-IP rate limits that can trigger a ~10-minute ban, and geo-restrictions that block requests from certain countries. Rotating residential proxies distribute requests across many IPs and allow geo-targeting to allowed regions.",{"question":23,"answer":24},"Which proxy type works best for scraping Bybit's API?","Residential proxies are the best choice for Bybit scraping. They use real ISP-assigned IP addresses, which are less likely to be flagged by Bybit's anti-bot systems or CloudFront CDN. Datacenter proxies are cheaper but more easily detected and blocked. For order-book polling, use per-request rotation; for kline pagination, use sticky sessions to maintain the same IP across a pagination sequence. SOCKS5 on port 1080 is recommended for WebSocket connections.",{"question":26,"answer":27},"How do you avoid 403 blocks when scraping the Bybit V5 Market API?","To avoid 403 blocks, implement three strategies: (1) use residential proxies geo-targeted to an allowed country like Germany or Japan, (2) enforce per-endpoint rate budgeting — for example, no more than 5 requests per second per IP for order-book polling, and (3) implement exponential backoff retries with a circuit breaker that stops requests when the failure rate exceeds a threshold. Also distinguish between rate-limit 403s (JSON body with retCode) and geo-block 403s (CloudFront HTML response).",{"question":29,"answer":30},"What is the difference between Bybit's REST orderbook endpoint and the WebSocket stream?","The REST endpoint \u002Fv5\u002Fmarket\u002Forderbook returns full point-in-time snapshots and consumes your per-IP rate-limit budget on every poll. The WebSocket orderbook.50.{symbol} topic pushes incremental depth updates at approximately 10ms intervals over a single long-lived connection. REST is best for periodic snapshots and historical backfill; WebSocket is best for real-time depth monitoring and HFT-style strategies. For WebSocket, use SOCKS5 proxies on gate.proxyhat.com:1080.",{"question":32,"answer":33},"How does the category parameter work in Bybit's V5 Market API?","The category parameter specifies the product type: spot for spot trading, linear for USDT-margined perpetuals and futures, inverse for inverse contracts, and option for options. The same endpoint path serves all categories — you change the parameter value. For example, \u002Fv5\u002Fmarket\u002Forderbook?category=spot&symbol=BTCUSDT returns the spot order book, while category=linear returns the USDT perpetual order book. The category is required on most V5 market endpoints.",false,"2026-08-03T16:09:16+00:00","2026-10-04T01:29:51+00:00","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fscrape-bybit-v5-market-api-rotating-proxies-master.webp","How to Scrape the Bybit V5 Market API with Rotating Proxies","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fscrape-bybit-v5-market-api-rotating-proxies-card.webp",12,{"name":42,"slug":43,"bio":44,"avatar_url":14,"website_url":14,"social_links":14},"ProxyHat Team","proxyhat-team","The ProxyHat editorial team covers proxies, web scraping, and data extraction.",[46],{"id":47,"slug":48,"name":49,"description":14,"meta_title":50,"meta_description":51,"parent_id":14},4,"developer-guides","Developer Guides","Developer Guides | ProxyHat Blog","Code tutorials and SDK guides for integrating proxies into your applications.",[],{"de":54,"fr":55,"it":56,"ko":57,"zh":58,"pl":59,"pt":60,"ru":61,"es":62,"en":13,"ja":63,"x-default":13},"\u002Fde\u002Fblog\u002Fbybit-v5-market-api-mit-rotating-proxies-scrapen","\u002Ffr\u002Fblog\u002Fscraper-api-bybit-v5-proxys-rotatifs","\u002Fit\u002Fblog\u002Fscrape-bybit-v5-market-api-proxy","\u002Fko\u002Fblog\u002Fbybit-v5-market-api-proxy-scraping","\u002Fcn\u002Fblog\u002Fscrape-bybit-v5-market-api-proxy","\u002Fpl\u002Fblog\u002Fjak-scrapowac-bybit-v5-api-proxy","\u002Fpt\u002Fblog\u002Fcomo-raspar-bybit-v5-market-api-proxies","\u002Fru\u002Fblog\u002Fkak-skrejpit-bybit-v5-market-api","\u002Fes\u002Fblog\u002Fscrapear-api-mercado-bybit-v5-proxies","\u002Fja\u002Fblog\u002Fbybit-v5-market-api-proxy-scraping",{"de":54,"fr":55,"it":56,"ko":57,"zh":58,"pl":59,"pt":60,"ru":61,"es":62,"en":13,"ja":63,"tr":65},"\u002Ftr\u002Fblog\u002Fbybit-v5-market-api-dondurulen-proxy",87,[68,100,126,141],{"id":69,"slug":70,"title":71,"excerpt":72,"featured_image_url":73,"featured_image_alt":71,"card_image_url":74,"og_image_url":75,"published_at":76,"updated_at":77,"content_updated_at":78,"reading_time_minutes":79,"is_featured":34,"indexable":4,"views":79,"author":80,"categories":81,"tags":83},210,"proxy-setup-for-ai-agents","Proxy Setup for AI Agents: browser-use, Crawl4AI and an MCP Server","Agents on cloud VMs exit from datacenter IPs. Here is current, working proxy configuration for browser-use, Crawl4AI and an MCP server, with a sticky-vs-rotating decision table and the cost traps to avoid.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fproxy-setup-for-ai-agents-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fproxy-setup-for-ai-agents-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fproxy-setup-for-ai-agents-og.webp","2026-10-24T09:00:00+00:00","2026-10-24T09:00:20+00:00","2026-10-03T09:31:26+00:00",14,{"name":42,"slug":43,"bio":44,"avatar_url":14,"website_url":14,"social_links":14},[82],{"id":47,"slug":48,"name":49,"description":14,"meta_title":50,"meta_description":51,"parent_id":14},[84,88,92,96],{"id":85,"slug":86,"name":87},1,"web-scraping","Web Scraping",{"id":89,"slug":90,"name":91},2,"residential-proxies","Residential Proxies",{"id":93,"slug":94,"name":95},9,"python","Python",{"id":97,"slug":98,"name":99},22,"playwright","Playwright",{"id":101,"slug":102,"title":103,"excerpt":104,"featured_image_url":105,"featured_image_alt":103,"card_image_url":106,"og_image_url":107,"published_at":108,"updated_at":109,"content_updated_at":78,"reading_time_minutes":79,"is_featured":34,"indexable":4,"views":110,"author":111,"categories":112,"tags":114},206,"camoufox-residential-proxies-python-guide","Camoufox with Residential Proxies: A Code-First Python Guide","How to run Camoufox, the C++-patched Firefox, behind residential proxies: current Python API, why geoip needs a sticky session, a fingerprint coherence checklist, and when Patchright or nodriver fits better.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fcamoufox-residential-proxies-python-guide-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fcamoufox-residential-proxies-python-guide-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fcamoufox-residential-proxies-python-guide-og.webp","2026-10-13T09:00:00+00:00","2026-10-13T09:00:22+00:00",29,{"name":42,"slug":43,"bio":44,"avatar_url":14,"website_url":14,"social_links":14},[113],{"id":47,"slug":48,"name":49,"description":14,"meta_title":50,"meta_description":51,"parent_id":14},[115,116,117,121,122],{"id":85,"slug":86,"name":87},{"id":89,"slug":90,"name":91},{"id":118,"slug":119,"name":120},6,"anti-detection","Anti-Detection",{"id":93,"slug":94,"name":95},{"id":123,"slug":124,"name":125},21,"headless-browser","Headless Browser",{"id":127,"slug":128,"title":129,"excerpt":130,"featured_image_url":131,"featured_image_alt":129,"card_image_url":132,"og_image_url":133,"published_at":134,"updated_at":134,"content_updated_at":135,"reading_time_minutes":40,"is_featured":34,"indexable":4,"views":136,"author":137,"categories":138,"tags":140},176,"got-scraping-nodejs-residential-proxies","got-scraping in Node.js: A Developer's Guide with Residential Proxies","A practical guide to using Apify's got-scraping HTTP client in Node.js with residential proxies — header generation, proxy rotation, retry hooks, and Crawlee scaling patterns for production scrapers.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fgot-scraping-nodejs-residential-proxies-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fgot-scraping-nodejs-residential-proxies-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Fgot-scraping-nodejs-residential-proxies-og.webp","2026-08-04T16:05:06+00:00","2026-10-03T08:29:58+00:00",121,{"name":42,"slug":43,"bio":44,"avatar_url":14,"website_url":14,"social_links":14},[139],{"id":47,"slug":48,"name":49,"description":14,"meta_title":50,"meta_description":51,"parent_id":14},[],{"id":142,"slug":143,"title":144,"excerpt":145,"featured_image_url":146,"featured_image_alt":144,"card_image_url":147,"og_image_url":148,"published_at":149,"updated_at":149,"content_updated_at":150,"reading_time_minutes":40,"is_featured":34,"indexable":34,"views":151,"author":152,"categories":153,"tags":155},175,"rotating-proxies-in-colly-golang","Rotating Proxies in Colly: A Framework-Idiomatic Guide for Go Developers","A developer-first guide to implementing rotating proxies in Colly using residential IPs, custom proxy switchers, geo-targeting, and production patterns for high-throughput Go web scraping.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Frotating-proxies-in-colly-golang-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Frotating-proxies-in-colly-golang-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fen\u002Frotating-proxies-in-colly-golang-og.webp","2026-08-04T10:05:34+00:00","2026-10-03T08:29:56+00:00",71,{"name":42,"slug":43,"bio":44,"avatar_url":14,"website_url":14,"social_links":14},[154],{"id":47,"slug":48,"name":49,"description":14,"meta_title":50,"meta_description":51,"parent_id":14},[],[]]