“Should I rotate on every request?” is one of the first questions teams ask when they start using residential proxies, and the honest answer is: it depends on what your requests have in common. Rotating too much breaks flows that a site expects to come from one visitor. Rotating too little piles all your traffic onto one address. Both cost you blocks, retries and bandwidth.
This guide gives a simple way to decide. The deciding question is not about the proxy at all. It is about your work: which requests belong together, and for how long.
The three modes
| Mode | What it does | On Shifter |
|---|---|---|
| Rotate per request | Every request leaves from a different residential IP | The default: no session flag in the username |
| Sticky session | Requests with the same session ID share one IP until the session ends | Add sid-<id> to the username, optionally ttl-<seconds>; the default lifetime is 120 seconds |
| Fixed IP | One address in a chosen city and ISP for the whole billing period | ISP proxies |
A sticky session also ends early if the residential IP behind it disconnects, so code that uses sessions should expect an occasional new IP mid-session. Sticky sessions are kept per region, so tools that share one session should connect through the same regional entry point.
The deciding question: what belongs together?
Look at the smallest unit of work your scraper repeats, and ask two things about it.
1. How many requests make up one unit? A product page fetched on its own is one request. A search followed by six detail pages is a flow. A checkout test is a flow with state: a cart, a session cookie, perhaps a CSRF token.
2. How long does one unit take? Seconds, minutes, or hours?
From those two answers:
- Independent single requests: rotate. Each page stands alone, so there is nothing to gain from keeping an IP, and spreading requests across many addresses keeps any one of them from looking busy.
- A flow that finishes within 30 minutes: sticky session. The site sets cookies on the first request and expects the rest of the flow from the same visitor. Changing IP halfway through a search or a cart looks like a hijacked session, and is a common cause of sudden blocks and lost state.
- Anything longer, or a login you come back to: fixed IP. Sticky sessions are built for minutes, not hours. For a session that lasts all day, or an account you use every day, use an ISP proxy. Our guide to ISP proxy setup for long sessions covers that in detail.
As code
The two functions below encode the rule and build the matching Shifter username:
def proxy_username(user, country=None, session_id=None, ttl_seconds=None):
"""Build a Shifter residential username: rotate per request, or stick to one IP."""
parts = [f"customer-{user}"]
if country:
parts.append(f"country-{country}")
if session_id:
parts.append(f"sid-{session_id}")
if ttl_seconds:
parts.append(f"ttl-{ttl_seconds}")
elif ttl_seconds:
raise ValueError("ttl only works together with a session id")
return "-".join(parts)
def choose_rotation(steps, minutes, needs_login=False, same_ip_required=False):
"""Pick a rotation mode for one unit of work.
steps: requests that belong together (one page = 1, a checkout flow = many)
minutes: how long that unit of work takes
"""
if needs_login and minutes > 30:
return "isp" # a login that outlives a sticky session
if steps == 1 and not same_ip_required:
return "rotate" # independent requests: new IP each time
if minutes <= 30:
return "sticky" # a short flow: one IP until it ends
return "isp"
Applied to four typical jobs:
from rotation import choose_rotation, proxy_username
jobs = { # session ids must not contain hyphens: the username uses them as separators
"products": dict(steps=1, minutes=0.1),
"search": dict(steps=6, minutes=2),
"checkout": dict(steps=15, minutes=8),
"seller": dict(steps=400, minutes=240, needs_login=True),
}
for name, job in jobs.items():
mode = choose_rotation(**job)
if mode == "rotate":
user = proxy_username("USERNAME", "us")
elif mode == "sticky":
user = proxy_username("USERNAME", "us", session_id=name, ttl_seconds=job["minutes"] * 60 + 120)
else:
user = "an ISP proxy username from your plan"
print(f"{name:<9} {mode:<7} {user}")
products rotate customer-USERNAME-country-us
search sticky customer-USERNAME-country-us-sid-search-ttl-240
checkout sticky customer-USERNAME-country-us-sid-checkout-ttl-600
seller isp an ISP proxy username from your plan
The TTL is set to the expected length of the flow plus two minutes of headroom, so the session outlives the work but does not linger. Give every concurrent flow its own session ID: two workers sharing one ID share one IP, which defeats the purpose.
How the choice affects cost and success
Success rate. The most common rotation mistake is rotating inside a flow. The first request gets a cookie, the second arrives from another country’s ISP with that cookie, and the site treats it as suspicious. Sticky sessions for flows usually fix more failures than any other single change. The opposite mistake, sending thousands of independent requests through one sticky IP, makes that one address look like a bot. Our guide to sticky versus rotating residential proxies goes deeper into both failure modes.
Bandwidth. Residential traffic is billed by the gigabyte, and rotation itself does not change that. What changes it is retries: every failed flow is downloaded again. Matching the mode to the work cuts retries, and that is where the saving comes from. Within a sticky session, reusing the same connection and cookies can also avoid downloading the same assets twice.
Speed. Rotation per request means a new route for every request. If you also open a new connection each time, setup time adds up. Reusing connections matters more than the rotation mode; our test in your scraper is slow and it probably isn’t the proxy measured how much.
Signals that you chose wrong
| What you see | Likely cause | Change |
|---|---|---|
| Flows fail after the first step; cart or session lost | Rotating inside a flow | Sticky session per flow |
| One IP blocked after a burst of requests | Too much independent work on one sticky session | Rotate per request for independent pages |
| Sessions drop before the work finishes | TTL too short, or the flow is too long for a sticky session | Raise the TTL; move hours-long work to an ISP proxy |
| Two workers interfere with each other | Shared session ID | One session ID per concurrent flow |
| Logins challenged every day | A login on rotating or sticky IPs | Fixed ISP IP per account |
FAQ
Should I rotate my proxy IP on every request?
For independent requests, such as fetching unrelated product pages, yes. For anything that spans several requests with shared state, such as a search, a cart or a login, keep one IP for the whole flow with a sticky session.
How long should a sticky session last?
As long as the flow it serves, plus a little headroom. On Shifter the default is 120 seconds and you can set it with ttl-<seconds>. Flows longer than about 30 minutes, and logins you return to, belong on ISP proxies.
Does rotating IPs reduce blocks?
It spreads load so no single address looks busy, which helps for independent requests. Rotating inside a flow does the opposite: it makes a single visitor look like many, which sites often block.
Does rotation affect bandwidth cost?
Not directly, since residential traffic is billed by volume. Indirectly, the wrong mode causes failed flows and retries, which are downloaded again and add to the bill.
The bottom line
Rotate per request for independent work, hold a sticky session for each flow that belongs together, and use a fixed ISP IP for anything that lasts hours or involves a login you come back to. Decide by looking at your unit of work, not the proxy settings, give every concurrent flow its own session ID, and watch for the signals in the table above. Our explainer on how residential proxy rotation works covers the mechanics behind each mode.