{“@context”: “https://schema.org”, “@type”: “Article”, “headline”: “How to Rotate Proxies in Python: Requests, Selenium and Scrapy (2026)”, “description”: “How to rotate proxies in Python with requests, Scrapy and Selenium: short code that runs, a retry loop, gateway rotation and the mistake that leaks your IP.”, “image”: “https://proxyadvice.net/wp-content/uploads/2026/10/rotate-proxies-python-cover.webp”, “datePublished”: “2026-11-05”, “dateModified”: “2026-11-05”, “inLanguage”: “en”, “wordCount”: 2049, “author”: {“@type”: “Organization”, “name”: “ProxyAdvice Editorial Team”, “url”: “https://proxyadvice.net/about-us/”}, “publisher”: {“@type”: “Organization”, “name”: “ProxyAdvice”, “url”: “https://proxyadvice.net”, “logo”: {“@type”: “ImageObject”, “url”: “https://proxyadvice.net/wp-content/uploads/2026/04/Proxyadvice.net-logo.png”}}, “mainEntityOfPage”: {“@type”: “WebPage”, “@id”: “https://proxyadvice.net/rotate-proxies-python/”}} {“@context”: “https://schema.org”, “@type”: “BreadcrumbList”, “itemListElement”: [{“@type”: “ListItem”, “position”: 1, “name”: “Home”, “item”: “https://proxyadvice.net”}, {“@type”: “ListItem”, “position”: 2, “name”: “Advanced Guides”, “item”: “https://proxyadvice.net/category/guides/advanced-guides/”}, {“@type”: “ListItem”, “position”: 3, “name”: “Rotate Proxies in Python”}]} {“@context”: “https://schema.org”, “@type”: “FAQPage”, “mainEntity”: [{“@type”: “Question”, “name”: “Does requests rotate proxies by itself?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “No. requests sends each call through whatever proxy you pass in proxies=. To rotate, keep a list and pass a different URL each time, for example with itertools.cycle, or pass one rotating gateway URL and let the provider change the IP.”}}, {“@type”: “Question”, “name”: “Should I use itertools.cycle or random.choice?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “itertools.cycle spreads load evenly: with 3 proxies, each gets every third request. random.choice is less predictable but can pick the same proxy twice in a row. For a small list, cycle is the safer default.”}}, {“@type”: “Question”, “name”: “How do I rotate proxies in Selenium with a username and password?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Chrome’s –proxy-server switch doesn’t accept a login. Allowlist your own IP in the provider’s dashboard, then start a new driver with the next proxy for each rotation. Each driver keeps one proxy until you quit it.”}}, {“@type”: “Question”, “name”: “How do I rotate proxies in Scrapy?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Set request.meta[‘proxy’] on each request. Scrapy’s HttpProxyMiddleware reads it, accepts a login in the URL, and gives it priority over the http_proxy and https_proxy environment variables. Choose the proxy with random.choice or a cycle in start() or in a downloader middleware.”}}, {“@type”: “Question”, “name”: “What is a sticky session?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “A sticky session keeps the same IP for a set time on a rotating gateway. On Oxylabs, adding sessid to the username starts one; it lasts 10 minutes by default or ends after 60 seconds idle, and sesstime extends it to as much as 1,440 minutes.”}}, {“@type”: “Question”, “name”: “Why does my scraper still show my own IP?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “The usual cause is a proxies dict with only an http key while you fetch https:// pages, so requests sends those directly. Put the proxy under both keys. If you set session.proxies, an HTTPS_PROXY environment variable can also override it.”}}, {“@type”: “Question”, “name”: “Can I rotate proxies with httpx?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Yes. httpx takes one proxy per client through the proxy= argument, for example httpx.Client(proxy=URL). To rotate a list, create a client per proxy, or send everything to a rotating gateway URL so one client is enough.”}}]} .ap-article{–acc:#00b85e;–acc-2:#00e87a;–acc-ink:#00894a;–link:#1a6fd4;–navy:#0a1628;–navy-2:#0d1f33;–navy-3:#132a45;–pos:#00b85e;–pos-ink:#00894a;–mid:#3d8ef0;–top-a:#00e87a;–top-b:#00b85e;–bg:#f8fafc;–bg-2:#ffffff;–bg-3:#f1f5f9;–card:#ffffff;–border:#e2e8f0;–border-md:#cbd5e1;–text-head:#0f172a;–text-body:#334155;–text-muted:#64748b;–text-light:#94a3b8;–acc-dim:rgba(0,232,122,.10);–acc-dim-2:rgba(0,232,122,.05);–pos-dim:rgba(0,232,122,.12);–red:#e53e3e;–yellow:#d97706;–f-head:’Outfit’,sans-serif;–f-body:’Rubik’,sans-serif;–f-code:’JetBrains Mono’,monospace;–sidebar-w:290px;–radius:10px;–radius-lg:14px;–gap:30px;–sh-sm:0 1px 4px rgba(10,22,40,.07),0 1px 2px rgba(10,22,40,.04);–sh-md:0 4px 14px rgba(10,22,40,.10),0 2px 4px rgba(10,22,40,.05);–sh-lg:0 10px 32px rgba(10,22,40,.14);–grad:linear-gradient(135deg,#00e87a,#00b85e);–on-grad:#0a1628} .ap-article *,.ap-article *::before,.ap-article *::after{box-sizing:border-box;margin:0;padding:0} .ap-article{font-family:var(–f-body);background:var(–bg);color:var(–text-body);line-height:1.75;font-size:16px;overflow-x:clip;-webkit-font-smoothing:antialiased} .ap-article a{color:var(–link);text-decoration:none;transition:color .2s} .ap-article a:hover{color:var(–navy);text-decoration:underline} .ap-article img{max-width:100%;height:auto;display:block;border-radius:var(–radius)} .ap-article strong{color:var(–text-head);font-weight:700} .ap-article p{font-size:15.5px;line-height:1.8} .ap-article .outer{max-width:1260px;margin:0 auto;padding:0 20px} .ap-article .two-col{display:grid;grid-template-columns:1fr var(–sidebar-w);gap:var(–gap);align-items:start;padding:24px 0 64px} .ap-article .main-col{min-width:0;grid-column:1} .ap-article .sidebar-col{grid-column:2;grid-row:1;position:sticky;top:16px;display:flex;flex-direction:column;gap:14px;align-self:start} .ap-article .ap-progress-top{position:fixed;top:0;left:0;height:3px;width:0;z-index:9999;background:var(–grad);transition:width .12s linear} @keyframes apFadeUp{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}} @keyframes apSlideLeft{from{opacity:0;transform:translateX(22px)}to{opacity:1;transform:translateX(0)}} @keyframes apPulse{0%,100%{box-shadow:0 0 0 0 rgba(0,184,94,.40)}50%{box-shadow:0 0 0 7px rgba(0,184,94,0)}} .ap-article.ap-js .reveal{opacity:0;transform:translateY(16px);transition:opacity .5s ease,transform .5s ease} .ap-article.ap-js .reveal.vis{opacity:1;transform:translateY(0)} .ap-article .s1{transition-delay:.04s} .ap-article .s2{transition-delay:.09s} .ap-article .s3{transition-delay:.14s} .ap-article .s4{transition-delay:.19s} .ap-article .s5{transition-delay:.24s} .ap-article .s6{transition-delay:.29s} .ap-article .s7{transition-delay:.34s} .ap-article .sb-anim{opacity:0;animation:apSlideLeft .5s ease forwards} .ap-article .sb-anim:nth-child(1){animation-delay:.08s} .ap-article .sb-anim:nth-child(2){animation-delay:.16s} .ap-article .sb-anim:nth-child(3){animation-delay:.24s} .ap-article .sb-anim:nth-child(4){animation-delay:.32s} @media(prefers-reduced-motion:reduce){.ap-article .reveal,.ap-article .sb-anim{opacity:1!important;transform:none!important;animation:none!important}} .ap-article .breadcrumb{padding:14px 0 8px;font-size:12.5px;color:var(–text-muted);display:flex;gap:6px;align-items:center;flex-wrap:wrap} .ap-article .breadcrumb a{color:var(–text-muted)} .ap-article .breadcrumb a:hover{color:var(–link)} .ap-article .breadcrumb .sep{color:var(–text-light);font-size:10px} .ap-article .breadcrumb .cur{color:var(–text-body);font-weight:600} .ap-article .article-header{padding:6px 0 26px} .ap-article .art-flags{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px} .ap-article .aflag{font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;letter-spacing:.4px;text-transform:uppercase;font-family:var(–f-code)} .ap-article .af-b{background:var(–acc-dim);color:var(–acc-ink);border:1px solid rgba(0,184,94,.25)} .ap-article .af-g{background:var(–pos-dim);color:var(–pos-ink);border:1px solid rgba(0,232,122,.25)} .ap-article .af-dot{width:3px;height:3px;background:var(–text-light);border-radius:50%} .ap-article .art-h1{font-family:var(–f-head);font-size:clamp(24px,4vw,40px);font-weight:800;color:var(–text-head);line-height:1.18;margin-bottom:16px;letter-spacing:-.5px} .ap-article .art-h1 mark{background:none;color:var(–acc-ink);border-bottom:3px solid var(–acc);padding-bottom:1px} .ap-article .art-intro{font-size:16.5px;color:var(–text-body);line-height:1.8;border-left:4px solid var(–acc);padding:12px 0 12px 18px;margin-bottom:20px;background:var(–acc-dim);border-radius:0 var(–radius) var(–radius) 0} .ap-article .author-strip{display:flex;gap:12px;align-items:center;background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);padding:12px 16px;box-shadow:var(–sh-sm)} .ap-article .author-ava{width:42px;height:42px;border-radius:50%;flex-shrink:0;background:var(–grad);display:flex;align-items:center;justify-content:center;font-family:var(–f-head);font-weight:800;font-size:15px;color:#fff} .ap-article .aname{font-weight:700;font-size:13.5px;color:var(–text-head)} .ap-article .arole{font-size:12px;color:var(–text-muted);margin-top:2px} .ap-article .achips{margin-left:auto;display:flex;gap:6px;flex-wrap:wrap} .ap-article .achip{font-size:10px;font-family:var(–f-code);padding:3px 8px;border-radius:4px;font-weight:600} .ap-article .ac-b{background:var(–acc-dim);color:var(–acc-ink);border:1px solid rgba(0,184,94,.2)} .ap-article .ac-g{background:var(–pos-dim);color:var(–pos-ink);border:1px solid rgba(0,232,122,.2)} .ap-article .sec-h2{font-family:var(–f-head);font-size:clamp(18px,2.6vw,24px);font-weight:800;color:var(–text-head);margin:40px 0 14px;padding-top:14px;border-top:2px solid var(–border);display:flex;align-items:center;gap:10px;letter-spacing:-.3px} .ap-article .sec-h2::before{content:”;flex-shrink:0;width:4px;height:22px;background:var(–grad);border-radius:2px} .ap-article .sec-h3{font-family:var(–f-head);font-size:clamp(15px,2vw,18px);font-weight:700;color:var(–text-head);margin:22px 0 9px;display:flex;align-items:center;gap:8px} .ap-article .sec-h3::before{content:”;flex-shrink:0;width:3px;height:14px;background:var(–acc);border-radius:2px} .ap-article .body-p{color:var(–text-body);line-height:1.8;margin-bottom:14px;font-size:15.5px} .ap-article .punch{font-family:var(–f-head);font-weight:700;color:var(–text-head);font-size:16px;margin:16px 0;line-height:1.45} .ap-article ul.slist{list-style:none;display:flex;flex-direction:column;gap:9px;margin:12px 0} .ap-article ul.slist li{display:flex;gap:10px;align-items:flex-start;color:var(–text-body);font-size:15px;line-height:1.65} .ap-article ul.slist li::before{content:’▸’;color:var(–acc-ink);font-size:11px;margin-top:4px;flex-shrink:0} .ap-article .extended{max-height:0;overflow:hidden;transition:max-height .5s ease} .ap-article .read-more-btn{display:inline-flex;align-items:center;gap:6px;margin:6px 0 4px;background:var(–acc-dim);border:1px solid rgba(0,184,94,.22);color:var(–acc-ink);font-family:var(–f-head);font-weight:700;font-size:13px;padding:8px 15px;border-radius:8px;cursor:pointer;transition:background .2s} .ap-article .read-more-btn:hover{background:rgba(0,184,94,.16)} .ap-article .read-more-btn .rm-ic{transition:transform .3s;display:inline-block} .ap-article .read-more-btn[aria-expanded=”true”] .rm-ic{transform:rotate(180deg)} .ap-article .sec-lbl{font-family:var(–f-code);font-size:10.5px;letter-spacing:2px;text-transform:uppercase;color:var(–acc-ink);margin-bottom:10px} .ap-article .quicklist{display:flex;flex-direction:column;gap:8px;margin:14px 0} .ap-article .ql-card{background:var(–card);border:1px solid var(–border);border-radius:var(–radius-lg);padding:15px 18px;display:grid;grid-template-columns:58px 1fr 1fr auto;gap:14px;align-items:center;transition:border-color .2s,box-shadow .2s,transform .2s;position:relative;overflow:hidden;box-shadow:var(–sh-sm)} .ap-article .ql-card::before{content:”;position:absolute;left:0;top:0;bottom:0;width:3px;background:var(–border);transition:background .2s} .ap-article .ql-card:hover{border-color:var(–border-md);box-shadow:var(–sh-md);transform:translateX(3px)} .ap-article .ql-card:hover::before{background:var(–acc)} .ap-article .ql-card.top-pick{border-color:rgba(0,184,94,.4)} .ap-article .ql-card.top-pick::before{background:var(–acc)} .ap-article .ql-rank{width:54px;height:54px;flex-shrink:0;background:var(–bg-3);border:2px solid var(–border);border-radius:var(–radius);display:flex;flex-direction:column;align-items:center;justify-content:center} .ap-article .ql-num{font-family:var(–f-head);font-weight:900;font-size:19px;color:var(–acc-ink);line-height:1} .ap-article .ql-numlbl{font-family:var(–f-code);font-size:9px;color:var(–text-muted);letter-spacing:.5px} .ap-article .ql-card.top-pick .ql-rank{border-color:var(–acc);background:var(–acc-dim)} .ap-article .ql-card.top-pick .ql-num{animation:apPulse 3s infinite} .ap-article .ql-name{font-family:var(–f-head);font-weight:700;font-size:14.5px;color:var(–text-head);margin-bottom:4px;display:flex;align-items:center;gap:7px;flex-wrap:wrap} .ap-article .ql-tag{font-size:9.5px;font-weight:700;padding:2px 7px;border-radius:14px;text-transform:uppercase;letter-spacing:.4px;font-family:var(–f-code);background:var(–acc-dim);color:var(–acc-ink);border:1px solid rgba(0,184,94,.25)} .ap-article .ql-sub{font-size:12.5px;color:var(–text-muted);line-height:1.5} .ap-article .ql-metrics{display:flex;flex-direction:column;gap:7px} .ap-article .ql-mrow{display:flex;align-items:center;gap:8px;font-size:11.5px} .ap-article .ql-mlbl{color:var(–text-muted);width:50px;flex-shrink:0;font-weight:500} .ap-article .ql-mbar{flex:1;height:5px;background:var(–bg-3);border-radius:3px;overflow:hidden} .ap-article .ql-mfill{height:100%;border-radius:3px;background:var(–grad);width:var(–tw,0);transition:width 1.1s ease .2s} .ap-article.ap-js .ql-mfill{width:0} .ap-article .ql-mval{font-family:var(–f-code);font-size:10px;color:var(–text-head);font-weight:600;width:30px;text-align:right;flex-shrink:0} .ap-article .btn-visit{display:inline-flex;align-items:center;gap:5px;background:var(–grad);color:#fff;font-family:var(–f-head);font-weight:800;font-size:12.5px;padding:9px 15px;border-radius:8px;border:none;cursor:pointer;transition:filter .2s,transform .15s;white-space:nowrap;text-decoration:none} .ap-article .btn-visit:hover{filter:brightness(1.08);transform:scale(.97);text-decoration:none;color:#fff} .ap-article .tldr{background:var(–card);border:1px solid var(–border);border-left:4px solid var(–acc);border-radius:var(–radius-lg);padding:20px 22px;margin:24px 0;box-shadow:var(–sh-sm)} .ap-article .tldr-lbl{font-family:var(–f-code);font-size:10px;letter-spacing:2px;text-transform:uppercase;color:var(–acc-ink);margin-bottom:12px;display:flex;align-items:center;gap:7px} .ap-article .tldr ul{list-style:none;display:flex;flex-direction:column;gap:9px} .ap-article .tldr ul li{display:flex;gap:10px;align-items:flex-start;font-size:14.5px;color:var(–text-body);line-height:1.55} .ap-article .tldr ul li::before{content:’▸’;color:var(–acc-ink);font-size:11px;margin-top:3px;flex-shrink:0} .ap-article .direct-ans{background:linear-gradient(135deg,var(–acc-dim),var(–pos-dim));border:1px solid rgba(0,184,94,.2);border-radius:var(–radius-lg);padding:18px 20px;margin:0 0 24px} .ap-article .da-lbl{font-family:var(–f-code);font-size:10px;letter-spacing:1.8px;text-transform:uppercase;color:var(–acc-ink);display:flex;align-items:center;gap:7px;margin-bottom:8px} .ap-article .da-text{font-size:15px;line-height:1.7;color:var(–text-head);font-weight:400} .ap-article .provider-block{background:var(–card);border:1px solid var(–border);border-radius:var(–radius-lg);padding:26px;margin:24px 0;box-shadow:var(–sh-md);position:relative;overflow:hidden} .ap-article .provider-block::after{content:”;position:absolute;top:0;left:0;right:0;height:3px;background:var(–grad)} .ap-article .pb-title{font-family:var(–f-head);font-size:19px;font-weight:800;color:var(–text-head);margin-bottom:4px;line-height:1.25} .ap-article .pb-sub{font-size:13px;color:var(–text-muted);font-style:italic;margin-bottom:14px} .ap-article .pb-hd{display:flex;gap:18px;align-items:center;flex-wrap:wrap;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(–border)} .ap-article .pb-logo{min-width:130px;height:50px;background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);display:flex;align-items:center;justify-content:center;font-family:var(–f-code);font-size:13px;color:var(–acc-ink);flex-shrink:0;padding:6px} .ap-article .pb-scores{display:flex;gap:16px;flex-wrap:wrap;align-items:center} .ap-article .pb-sc-val{font-family:var(–f-head);font-size:22px;font-weight:900;color:var(–acc-ink);line-height:1} .ap-article .pb-sc-lbl{font-family:var(–f-code);font-size:9.5px;color:var(–text-muted);margin-top:3px;text-transform:uppercase;letter-spacing:.5px;text-align:center} .ap-article .pb-verdict{font-size:15px;line-height:1.7;color:var(–text-body);margin-bottom:6px} .ap-article .pb-ss{width:100%;height:180px;background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);display:flex;align-items:center;justify-content:center;color:var(–text-muted);font-size:13px;margin:16px 0;overflow:hidden;position:relative} .ap-article .pb-ss img{width:100%;height:100%;object-fit:cover;border-radius:0} .ap-article .pb-ss-cap{position:absolute;bottom:0;left:0;right:0;background:linear-gradient(transparent,rgba(10,22,40,.85));color:#dbe7f0;font-size:11px;padding:8px 12px} .ap-article .pb-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px} .ap-article .pb-box{background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);padding:14px} .ap-article .pb-box-t{font-family:var(–f-code);font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(–acc-ink);margin-bottom:9px;font-weight:600} .ap-article .pb-box ul{list-style:none;display:flex;flex-direction:column;gap:6px} .ap-article .pb-box ul li{font-size:13px;color:var(–text-body);padding-left:14px;position:relative;line-height:1.5} .ap-article .pb-box ul li::before{content:’✓’;position:absolute;left:0;color:var(–pos-ink);font-size:11px;top:1px;font-weight:700} .ap-article .pb-pc{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px} .ap-article .pb-pcb{background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);padding:14px} .ap-article .pb-pct{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1px;margin-bottom:9px;display:flex;gap:5px;align-items:center} .ap-article .pb-pcb.pros .pb-pct{color:var(–pos-ink)} .ap-article .pb-pcb.cons .pb-pct{color:var(–red)} .ap-article .pb-pcb ul{list-style:none;display:flex;flex-direction:column;gap:6px} .ap-article .pb-pcb ul li{font-size:13px;color:var(–text-body);padding-left:15px;position:relative;line-height:1.5} .ap-article .pb-pcb.pros ul li::before{content:’+’;position:absolute;left:0;color:var(–pos-ink);font-weight:700} .ap-article .pb-pcb.cons ul li::before{content:’−’;position:absolute;left:0;color:var(–red);font-weight:700} .ap-article .pb-coupon{background:linear-gradient(90deg,var(–acc-dim),var(–pos-dim));border:1px solid rgba(0,184,94,.2);border-radius:var(–radius);padding:11px 15px;display:flex;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap} .ap-article .pb-coupon-t{font-size:13.5px;color:var(–text-body);flex:1} .ap-article .pb-coupon-code{font-family:var(–f-code);font-size:12px;font-weight:700;color:#fff;background:var(–acc-ink);padding:5px 12px;border-radius:6px;cursor:pointer;white-space:nowrap;transition:filter .2s} .ap-article .pb-coupon-code:hover{filter:brightness(1.1)} .ap-article .pb-tw{overflow-x:auto;margin-bottom:16px} .ap-article .pb-tbl{width:100%;border-collapse:collapse;font-size:13.5px;min-width:400px} .ap-article .pb-tbl th{background:var(–bg-3);color:var(–acc-ink);border-bottom:2px solid var(–border);font-family:var(–f-code);font-size:9.5px;letter-spacing:1px;text-transform:uppercase;padding:10px 13px;text-align:left} .ap-article .pb-tbl td{padding:9px 13px;border-bottom:1px solid var(–border);color:var(–text-body);font-size:13.5px} .ap-article .pb-tbl tr:hover td{background:var(–bg-3)} .ap-article .pb-tbl td strong{color:var(–text-head)} .ap-article .pb-tbl .best-row td{color:var(–acc-ink);font-weight:600} .ap-article .pb-foot{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding-top:16px;border-top:1px solid var(–border)} .ap-article .btn-expand{background:transparent;border:1px solid var(–border);color:var(–text-muted);padding:8px 14px;border-radius:var(–radius);font-size:13px;cursor:pointer;transition:all .2s;font-family:var(–f-body);display:flex;gap:6px;align-items:center} .ap-article .btn-expand:hover{border-color:var(–acc);color:var(–acc-ink)} .ap-article .btn-expand .exp-ic{transition:transform .3s;display:inline-block} .ap-article .btn-expand[aria-expanded=”true”] .exp-ic{transform:rotate(180deg)} .ap-article .pb-extended{max-height:0;overflow:hidden;transition:max-height .5s ease} .ap-article .pb-extended-in{padding-top:18px} .ap-article .pb-test-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin:6px 0 18px} .ap-article .pb-test-box{background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);padding:14px;text-align:center} .ap-article .pb-test-label{font-family:var(–f-code);font-size:10px;letter-spacing:1px;text-transform:uppercase;color:var(–text-muted);margin-bottom:8px} .ap-article .pb-test-val{font-family:var(–f-head);font-size:20px;font-weight:800;color:var(–navy);margin-bottom:10px} .ap-article .pb-test-bar{height:6px;background:rgba(10,22,40,.08);border-radius:10px;overflow:hidden} .ap-article .pb-test-fill{height:100%;background:var(–grad);border-radius:10px} .ap-article .pb-audit{background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);padding:16px;margin-bottom:14px} .ap-article .pb-audit-t{font-family:var(–f-code);font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(–acc-ink);margin-bottom:8px;font-weight:600} .ap-article .pb-audit p{font-size:14px;line-height:1.7;color:var(–text-body)} .ap-article .pb-bottomline{font-size:13.5px;color:var(–text-muted);font-style:italic;border-top:1px solid var(–border);padding-top:14px} .ap-article .ct-wrap{overflow-x:auto;margin:22px 0;border-radius:var(–radius-lg);border:1px solid var(–border);box-shadow:var(–sh-sm)} .ap-article .ct{width:100%;border-collapse:collapse;font-size:13px;min-width:700px} .ap-article .ct thead th{background:var(–bg-3);color:var(–text-muted);padding:12px 14px;text-align:left;font-family:var(–f-code);font-size:9.5px;letter-spacing:1px;text-transform:uppercase;border-bottom:2px solid var(–border);white-space:nowrap} .ap-article .ct-b{color:var(–acc-ink)!important} .ap-article .ct tbody td{padding:10px 14px;border-bottom:1px solid var(–border);vertical-align:middle} .ap-article .ct tbody tr:last-child td{border-bottom:none} .ap-article .ct tbody tr:hover td{background:var(–bg-3)} .ap-article .ct tbody td:first-child{font-size:13px;font-weight:600;color:var(–text-muted)} .ap-article .cy{color:var(–pos-ink);font-weight:700} .ap-article .cn{color:var(–red)} .ap-article .cbest{color:var(–acc-ink);font-weight:700} .ap-article .cok{color:var(–text-head)} .ap-article .cna{color:var(–text-muted);font-style:italic} .ap-article .wchip{display:inline-block;font-size:9px;font-weight:700;padding:2px 6px;background:var(–acc-dim);color:var(–acc-ink);border:1px solid rgba(0,184,94,.25);border-radius:12px;text-transform:uppercase;letter-spacing:.4px;margin-left:5px;vertical-align:middle} .ap-article .types-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(188px,1fr));gap:12px;margin:18px 0} .ap-article .type-card{background:var(–card);border:1px solid var(–border);border-radius:var(–radius-lg);padding:18px 15px;transition:border-color .2s,box-shadow .2s,transform .2s;box-shadow:var(–sh-sm)} .ap-article .type-card:hover{border-color:var(–border-md);box-shadow:var(–sh-md);transform:translateY(-2px)} .ap-article .type-icon{font-size:26px;margin-bottom:9px} .ap-article .type-name{font-family:var(–f-head);font-weight:700;font-size:14px;color:var(–text-head);margin-bottom:5px} .ap-article .type-desc{font-size:12.5px;color:var(–text-muted);line-height:1.6} .ap-article .faq-wrap{display:flex;flex-direction:column;gap:0;margin:18px 0;border-radius:var(–radius-lg);overflow:hidden;border:1px solid var(–border);box-shadow:var(–sh-sm)} .ap-article .faq-item{border-bottom:1px solid var(–border)} .ap-article .faq-item:last-child{border-bottom:none} .ap-article .faq-q{width:100%;background:#fff;border:none;padding:15px 46px 15px 18px;text-align:left;cursor:pointer;font-family:var(–f-head);font-weight:700;font-size:14.5px;color:var(–text-head);transition:background .15s;position:relative;line-height:1.4} .ap-article .faq-q:hover{background:var(–bg-3)} .ap-article .faq-q::after{content:’▾’;position:absolute;right:16px;top:50%;transform:translateY(-50%);font-size:15px;color:var(–text-muted);transition:transform .3s,color .2s} .ap-article .faq-item.open .faq-q{background:var(–bg-3);color:var(–acc-ink)} .ap-article .faq-item.open .faq-q::after{transform:translateY(-50%) rotate(180deg);color:var(–acc-ink)} .ap-article .faq-a{max-height:0;overflow:hidden;background:var(–bg-3);transition:max-height .35s ease} .ap-article .faq-a-in{padding:14px 18px;font-size:14.5px;color:var(–text-body);line-height:1.75;border-top:1px solid var(–border)} .ap-article .method{background:var(–card);border:1px solid rgba(0,184,94,.22);border-radius:var(–radius-lg);padding:22px;margin:26px 0;box-shadow:var(–sh-sm)} .ap-article .method-hd{display:flex;gap:12px;align-items:center;margin-bottom:14px} .ap-article .method-t{font-family:var(–f-head);font-weight:800;font-size:15px;color:var(–text-head)} .ap-article .method-sub{font-size:12px;color:var(–text-muted);margin-top:2px} .ap-article .method-cells{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px;margin-top:12px} .ap-article .method-cell{background:var(–bg-3);border-radius:8px;padding:11px 13px;border:1px solid var(–border)} .ap-article .method-cl{font-family:var(–f-code);font-size:9.5px;color:var(–acc-ink);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px} .ap-article .method-cv{font-size:13px;color:var(–text-head);font-weight:700} .ap-article .related{background:var(–card);border:1px solid var(–border);border-radius:var(–radius-lg);padding:22px;margin:30px 0;box-shadow:var(–sh-sm)} .ap-article .related-t{font-family:var(–f-head);font-size:16px;font-weight:800;color:var(–text-head);margin-bottom:14px;display:flex;align-items:center;gap:8px} .ap-article .related-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px} .ap-article .related-link{display:flex;align-items:center;gap:8px;padding:10px 13px;background:var(–bg-3);border:1px solid var(–border);border-radius:var(–radius);font-size:13px;color:var(–link);transition:background .2s,border-color .2s} .ap-article .related-link:hover{background:var(–acc-dim);border-color:var(–acc);text-decoration:none} .ap-article .related-link::before{content:’→’;color:var(–acc-ink);font-size:12px;flex-shrink:0} .ap-article .footer-cta{background:var(–navy);border-radius:var(–radius-lg);padding:32px;text-align:center;margin:36px 0;box-shadow:var(–sh-lg);border:1px solid rgba(0,184,94,.2);position:relative;overflow:hidden} .ap-article .footer-cta::before{content:”;position:absolute;top:-60px;right:-60px;width:200px;height:200px;border-radius:50%;background:radial-gradient(circle,rgba(0,184,94,.18) 0%,transparent 70%);pointer-events:none} .ap-article .footer-cta h3{font-family:var(–f-head);font-size:22px;font-weight:900;margin-bottom:10px;color:#fff;letter-spacing:-.3px} .ap-article .footer-cta p{color:rgba(255,255,255,.66);font-size:14.5px;margin-bottom:20px} .ap-article .cta-btns{display:flex;gap:10px;justify-content:center;flex-wrap:wrap} .ap-article .btn-cta-primary{background:var(–grad);color:#fff;font-family:var(–f-head);font-weight:900;font-size:14px;padding:12px 26px;border-radius:var(–radius);border:none;cursor:pointer;transition:filter .2s,transform .15s;text-decoration:none} .ap-article .btn-cta-primary:hover{filter:brightness(1.1);transform:scale(.97);text-decoration:none;color:#fff} .ap-article .btn-cta-sec{background:transparent;color:#fff;font-family:var(–f-head);font-weight:700;font-size:14px;padding:12px 26px;border-radius:var(–radius);border:1px solid rgba(255,255,255,.28);cursor:pointer;text-decoration:none} .ap-article .btn-cta-sec:hover{border-color:var(–acc-2);color:var(–acc-2);text-decoration:none} .ap-article .sb-widget{background:var(–card);border:1px solid var(–border);border-radius:var(–radius-lg);overflow:hidden;box-shadow:var(–sh-sm)} .ap-article .sb-head{background:#fff;padding:11px 15px;font-family:var(–f-code);font-size:10px;letter-spacing:1.6px;text-transform:uppercase;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(–border);border-left:4px solid var(–acc);color:var(–acc-ink)} .ap-article .ep-wrap{padding:16px} .ap-article .ep-lbl{font-family:var(–f-code);font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(–acc-ink);margin-bottom:7px} .ap-article .ep-name{font-family:var(–f-head);font-weight:900;font-size:16px;color:var(–text-head);margin-bottom:5px} .ap-article .ep-desc{font-size:12px;color:var(–text-muted);line-height:1.5;margin-bottom:12px} .ap-article .ep-stats{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px} .ap-article .ep-stat{text-align:center;background:var(–bg-3);border-radius:8px;padding:7px 9px;flex:1;border:1px solid var(–border)} .ap-article .ep-stat-val{font-family:var(–f-head);font-size:15px;font-weight:900;color:var(–acc-ink)} .ap-article .ep-stat-lbl{font-size:9.5px;color:var(–text-muted);font-family:var(–f-code);letter-spacing:.5px} .ap-article .ep-btn{width:100%;background:var(–grad);color:#fff;font-family:var(–f-head);font-weight:900;font-size:13px;padding:10px;border-radius:8px;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;text-decoration:none} .ap-article .ep-btn:hover{filter:brightness(1.08);text-decoration:none;color:#fff} .ap-article .stepper-wrap{padding:14px 16px 16px} .ap-article .step-progress-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px} .ap-article .step-pct{font-family:var(–f-head);font-weight:900;font-size:22px;color:var(–acc-ink);line-height:1} .ap-article .step-pct span{font-size:12px;font-weight:600;color:var(–text-muted)} .ap-article .step-pct-lbl{font-family:var(–f-code);font-size:9px;letter-spacing:1px;text-transform:uppercase;color:var(–text-muted);text-align:right;line-height:1.4} .ap-article .stepper{position:relative;padding-left:6px} .ap-article .stepper-rail{position:absolute;left:12px;top:8px;bottom:8px;width:3px;background:var(–bg-3);border-radius:2px;overflow:hidden} .ap-article .stepper-rail-fill{position:absolute;left:0;top:0;width:100%;height:0;background:var(–grad);border-radius:2px;transition:height .25s ease} .ap-article .step{position:relative;display:flex;align-items:flex-start;gap:11px;padding:7px 0;cursor:pointer} .ap-article .step-dot{width:15px;height:15px;border-radius:50%;background:#fff;border:2px solid var(–border-md);flex-shrink:0;margin-top:2px;z-index:1;transition:all .25s ease;display:flex;align-items:center;justify-content:center} .ap-article .step-dot::after{content:”;width:5px;height:5px;border-radius:50%;background:transparent;transition:background .25s} .ap-article .step-label{font-size:12.5px;color:var(–text-muted);line-height:1.4;transition:color .2s,font-weight .2s;font-weight:500} .ap-article .step.done .step-dot{border-color:var(–acc);background:var(–acc)} .ap-article .step.done .step-dot::after{background:#fff} .ap-article .step.active .step-dot{border-color:var(–acc-ink);box-shadow:0 0 0 4px var(–acc-dim);background:#fff} .ap-article .step.active .step-dot::after{background:var(–acc-ink)} .ap-article .step.active .step-label{color:var(–acc-ink);font-weight:700} .ap-article .step:hover .step-label{color:var(–text-head)} .ap-article .bf-list{padding:10px 14px} .ap-article .bf-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(–border);font-size:13px;gap:8px} .ap-article .bf-row:last-child{border-bottom:none;padding-bottom:0} .ap-article .bf-use{color:var(–text-muted);flex-shrink:0;font-weight:500} .ap-article .bf-pick{color:var(–acc-ink);font-family:var(–f-head);font-weight:700;font-size:11.5px;background:var(–acc-dim);padding:3px 8px;border-radius:6px;border:1px solid rgba(0,184,94,.2);text-align:right;line-height:1.3;max-width:120px} .ap-article .sb-cta{padding:16px;text-align:center} .ap-article .sb-cta-d{font-size:12px;color:var(–text-muted);margin-bottom:12px;line-height:1.5} .ap-article .sb-cta-btn{width:100%;background:var(–acc-ink);color:#fff;font-family:var(–f-head);font-weight:800;font-size:13px;padding:10px;border-radius:8px;border:none;cursor:pointer} .ap-article .sb-cta-btn:hover{filter:brightness(1.08)} @media(max-width:920px){.ap-article .two-col{grid-template-columns:1fr} .ap-article .sidebar-col{position:static} .ap-article .ql-card{grid-template-columns:52px 1fr} .ap-article .ql-metrics{display:none} .ap-article .pb-grid,.ap-article .pb-pc,.ap-article .pb-test-grid{grid-template-columns:1fr}} @media(max-width:540px){.ap-article .types-grid{grid-template-columns:1fr 1fr} .ap-article .method-cells{grid-template-columns:1fr 1fr}} .ap-article .bench-lead{font-size:14px;color:var(–text-muted);margin:0 0 18px} .ap-article .bench-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px} .ap-article .bench-card{background:#fff;border:1px solid var(–border);border-radius:var(–radius-lg);box-shadow:var(–sh-sm);padding:18px 20px} .ap-article .bench-card.full{grid-column:1 / -1} .ap-article .bench-ctitle{font-family:var(–f-code);font-size:11.5px;letter-spacing:.06em;color:var(–acc-ink);font-weight:600;text-transform:uppercase;margin:0 0 14px} .ap-article .bench-row{display:grid;grid-template-columns:120px 1fr auto;align-items:center;gap:10px;margin:9px 0} .ap-article .bench-name{font-size:13px;color:var(–text-body);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .ap-article .bench-track{height:9px;background:var(–bg-3);border-radius:5px;overflow:hidden} .ap-article .bench-fill{height:100%;width:var(–tw,0);border-radius:5px;transition:width 1.1s cubic-bezier(.22,.61,.36,1) .15s;background:var(–acc)} .ap-article.ap-js .bench-fill{width:0} .ap-article .bench-val{font-family:var(–f-code);font-size:12.5px;font-weight:600;color:var(–text-head);min-width:48px;text-align:right} .ap-article .bench-card .bench-row:nth-child(2) .bench-fill,.ap-article .bench-card .bench-row:nth-child(3) .bench-fill{background:linear-gradient(90deg,var(–top-a),var(–top-b))} .ap-article .bench-card .bench-row:nth-child(4) .bench-fill,.ap-article .bench-card .bench-row:nth-child(5) .bench-fill{background:var(–mid)} .ap-article .bench-card .bench-row:nth-child(6) .bench-fill,.ap-article .bench-card .bench-row:nth-child(7) .bench-fill{background:#8a98a6} .ap-article .bench-card .bench-row:nth-child(8) .bench-fill{background:var(–red)} .ap-article .bench-card.full .bench-row:nth-child(2) .bench-val::after{content:’ \2605′;color:#f5b50a} .ap-article .price-lead{font-size:14px;color:var(–text-muted);margin:0 0 16px} .ap-article .price-box{background:#fff;border:1px solid var(–border);border-radius:var(–radius-lg);box-shadow:var(–sh-sm);overflow:hidden} .ap-article .ptabs{display:flex;flex-wrap:wrap;gap:2px;background:var(–bg-2);border-bottom:1px solid var(–border);padding:6px 8px} .ap-article .ptab{appearance:none;border:none;background:transparent;font-family:var(–f-head);font-weight:700;font-size:13px;color:var(–text-muted);padding:9px 14px;border-radius:8px;cursor:pointer;transition:all .18s;white-space:nowrap} .ap-article .ptab:hover{color:var(–acc-ink);background:var(–acc-dim)} .ap-article .ptab[aria-selected=”true”]{color:var(–acc-ink);background:#fff;box-shadow:0 -2px 0 var(–acc) inset} .ap-article .ppanel{display:none;padding:20px} .ap-article .ppanel.active{display:block;animation:apFade .25s ease} @keyframes apFade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}} .ap-article .ppanel-h{font-family:var(–f-head);font-weight:800;font-size:18px;color:var(–text-head);margin:0 0 4px} .ap-article .ppanel-sum{font-size:13.5px;color:var(–text-muted);margin:0 0 16px;line-height:1.55} .ap-article .plan-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px} .ap-article .plan-card{position:relative;border:1px solid var(–border);border-radius:var(–radius);padding:16px 15px;background:var(–bg-2)} .ap-article .plan-card.feat{border-color:var(–pos);box-shadow:0 0 0 1px var(–pos)} .ap-article .plan-tag{position:absolute;top:12px;right:12px;font-family:var(–f-code);font-size:9.5px;letter-spacing:.05em;font-weight:600;text-transform:uppercase;color:var(–text-muted)} .ap-article .plan-name{font-family:var(–f-head);font-weight:800;font-size:15px;color:var(–pos-ink);margin:0 0 6px} .ap-article .plan-price{font-family:var(–f-head);font-weight:800;font-size:30px;color:var(–pos-ink);line-height:1} .ap-article .plan-price .per{font-size:13px;font-weight:600;color:var(–text-muted)} .ap-article .plan-sub{font-size:11.5px;color:var(–text-muted);margin:5px 0 12px} .ap-article .plan-feats{list-style:none;margin:0;padding:0} .ap-article .plan-feats li{font-size:12.5px;color:var(–text-body);padding:5px 0 5px 22px;position:relative;line-height:1.4} .ap-article .plan-feats li.pf-y::before{content:’\2713′;position:absolute;left:0;color:var(–pos-ink);font-weight:700} .ap-article .plan-feats li.pf-n{color:var(–text-muted)} .ap-article .plan-feats li.pf-n::before{content:’\2715′;position:absolute;left:0;color:var(–red);font-weight:700} .ap-article .price-strip{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;align-items:center;padding:14px 16px;background:var(–bg-2);border-top:1px solid var(–border);font-family:var(–f-code);font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(–text-muted)} .ap-article .price-strip .pstop{color:var(–pos-ink);font-weight:600} .ap-article .price-strip .pspill{display:inline-block;margin-left:6px;background:var(–pos-dim);color:var(–pos-ink);border:1px solid rgba(0,232,122,.4);border-radius:20px;padding:1px 8px;font-size:9.5px} @media(max-width:760px){.ap-article .bench-grid{grid-template-columns:1fr} .ap-article .plan-grid{grid-template-columns:1fr} .ap-article .bench-row{grid-template-columns:96px 1fr auto}} .ap-article .steps{list-style:none;counter-reset:apst;display:flex;flex-direction:column;gap:10px;margin:14px 0;padding:0} .ap-article .steps li{counter-increment:apst;position:relative;padding:12px 14px 12px 52px;background:var(–card);border:1px solid var(–border);border-radius:var(–radius);font-size:15px;line-height:1.65;color:var(–text-body)} .ap-article .steps li::before{content:counter(apst);position:absolute;left:14px;top:12px;width:26px;height:26px;border-radius:50%;background:var(–grad);color:var(–on-grad);font-family:var(–f-head);font-weight:800;font-size:13px;display:flex;align-items:center;justify-content:center} .ap-article .steps li strong{display:block;margin-bottom:2px} .ap-article pre.code{background:var(–navy);color:#e6edf5;font-family:var(–f-code);font-size:12.5px;line-height:1.6;padding:14px 16px;border-radius:var(–radius);overflow-x:auto;margin:14px 0;white-space:pre} .ap-article code{font-family:var(–f-code);font-size:.9em;background:var(–bg-3);border:1px solid var(–border);border-radius:5px;padding:1px 5px} .ap-article pre.code code{background:none;border:none;padding:0;color:inherit;font-size:inherit} .ap-article .callout{border:1px solid var(–border);border-left:4px solid var(–acc);background:var(–acc-dim-2);border-radius:var(–radius);padding:14px 16px;margin:16px 0;font-size:14.5px;line-height:1.65;color:var(–text-body)} .ap-article .callout.warn{border-left-color:var(–yellow);background:rgba(217,119,6,.06)} .ap-article .callout-t{font-family:var(–f-code);font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:var(–acc-ink);margin-bottom:6px;font-weight:600} .ap-article .callout.warn .callout-t{color:var(–yellow)} .ap-article .dt-wrap{overflow-x:auto;margin:18px 0;border:1px solid var(–border);border-radius:var(–radius-lg);box-shadow:var(–sh-sm)} .ap-article table.dt{width:100%;border-collapse:collapse;font-size:13.5px;min-width:480px;background:var(–card)} .ap-article table.dt th{background:var(–bg-3);color:var(–text-muted);font-family:var(–f-code);font-size:9.5px;letter-spacing:1px;text-transform:uppercase;text-align:left;padding:11px 14px;border-bottom:2px solid var(–border)} .ap-article table.dt td{padding:10px 14px;border-bottom:1px solid var(–border);color:var(–text-body);vertical-align:top} .ap-article table.dt tr:last-child td{border-bottom:none} .ap-article table.dt td:first-child{font-weight:600;color:var(–text-head)} .ap-article .tbl-note{font-size:12px;color:var(–text-muted);margin:-8px 0 16px} .ap-article .quote{border-left:4px solid var(–border-md);padding:6px 0 6px 16px;margin:16px 0;color:var(–text-head);font-size:15.5px;line-height:1.7;font-style:italic} .ap-article .quote cite{display:block;font-style:normal;font-size:12.5px;color:var(–text-muted);margin-top:6px} .ap-article .src-list{list-style:none;counter-reset:apsrc;display:flex;flex-direction:column;gap:7px;margin:14px 0 0;padding:0} .ap-article .src-list li{counter-increment:apsrc;font-size:13px;padding-left:26px;position:relative;line-height:1.5;color:var(–text-body)} .ap-article .src-list li::before{content:counter(apsrc);position:absolute;left:0;top:1px;width:18px;height:18px;border-radius:50%;background:var(–bg-3);border:1px solid var(–border);font-family:var(–f-code);font-size:9.5px;display:flex;align-items:center;justify-content:center;color:var(–text-muted)} .ap-article .src-t{font-family:var(–f-code);font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(–acc-ink);margin-top:16px;font-weight:600} .ap-article .vs-head{display:grid;grid-template-columns:1fr auto 1fr;gap:14px;align-items:stretch;margin:18px 0} .ap-article .vs-side{background:var(–card);border:1px solid var(–border);border-radius:var(–radius-lg);padding:16px;text-align:center;box-shadow:var(–sh-sm)} .ap-article .vs-side.win{border-color:var(–acc);box-shadow:0 0 0 1px var(–acc)} .ap-article .vs-name{font-family:var(–f-head);font-weight:900;font-size:18px;color:var(–text-head)} .ap-article .vs-score{font-family:var(–f-head);font-weight:900;font-size:30px;color:var(–acc-ink);line-height:1.1;margin:6px 0 2px} .ap-article .vs-sub{font-size:12.5px;color:var(–text-muted);line-height:1.5} .ap-article .vs-mid{align-self:center;font-family:var(–f-head);font-weight:900;font-size:14px;color:var(–text-muted);background:var(–bg-3);border:1px solid var(–border);border-radius:50%;width:42px;height:42px;display:flex;align-items:center;justify-content:center} .ap-article .vs-logo{height:38px;display:flex;align-items:center;justify-content:center;margin-bottom:8px} .ap-article .vs-logo img{max-height:34px;max-width:130px;object-fit:contain;border-radius:0} .ap-article .ql-mlbl{width:86px} .ap-article .ql-mval{width:66px} @media(max-width:920px){.ap-article .sidebar-col{grid-column:1;grid-row:auto}.ap-article .sb-progress{display:none}} .ap-article .fig-note{font-size:12px;color:var(–text-muted);margin:10px 0 0} .ap-article .btn-visit,.ap-article .btn-visit:hover,.ap-article .btn-cta-primary,.ap-article .btn-cta-primary:hover,.ap-article .ep-btn,.ap-article .ep-btn:hover,.ap-article .author-ava{color:var(–on-grad)} .ap-article .sb-cta-btn,.ap-article .sb-cta-btn:hover{color:#fff} .ap-article .bench-card .bench-row:nth-child(n+9) .bench-fill{background:#8a98a6} @media(max-width:540px){.ap-article .vs-head{grid-template-columns:1fr}.ap-article .vs-mid{justify-self:center}} .ap-article .sec-h2::before{background:linear-gradient(180deg,#00e87a,#3d8ef0)}
Updated November 2026 9 min read Guide

How to Rotate Proxies in Python: Requests, Selenium and Scrapy (2026)

Say your scraper sends 500 requests to one site from one IP, and after the first 50 the site answers HTTP 429 Too Many Requests. More threads won’t fix it; more IPs will. This guide shows how to rotate proxies in Python with requests, Scrapy and Selenium, in short samples you can paste and run.

PA
ProxyAdvice Editorial Team, Proxy Research Desk
ProxyAdvice · Provider comparisons from published pricing and docs
✓ Sources checked November 5, 2026
How to rotate proxies in Python: code sending requests through a ring of proxy servers
🤖 Direct answer

Rotating proxies means sending each request through a different proxy IP, so no single address carries enough traffic to hit a site’s rate limit. In Python you either cycle through a list of proxy URLs yourself, passing one per request, or send everything to a provider’s rotating gateway, which picks a new IP for every request on its side.

⚡ TL;DR · skip here if you are in a hurry
  • requests: keep a list, take the next URL with itertools.cycle, and pass it under both the http and https keys.
  • Scrapy and Selenium: Scrapy takes a proxy per request in meta; Selenium takes one per browser, so rotation means a new driver.
  • Gateway: with Oxylabs, one address rotates per request by default, and a session ID holds an IP for up to 1,440 minutes.

What does it mean to rotate proxies?

Proxy rotation is changing the IP address behind your requests as you go: per request, per batch, or per browser session.

MethodWho picks the IPFits
Proxy listYour codeStatic IPs you bought one by one (datacenter, ISP)
Rotating gatewayThe providerResidential and mobile plans billed per GB
Sticky sessionThe provider, held for a set timeLogins, carts, anything that needs the same IP for a few minutes

Most residential plans give you a gateway; most static plans give you a list. Check which one you bought before writing code.

The samples below use https://api.ipify.org as the target because it returns only the caller’s IP, so you can see each rotation. Replace the words in capitals with your own proxy details.

How to rotate proxies in Python with requests

  1. Install requestsRun pip install requests. You should see Successfully installed, or Requirement already satisfied.
  2. Paste your proxy listOne full URL per proxy: scheme, login, host and port.
  3. Run the loopEach request takes the next proxy. With 3 proxies, the 6 lines of output repeat in a pattern of 3 IPs.
import itertools
import requests

PROXIES = [
    "http://USERNAME:PASSWORD@HOST1:PORT",
    "http://USERNAME:PASSWORD@HOST2:PORT",
    "http://USERNAME:PASSWORD@HOST3:PORT",
]
pool = itertools.cycle(PROXIES)

for _ in range(6):
    proxy = next(pool)
    r = requests.get("https://api.ipify.org", proxies={"http": proxy, "https": proxy}, timeout=10)
    print(r.status_code, r.text)

itertools.cycle walks the list in order and starts again at the end. Use random.choice(PROXIES) instead when you don’t want a fixed order; it can pick the same proxy twice in a row.

Always set a timeout
The requests docs say requests don’t time out unless you set one, and without it your code may hang for minutes or more. A dead proxy is the usual cause. timeout=10 caps each attempt at 10 seconds.

What should your code do when a proxy fails?

Move to the next proxy and try again, up to a limit. A failed proxy raises an exception; a refused login can come back as a 407 status instead. The function below handles both.

import itertools
import requests

PROXIES = [
    "http://USERNAME:PASSWORD@HOST1:PORT",
    "http://USERNAME:PASSWORD@HOST2:PORT",
    "http://USERNAME:PASSWORD@HOST3:PORT",
]
pool = itertools.cycle(PROXIES)


def fetch(url, tries=3):
    for _ in range(tries):
        proxy = next(pool)
        try:
            r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=10)
            if r.status_code == 200:
                return r
            print("status", r.status_code, "from", proxy.split("@")[1])
        except requests.RequestException as e:
            print("failed", proxy.split("@")[1], type(e).__name__)
    raise RuntimeError(f"{tries} proxies in a row failed for {url}")


