> For the complete documentation index, see [llms.txt](https://docs.helodata.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.helodata.com/products/overview-2/rotation.md).

# Client-side rotation

ISP IPs are permanently stable — there's no `session` parameter and no gateway rotation. If you want different IPs across requests, your client picks them. This page documents the common patterns.

## Round-robin

The simplest pattern: cycle through the list in order.

```python
import itertools, requests

with open("isp.txt") as f:
    lines = [l.strip() for l in f if l.strip()]

cycle = itertools.cycle(lines)

def get(url):
    ip, port, user, password = next(cycle).split(":", 3)
    proxy = f"http://{user}:{password}@{ip}:{port}"
    return requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
```

Pros: trivial. Cons: failed IPs keep getting picked.

## Round-robin + health tracking

Track failures and skip dead IPs for a cool-down window:

```python
import time, itertools, requests
from collections import defaultdict

class IPPool:
    def __init__(self, lines, cooldown=300):
        self.lines = lines
        self.cooldown = cooldown
        self.bad_until = defaultdict(float)
        self._iter = itertools.cycle(lines)

    def next_alive(self):
        for _ in range(len(self.lines)):
            line = next(self._iter)
            if self.bad_until[line] < time.time():
                return line
        raise RuntimeError("All IPs are in cool-down")

    def mark_bad(self, line):
        self.bad_until[line] = time.time() + self.cooldown

pool = IPPool(open("isp.txt").read().splitlines())

def get(url):
    line = pool.next_alive()
    ip, port, user, password = line.split(":", 3)
    proxy = f"http://{user}:{password}@{ip}:{port}"
    try:
        r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
        if r.status_code >= 500:
            pool.mark_bad(line)
        return r
    except requests.RequestException:
        pool.mark_bad(line)
        raise
```

## Sticky-per-task

If your unit of work needs a stable IP (login flow, multi-step purchase), pin one IP per task:

```python
import hashlib

def ip_for_task(task_id, lines):
    h = int(hashlib.sha256(task_id.encode()).hexdigest(), 16)
    return lines[h % len(lines)]
```

Consistent hashing means restarts and re-runs of the same `task_id` hit the same IP.

## Concurrency caps

Each ISP IP can handle high throughput (1 Gbps+), but most target sites will rate-limit you per IP. A reasonable starting point: **max 5 concurrent requests per IP** to a single target site. Sharded across the batch, this scales linearly.

## Coordinating across processes

If multiple workers share one batch, use a shared store (Redis, etcd) to coordinate rotation and health tracking. A simple Redis-backed pool:

```python
import redis, time, json

r = redis.Redis()

def pick(batch_key):
    lines = json.loads(r.get(f"isp:{batch_key}:lines"))
    idx = r.incr(f"isp:{batch_key}:counter") % len(lines)
    return lines[idx]
```

For health tracking, use a sorted set keyed on `cooldown_until` and skip entries above `now()`.

## When **not** to rotate

If your use case is account-bound (logged-in scraping, posting, monitoring a logged-in dashboard), **do not rotate**. Pin one IP per account. The whole reason to use ISP is IP stability — rotating defeats it.

## Tools that handle rotation for you

* **Antidetect browsers** (AdsPower, Multilogin, GoLogin) — assign a different ISP IP per browser profile
* **Proxifier** — round-robin across imported list at the OS level
* **Scrapy + scrapy-rotating-proxies** — middleware reads a list and rotates
* **Apify SDK proxy configuration** — handles rotation automatically when given a list

For most production stacks, your scraper framework's built-in proxy rotator is enough. The code above is for when you need custom logic.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.helodata.com/products/overview-2/rotation.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
