How to Rotate Proxies Automatically
How rotation works with sticky 4G proxies: change the IP on demand, rotate between several proxies in your own code and know when to keep the same IP.
Rotation spreads requests across IP addresses. You can change the IP on demand from your account or rotate between several proxies in your own code. This guide covers both.
“Rotating proxies” can mean several different things, and mixing them up leads to broken sessions and wasted traffic. Below you will find what each model does, when to use it and Python code for rotation across several proxies, with health tracking and per-proxy pacing.
- Your IP stays the same while you work. That is the right default for logins, checkouts and long sessions. After a pause of about 10 minutes without connections, the next connection can get a different IP.
- Change the IP on demand between jobs, not in the middle of one.
- To spread a workload, order several proxies and rotate between them in your own code.
- Rotate on errors and on a plan you choose, and never faster than a site allows.
- Rotation does not entitle you to exceed a site’s rate limits or ignore its terms.
1.Three models of rotation
Providers use the word “rotation” for different things. This table separates them and shows what you get with Xameleon.
| Model | How it works | Suits | With Xameleon |
|---|---|---|---|
| Sticky IP | The IP stays while you work | Logins, checkouts, long sessions, monitoring that compares results over time | Yes, this is how every proxy works |
| Change on demand | You ask for a new IP when you want one | A clean start between jobs | Yes, with Change IP in your account; API control is planned |
| Rotation across your proxies | Your code picks the next of several proxies | Spreading a steady workload across several connections | Yes, order more than one proxy and rotate in your code |
| Timed or per-request rotation | The provider swaps the IP automatically | Stateless jobs where every request may leave from a different address | Not offered: the IP never changes unless you change it |
So “rotate proxies automatically” with Xameleon means your own code choosing between several proxies, and the on-demand change when you want a new IP. The rest of this guide shows how to do both well.
2.Change the IP on demand
Your IP stays the same while you work, so a login or checkout keeps running from one address. When you want a fresh IP, press Change IP in your account: on Shared proxies you are moved to another modem in the same country, and on Exclusive proxies your own modem rotates. A planned API will let you do the same from your code.
Open connections can drop while the address changes, and a new IP can take a few seconds to appear. This helper waits until the exit IP really differs before your job continues:
import time
import requests
def exit_ip(proxies):
r = requests.get("https://xameleon.cloud/api/tools/ip", proxies=proxies, timeout=(5, 20))
r.raise_for_status()
return r.json()["ip"]
def wait_for_new_ip(proxies, old_ip, attempts=12, pause=5):
for _ in range(attempts):
time.sleep(pause)
try:
ip = exit_ip(proxies)
except requests.exceptions.RequestException:
continue # the connection may drop for a moment while the IP changes
if ip != old_ip:
return ip
return None
proxy = "http://USERNAME:PASSWORD@HOST:PORT"
proxies = {"http": proxy, "https": proxy}
old_ip = exit_ip(proxies)
input("Press Change IP in your account, then press Enter... ")
print("New IP:", wait_for_new_ip(proxies, old_ip))Every call creates a new connection, so nothing keeps pointing at the old address. If you use sessions, close them and open new ones after the change.
3.Rotation on the client side
If you have several proxies, cycle through them in your code.
Each proxy has its own connection details in your account, so you can use them side by side. The simplest rotation takes them in turn:
from itertools import cycle
import requests
proxy_urls = [
"http://USER1:PASS1@HOST1:PORT1",
"http://USER2:PASS2@HOST2:PORT2",
"http://USER3:PASS3@HOST3:PORT3",
]
turns = cycle(proxy_urls)
urls = ["https://example.com/page/1", "https://example.com/page/2", "https://example.com/page/3"]
for url in urls:
proxy = next(turns)
r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=(5, 20))
print(url, r.status_code)Percent-encode special characters in each username and password, as in the Python proxies guide. That guide also covers sessions, timeouts and retries.
4.Rotate on errors, not on a whim
Switch to the next proxy after a failure or a rate-limit response, and wait before retrying. Rotating faster than a site allows does not make heavy traffic acceptable.
| What you get | What it means | What to do |
|---|---|---|
200 | The request worked | Keep using the same proxy |
429 or 503, often with Retry-After | The site asks you to slow down | Rest this proxy, wait at least the Retry-After time and lower your rate |
403 | The site refuses the request | Stop and read the site’s terms and robots.txt. More proxies are not the answer |
407 | The proxy rejected your credentials | Fix the credentials: rotating does not help |
| A connection error or a timeout | The proxy or the network had a problem | Rest the proxy and retry on another one after a short wait |
5xx | The site has a problem | Retry later with a growing delay |
A small pool class does the bookkeeping. It hands out proxies in turn and lets a failing one rest for a while:
import itertools
import time
class ProxyPool:
"""Hands out proxies in turn and lets the ones that fail rest for a while."""
def __init__(self, proxy_urls, cooldown=60):
self.urls = list(proxy_urls)
self.cooldown = cooldown
self.resting_until = {} # proxy URL -> time it may be used again
self._turns = itertools.cycle(self.urls)
def get(self):
for _ in range(len(self.urls)):
url = next(self._turns)
if self.resting_until.get(url, 0) <= time.monotonic():
return url
raise RuntimeError("every proxy is resting: wait or add more proxies")
def rest(self, url):
self.resting_until[url] = time.monotonic() + self.cooldown
pool = ProxyPool(proxy_urls, cooldown=60)5.Sticky sessions for multi-step flows
Keep the same IP for a whole login or checkout flow, and change it only when the flow is done. Changing the IP in the middle of a flow can break the session.
Give every flow its own session, bound to one proxy. Cookies and the IP then stay together, and nothing leaks from one proxy to another:
def run_flow(proxy_url, steps):
"""Runs every step of one flow through the same proxy, the same IP and the same cookies."""
with requests.Session() as session:
session.proxies.update({"http": proxy_url, "https": proxy_url})
for step in steps:
step(session)
def open_home(session):
session.get("https://example.com/", timeout=(5, 20))
def open_account(session):
session.get("https://example.com/account", timeout=(5, 20))
flows = [[open_home, open_account], [open_home, open_account]]
for flow in flows:
run_flow(pool.get(), flow) # rotate between flows, never inside one6.Set limits for proxy rotation
Cap concurrency per proxy, add delays and respect target sites' rate limits.
Three limits keep a rotation polite. Pace every proxy separately, run no more workers than your plan’s concurrent connections allow, and watch the traffic you use. Several proxies spread a workload, but the site’s rules apply to the whole workload.
import threading
import time
class RateLimiter:
"""Lets one request through every <interval> seconds for each key (a proxy or a site)."""
def __init__(self, interval):
self.interval = interval
self.next_slot = {}
self.lock = threading.Lock()
def wait(self, key):
with self.lock:
now = time.monotonic()
slot = max(self.next_slot.get(key, now), now)
self.next_slot[key] = slot + self.interval
time.sleep(slot - now)
limiter = RateLimiter(interval=2.0) # one request every 2 seconds per proxy7.Putting it together
This fetch function takes the next proxy that is not resting, waits for that proxy’s turn, rests it on trouble and tries another. A thread pool runs it with a fixed number of workers:
from concurrent.futures import ThreadPoolExecutor
import requests
def fetch(url, attempts=3):
for _ in range(attempts):
proxy = pool.get()
limiter.wait(proxy) # pace every proxy separately
try:
r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=(5, 20))
except requests.exceptions.RequestException:
pool.rest(proxy) # a connection problem: let this proxy rest
continue
if r.status_code in (407, 429, 503): # wrong credentials, or the site asks us to slow down
pool.rest(proxy)
continue
return r
return None
urls = [f"https://example.com/page/{i}" for i in range(1, 7)]
with ThreadPoolExecutor(max_workers=3) as workers: # no more workers than your plan allows
for url, r in zip(urls, workers.map(fetch, urls)):
print(url, r.status_code if r else "gave up")The pool is shared by the threads, which is fine for a handful of workers. For a large crawler, put a lock around it or use a queue. Web scraping at scale shows how to set a request budget for the whole job.
8.Common mistakes
- Changing the IP in the middle of a flow. Logins and checkouts can break when the address changes underneath them.
- Rotating on every request for no reason. It adds connections, breaks cookies and does not raise a site’s limits.
- Sharing one session between proxies. Cookies set through one IP end up in requests that leave from another. Use one session per proxy.
- Retrying forever. Stop after a few attempts and rest the proxy. Never retry
407or403in a loop. - Ignoring
Retry-After. When a site says how long to wait, wait. - Not checking the result. After a change, confirm that the exit IP is new before you continue.
9.FAQs
Does Xameleon rotate my IP automatically?
No. Your IP stays the same while you work, which suits logins and long jobs. Change it on demand from your account; API control is planned. Automatic rotation in this guide means your own code choosing between several proxies.
How many proxies do I need?
Divide the requests per minute you need by the rate one proxy can safely handle within the site’s limits. For example, 120 requests a minute at one request every 2 seconds per proxy (30 a minute) calls for four proxies. Start small and measure.
Should I rotate on every request?
Usually not. Keep one proxy for a whole flow or a batch, and rotate between flows.
Does rotation raise a site’s rate limits?
No. Rotation spreads a legitimate workload across your connections. It does not entitle you to exceed a site’s rate limits or ignore its terms.
How do I know that the IP changed?
Call the IP endpoint before and after the change and compare the results. The wait_for_new_ip helper above does exactly that.
What happens to open connections when I change the IP?
They can drop. Close old sessions and start new ones after the change.
Still have questions about mobile proxies? Visit our FAQ page for detailed answers, or contact our support team — we're here to help.