print(fetch("https://api.ipify.org").text)

When it works, you see one IP. When the first proxy is down, you see a failed line naming it, then the IP from the next one. If 3 proxies in a row fail, the error stops the script instead of looping forever.

Retrying on any status other than 200 is a starting point. A 404 means the page doesn’t exist, so a new IP won’t help; a 403 or 429 usually means the IP was blocked or rate-limited, which is what rotation fixes.

How do you rotate proxies in Scrapy?

Scrapy’s built-in HttpProxyMiddleware reads a proxy from each request’s meta, so rotation is one line per request. Its docs list both http://some_proxy_server:port and the form with username:password@ before the host.

import random
import scrapy

PROXIES = [
    "http://USERNAME:PASSWORD@HOST1:PORT",
    "http://USERNAME:PASSWORD@HOST2:PORT",
    "http://USERNAME:PASSWORD@HOST3:PORT",
]


class IpSpider(scrapy.Spider):
    name = "ip"

    async def start(self):
        for _ in range(6):
            proxy = random.choice(PROXIES)
            yield scrapy.Request("https://api.ipify.org", meta={"proxy": proxy}, dont_filter=True)

    def parse(self, response):
        yield {"ip": response.text}

Save it as ip_spider.py and run scrapy runspider ip_spider.py -o ips.jsonl. When it works, ips.jsonl holds 6 lines like {“ip”: “…”} with the IPs of your proxies. The async def start() method needs Scrapy 2.13 or later.

