Rotation is the default behaviour of most mobile proxy pools, and for a plain crawler it is exactly what you want: a fresh exit IP per request, a clean rate-limit budget everywhere you go, and no single address accumulating a bad reputation. But an AI agent is not a plain crawler. An agent run is a stateful chain: log in, list the accounts, open the one that matters, download the report, post the update. Every step depends on state built in the previous steps, and a surprising amount of that state is bound to the IP address the server saw.
So the first design decision for any agent that browses is not "which proxy" but "which IP identity does each step use". Get it wrong in the obvious direction (rotate everything) and your agent gets logged out mid-run, captcha-walled, or risk-scored into step-up authentication. Get it wrong in the other direction (pin everything forever) and you lose the diversity that made mobile proxies worth paying for.
This article walks through why rotation breaks agent workflows, the decision rule that fixes it, and a per-run identity pattern you can drop into an existing agent. Every command and output below was captured live through a UK 4G/5G mobile pool (Simply Proxies, SOCKS5 and HTTPS endpoints), so what you see is what the infrastructure actually does, not what a diagram says it should do.
Why rotation breaks agent workflows
Three failure modes show up in practice, in rough order of how quickly they ruin your day.
1. The server terminates your session. Many web applications deliberately bind login sessions to properties of the client: the IP address, the User-Agent, sometimes a client certificate. The OWASP Session Management Cheat Sheet recommends exactly this and spells out the consequence: if "the user location or User-Agent changes in the middle of a session", that is "a very good indicator of session manipulation and hijacking attempts", and the recommended response is to alert and/or terminate the session. That guidance exists to stop cookie theft, and it works. It also stops your agent: rotate the exit IP between the login request and the next request, and from the server's point of view the session cookie was just replayed from a different network. The safest servers kill the session on the spot. Your agent's step 3 now fails with a login redirect, and the retry logic re-authenticates from yet another IP, which looks even worse.
2. You trigger captcha or step-up authentication. Even when the session survives, a sudden IP change raises a risk score. Risk engines read a mid-session IP change as possible account sharing or cookie theft, and the standard soft response is a challenge page or a re-verification email. Humans rarely change countries between two clicks; agents that rotate do it constantly.
3. Your fingerprint becomes incoherent. A rotating agent usually presents a stable TLS and HTTP fingerprint (same Python client, same cipher list, same header order) from a changing sequence of IP addresses. No human produces that pattern. Anti-bot systems that correlate transport fingerprint with IP behaviour notice it quickly, which is how you end up with working credentials and 403s anyway.
None of this means rotation is wrong. It means rotation belongs where it works (stateless fetching) and pinning belongs where it is required (stateful sequences).
The decision rule: rotate reads, pin runs
Classify each step of the agent workflow by whether the server accumulates state about you:
| Workflow step | Server keeps state? | IP strategy |
|---|---|---|
| Fetch a public page / search results | No | Rotate (maximum diversity) |
| Crawl many pages anonymously | No | Rotate |
| Log in | Yes (session creation) | Pin, and keep the pin |
| Act inside the account (post, order, download) | Yes | Pin, same identity as login |
| Localised results for one city or one carrier | Yes (geo) | Pin, or pin the network |
| Poll one endpoint on a schedule | Session per run | New pin per run |
The compact version: one agent run, one identity. Derive a session identifier at the start of the run, use it for every stateful request in that run, and throw it away when the run ends. The next run gets a fresh identifier, which means a fresh exit IP, which means runs do not accumulate reputation with each other. You get rotation between runs and stability within them.
See it live: rotation vs pinning
Plain credentials rotate. Three requests, three different UK mobile exits (real output, SOCKS5 endpoint):
curl -x socks5h://USER:PASS@proxy.simplyproxies.com:6890 \
"http://ip-api.com/json/?fields=query,country,as,mobile"
{"country":"United Kingdom","as":"AS35228 Telefonica UK Limited","mobile":true,"query":"82.132.217.169"}
{"country":"United Kingdom","as":"AS2856 British Telecommunications Limited","mobile":true,"query":"31.94.32.205"}
{"country":"United Kingdom","as":"AS2856 British Telecommunications Limited","mobile":true,"query":"31.94.34.133"}
Two different carriers, three different addresses, in under ten seconds. Perfect for anonymous crawling, fatal for a login flow.
Now the same command with a session suffix. Appending -session-<id>-<minutes> to the username pins consecutive connections to one device for the window you choose (5 minutes to 12 hours, 2 hours by default). Here the id is e47e604f and the window is 15 minutes:
curl -x socks5h://USER-session-e47e604f-15:PASS@proxy.simplyproxies.com:6890 \
"http://ip-api.com/json/?fields=query,country,as,mobile"
{"country":"United Kingdom","as":"AS2856 British Telecommunications Limited","mobile":true,"query":"31.94.37.115"}
{"country":"United Kingdom","as":"AS2856 British Telecommunications Limited","mobile":true,"query":"31.94.37.115"}
{"country":"United Kingdom","as":"AS2856 British Telecommunications Limited","mobile":true,"query":"31.94.37.115"}
One exit, held. The server on the other end sees one stable visitor for the whole window. Billing does not change: stickiness is free, you still pay per GB transferred, not per IP.
One run, one identity: the pattern
Here is the whole idea as a Python function. The agent derives a random run id, builds the proxy URL with the session suffix, and reuses it for every step of the run. The next invocation derives a new id and therefore gets a different exit. This snippet is the exact code that produced the output below it (credentials read from the environment, never hardcoded):
import os
import time
import urllib.parse
import uuid
import requests
PROXY_HOST = "proxy.simplyproxies.com:6889"
CHECK_URL = "https://ipwho.is/?fields=ip,country,connection"
def proxy_for_run(run_id: str) -> dict:
user = f"{os.environ['PROXY_USER']}-session-{run_id}-15"
auth = urllib.parse.quote(user) + ":" + os.environ["PROXY_PASS"]
proxy = f"https://{auth}@{PROXY_HOST}"
return {"http": proxy, "https": proxy}
def agent_run(task_name: str, steps: tuple) -> None:
run_id = str(uuid.uuid4())[:8]
proxies = proxy_for_run(run_id)
print(f"--- agent run {task_name!r} pinned as session {run_id}")
for step in steps:
for attempt in range(3):
try:
r = requests.get(CHECK_URL, proxies=proxies, timeout=20)
r.raise_for_status()
data = r.json()
net = data["connection"]["org"] or data["connection"]["isp"]
print(f" {step}: {data['ip']} ({data['country']}, {net})")
break
except requests.RequestException:
if attempt == 2:
raise
time.sleep(2 ** attempt)
Two invocations back to back:
--- agent run 'morning-price-watch' pinned as session b98a2415
fetch target list: 82.132.216.209 (United Kingdom, 02 Online uk)
fetch page 1: 82.132.216.209 (United Kingdom, 02 Online uk)
fetch page 2: 82.132.216.209 (United Kingdom, 02 Online uk)
--- agent run 'morning-price-watch' pinned as session 56f71e5c
fetch target list: 31.94.37.115 (United Kingdom, EE Limited)
fetch page 1: 31.94.37.115 (United Kingdom, EE Limited)
Read the two runs together: within each run the exit never moves, and between runs the identity changes completely (a different device, and here even a different carrier network). That is the rotation/stability split exactly where you want it. The retry ladder with exponential backoff matters too: a mobile exit can occasionally fail to connect, and the correct response is a retry on the same identity, not an immediate re-rotation.
One practical detail: set the sticky window at or above the expected duration of the whole run (the -15 above means 15 minutes). If a lease outlives the run, nothing breaks; the window simply expires and the device returns to the general rotation.
Pinning the network too
Some workflows care about which UK network the exit belongs to (carrier-specific results, avoiding one ASN's reputation, matching a previous account's network). The grammar supports that: a closed-set token (-ee, -o2, -vodafone, -three) goes directly after the base username and before any session suffix:
USER-ee-session-<id>-<minutes>
The network token narrows the pool to devices whose active SIM maps onto that host network, and the session suffix pins within it. Real output from the HTTPS endpoint, three requests with -ee-session-499f69da-15:
31.94.32.101 United Kingdom EE Limited
31.94.32.101 United Kingdom EE Limited
31.94.32.101 United Kingdom EE Limited
If no device on the requested network is available, the connection is refused rather than silently served from another network, so an agent's network assumption can never be quietly violated. Two ports, two behaviours worth knowing: on SOCKS5 the custom minutes window applies as described; on the HTTPS port stickiness uses a 12-hour table window.
Treat pinning as best-effort
Pinning is best-effort rather than guaranteed (the docs say so explicitly). For an agent, that means one habit: check the exit IP at the start of each stateful run, and if it changes unexpectedly mid-run, treat it as a session-reset event and log in again deliberately rather than letting the target site do it for you with worse error messages.
A resilient agent loop
Putting it together, the pattern that survives production:
- Derive a run id at run start (
uuid4truncated is plenty) and use one sticky suffix for every stateful request in the run. - Verify the exit at run start with an IP check call, and record it. If the exit changes mid-run on a stateful flow, treat it as a session-reset event: log in again on the current identity.
- Retry with backoff on transport errors, on the same identity. Re-rotate only after repeated failures, and do it by starting a new run id deliberately.
- Keep stateless bulk fetches on plain rotating credentials, in the same process if you like; the suffix only affects connections that carry it.
- Size the sticky window to the run, not to the hour: short windows return the device to rotation sooner, which keeps the shared pool healthy.
- Alert on step-up challenges, not just on HTTP errors. A captcha page returned with status 200 is a failure mode your retry ladder should recognise.
Summary
- Rotation is for stateless reads; stateful chains (login, act, download) need one stable identity for the whole run.
- Mid-session IP change is, by widely followed security guidance, treated as session hijacking: expect terminations, captchas, and risk scoring if you rotate through a logged-in flow.
- The fix costs one string: append
-session-<run-id>-<minutes>to the username, derive a fresh id per run, and you get stability inside runs and diversity between them. - Network tokens (
-ee,-o2,-vodafone,-three) compose with sessions and fail closed: you never silently exit from the wrong network. - Pinning is best-effort: design the agent loop to detect identity changes and re-authenticate deliberately.
If you are wiring agents to UK mobile IPs, the practical agent integration guide covers the fetch/retry plumbing, and the 500 MB free trial is enough to reproduce every demo in this article with your own agent before spending anything.
Last verified 2026-10-16
Try It With Real UK Mobile IPs
Run the examples in this guide through genuine UK 4G/5G exits. 500 MB free, no card required.
Start Free Trial