A proxy set in meta wins over the http_proxy and https_proxy environment variables, according to the same docs. For a full project, move the random.choice line into a downloader middleware so every spider gets it.

How do you rotate proxies in Selenium?

A browser keeps one proxy for its whole life. Rotating means closing the driver and starting a new one with the next proxy.

from selenium import webdriver

PROXIES = ["HOST1:PORT", "HOST2:PORT", "HOST3:PORT"]

for proxy in PROXIES:
    options = webdriver.ChromeOptions()
    options.add_argument(f"--proxy-server=http://{proxy}")
    driver = webdriver.Chrome(options=options)
    driver.get("https://api.ipify.org")
    print(proxy, driver.find_element("tag name", "body").text)
    driver.quit()

When it works, each line shows a proxy and the IP that ipify saw for it. Selenium 4.6 and later fetch the matching driver for your Chrome, so pip install selenium is the only setup.

No login in the Chrome flag
Chrome’s –proxy-server switch takes a scheme, host and port, not a username and password. Allowlist your own IP in the provider’s dashboard so the proxy accepts you without a login.

Starting a browser per proxy is slow next to an HTTP request. If you only need page HTML, the requests loop above does the same job faster. For a closer look at the two browser tools, see our Playwright vs Selenium comparison.

When should the provider do the rotating?

When you bought residential or mobile traffic. Those plans hand you one address, and the provider swaps the IP behind it.

Oxylabs is a documented example. Its session control page says its residential proxies rotate with every request by default through pr.oxylabs.io:7777. Adding sessid and a random string to the username holds one IP; a session lasts 10 minutes by default, or ends after 60 seconds without requests, and sesstime extends it up to 1,440 minutes.

import requests

ROTATING = "http://customer-USERNAME:PASSWORD@pr.oxylabs.io:7777"
STICKY = "http://customer-USERNAME-sessid-abcd1234-sesstime-30:PASSWORD@pr.oxylabs.io:7777"

for proxy in (ROTATING, ROTATING, STICKY, STICKY):
    r = requests.get("https://api.ipify.org", proxies={"http": proxy, "https": proxy}, timeout=15)
    print(r.text)

When it works, the first two lines show different IPs and the last two show the same one. Other providers use their own username format for sessions, so copy theirs from the dashboard rather than this one.

Prices for these plans are per GB. Our best residential proxies ranking compares them, and our proxy speed claims article explains what the advertised success rates measure.

What is the most common proxy rotation mistake?

Passing the proxy under the http key only. requests picks the proxy by the scheme of the URL you fetch, so an https:// page with no https key goes out from your own IP. No error appears; the scraper just stops rotating.

import requests

proxy = "http://USERNAME:PASSWORD@HOST:PORT"
wrong = {"http": proxy}                  # https:// URLs skip the proxy
right = {"http": proxy, "https": proxy}  # both schemes use it

print(requests.get("https://api.ipify.org", proxies=wrong, timeout=10).text)
print(requests.get("https://api.ipify.org", proxies=right, timeout=10).text)

The first line prints your real IP, the second the proxy’s. Always fill both keys, as every sample in this guide does.

A second trap: session.proxies
The requests docs warn that values in session.proxies are overwritten by proxies set in the environment, such as HTTPS_PROXY. Pass proxies= on each request, as above, and the environment can’t override your rotation.

Which rotation method should you start with?

Your setupStart with
requests or httpx script, residential planGateway URL in both keys, plus the retry function
requests script, list of static IPsitertools.cycle over the list, plus the retry function
Scrapy projectmeta proxy set in a downloader middleware
Selenium or another browserOne driver per proxy, IP allowlisted

Free proxies work for trying the code: our free proxy list needs no login. Check which ones are alive with the free proxy checker at GoToProxy.

Most readers here run a requests script against one site. For that case, buy a rotating residential plan, put the gateway URL under both the http and https keys, and wrap calls in the retry function above. The provider handles IP changes, so your code stays at about 20 lines, and the retry covers the requests that still fail. Compare plans in all proxy rankings and independent proxy ratings on ProxyRates before you pick one.

FAQ

No. requests sends each call through whatever proxy you pass in proxies=. To rotate, keep a list and pass a different URL each time, for example with itertools.cycle, or pass one rotating gateway URL and let the provider change the IP.
itertools.cycle spreads load evenly: with 3 proxies, each gets every third request. random.choice is less predictable but can pick the same proxy twice in a row. For a small list, cycle is the safer default.
Chrome’s –proxy-server switch doesn’t accept a login. Allowlist your own IP in the provider’s dashboard, then start a new driver with the next proxy for each rotation. Each driver keeps one proxy until you quit it.
Set request.meta[‘proxy’] on each request. Scrapy’s HttpProxyMiddleware reads it, accepts a login in the URL, and gives it priority over the http_proxy and https_proxy environment variables. Choose the proxy with random.choice or a cycle in start() or in a downloader middleware.
A sticky session keeps the same IP for a set time on a rotating gateway. On Oxylabs, adding sessid to the username starts one; it lasts 10 minutes by default or ends after 60 seconds idle, and sesstime extends it to as much as 1,440 minutes.
The usual cause is a proxies dict with only an http key while you fetch https:// pages, so requests sends those directly. Put the proxy under both keys. If you set session.proxies, an HTTPS_PROXY environment variable can also override it.
Yes. httpx takes one proxy per client through the proxy= argument, for example httpx.Client(proxy=URL). To rotate a list, create a client per proxy, or send everything to a rotating gateway URL so one client is enough.
🔎
How we put this together
Last verified: October 30, 2026 · ProxyAdvice · affiliate links marked ↗ may earn a commission

The requests and Scrapy samples were run on October 30, 2026 with requests 2.34.2 and Scrapy 2.19.0 against three local test proxies, with the capital-letter placeholders swapped for local addresses. The Selenium sample follows Selenium’s browser options page, and the gateway sample follows Oxylabs’ session control page as read on October 30, 2026. Library behaviour comes from each project’s own docs.

Samples
6
requests
2.34.2
Scrapy
2.19.0
Docs read
October 30, 2026
(function(){ if (window.__apArticleInit) return; window.__apArticleInit = true; var reduce = window.matchMedia ? window.matchMedia(‘(prefers-reduced-motion:reduce)’).matches : false; var wrap = document.querySelector(‘[data-ap-article]’); if (wrap) wrap.classList.add(‘ap-js’); try{ var io = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if(!e.isIntersecting) return; e.target.classList.add(‘vis’); e.target.querySelectorAll(‘.bar-anim’).forEach(function(b){ b.style.width = b.style.getPropertyValue(‘–tw’) || ‘0%’; }); io.unobserve(e.target); }); },{threshold:0.07}); document.querySelectorAll(‘.reveal’).forEach(function(el){ io.observe(el); }); }catch(e){} function togglePanel(btn){ var panel = document.getElementById(btn.getAttribute(‘data-target’)); if(!panel) return; var open = btn.getAttribute(‘aria-expanded’) === ‘true’; if(open){ panel.style.maxHeight = ‘0px’; btn.setAttribute(‘aria-expanded’,’false’); }else{ panel.style.maxHeight = panel.scrollHeight + ‘px’; btn.setAttribute(‘aria-expanded’,’true’); setTimeout(function(){ if(btn.getAttribute(‘aria-expanded’)===’true’) panel.style.maxHeight = panel.scrollHeight + ‘px’; }, 520); } } document.querySelectorAll(‘.read-more-btn,.btn-expand’).forEach(function(btn){ btn.addEventListener(‘click’, function(){ togglePanel(btn); }); }); document.querySelectorAll(‘.faq-q’).forEach(function(q){ q.addEventListener(‘click’, function(){ var item = q.closest(‘.faq-item’); var was = item.classList.contains(‘open’); document.querySelectorAll(‘.faq-item.open’).forEach(function(i){ i.classList.remove(‘open’); var a=i.querySelector(‘.faq-a’); if(a) a.style.maxHeight=’0px’; var b=i.querySelector(‘.faq-q’); if(b) b.setAttribute(‘aria-expanded’,’false’); }); if(!was){ item.classList.add(‘open’); var a=item.querySelector(‘.faq-a’); if(a) a.style.maxHeight = a.scrollHeight + ‘px’; q.setAttribute(‘aria-expanded’,’true’); } }); }); window.apCopy = function(el, code){ try{ if(navigator.clipboard ? navigator.clipboard.writeText : false){ navigator.clipboard.writeText(code).then(function(){ var o=el.textContent; el.textContent=(wrap ? wrap.getAttribute(‘data-copied’) : ”)||’✓ Copied’; setTimeout(function(){ el.textContent=o; },1600); }); } }catch(e){} }; (function(){ var tabs = Array.prototype.slice.call(document.querySelectorAll(‘.ptab’)); if(!tabs.length) return; function activate(tab){ tabs.forEach(function(t){ t.setAttribute(‘aria-selected’, t===tab ? ‘true’:’false’); }); document.querySelectorAll(‘.ppanel’).forEach(function(p){ p.classList.remove(‘active’); }); var panel = document.getElementById(tab.getAttribute(‘data-panel’)); if(panel){ panel.classList.add(‘active’); panel.querySelectorAll(‘.bar-anim’).forEach(function(b){ b.style.width = b.style.getPropertyValue(‘–tw’) || ‘0%’; }); } } tabs.forEach(function(t){ t.addEventListener(‘click’, function(){ activate(t); }); t.addEventListener(‘keydown’, function(ev){ var i = tabs.indexOf(t), n = tabs.length; if(ev.key===’ArrowRight’){ ev.preventDefault(); tabs[(i+1)%n].focus(); activate(tabs[(i+1)%n]); } if(ev.key===’ArrowLeft’){ ev.preventDefault(); tabs[(i-1+n)%n].focus(); activate(tabs[(i-1+n)%n]); } }); }); })(); var stepper = document.getElementById(‘stepper’); var fill = document.getElementById(‘stepperFill’); var pctEl = document.getElementById(‘stepPct’); var topBar = document.getElementById(‘apProgressTop’); var sections = Array.prototype.slice.call(document.querySelectorAll(‘section[data-step]’)); var steps = []; if(stepper ? sections.length : 0){ sections.forEach(function(sec, i){ var div = document.createElement(‘div’); div.className = ‘step’; div.setAttribute(‘data-i’, i); div.innerHTML = ‘‘+ (sec.getAttribute(‘data-step’)||(((wrap ? wrap.getAttribute(‘data-step-word’) : ”)||’Step’)+’ ‘+(i+1))) +’‘; div.addEventListener(‘click’, function(){ var y = sec.getBoundingClientRect().top + window.pageYOffset – 70; window.scrollTo({top:y, behavior: reduce ? ‘auto’ : ‘smooth’}); }); stepper.appendChild(div); steps.push(div); }); } function clamp(v,a,b){ return Math.max(a, Math.min(b, v)); } function onScroll(){ var docH = document.documentElement.scrollHeight – window.innerHeight; var sc = window.pageYOffset; var pct = docH > 0 ? clamp(sc / docH, 0, 1) : 0; if(topBar) topBar.style.width = (pct*100).toFixed(1) + ‘%’; if(pctEl) pctEl.innerHTML = Math.round(pct*100) + ‘%‘; var line = window.innerHeight * 0.4; var activeIdx = 0; sections.forEach(function(sec, i){ if(sec.getBoundingClientRect().top – line <= 0) activeIdx = i; }); steps.forEach(function(st, i){ st.classList.remove('active','done'); if(i < activeIdx) st.classList.add('done'); else if(i === activeIdx) st.classList.add('active'); }); if(fill ? steps.length : 0){ var railTop = stepper.querySelector('.stepper-rail').getBoundingClientRect().top; var target = steps[activeIdx].querySelector('.step-dot').getBoundingClientRect(); var h = clamp((target.top + target.height/2) – railTop, 0, 100000); var byPct = (stepper.querySelector('.stepper-rail').offsetHeight) * pct; fill.style.height = Math.max(h, byPct) + 'px'; } } var ticking = false; function requestScroll(){ if(ticking) return; ticking = true; window.requestAnimationFrame(function(){ onScroll(); ticking = false; }); } window.addEventListener('scroll', requestScroll, {passive:true}); window.addEventListener('resize', requestScroll); onScroll(); })();
Proxy Advice
Logo