Rotate a proxy IP automatically with an API — curl, Python and Node.js examples
In this article
Opening a rotation link in a browser is easy, but once you run tools, scrapers or a series of browser profiles, rotation should be automatic: call the link, wait for the proxy to reconnect, confirm the IP changed, and only then carry on. This guide has ready-to-run code for curl, PowerShell, Python and Node.js, and shows how to use a password-protected proxy with Playwright and Puppeteer.
How it works: one link, one GET request
With 65Proxy's 4G proxies, every proxy has its own rotation link under My proxies. The "rotation API" is that link: send a GET request to it (directly, not through the proxy), and the 4G device reconnects to the mobile network and gets a new IP.
All the code below follows the same rhythm:
- Get the current IP through the proxy (using
https://api.ipify.org, which returns nothing but the IP address). - Call the rotation link.
- Wait and re-check every few seconds until the IP differs from the old one or time runs out.
Step 3 matters more than it looks: the device needs a few seconds to reconnect, and requests through the proxy may fail meanwhile. Don't rely on whatever the rotation link returns — check that the IP actually changed.
In the examples, USER, PASS, HOST and PORT are your proxy details, and the rotation link is read from the ROTATE_URL environment variable. Don't hard-code the link: anyone who has it can rotate your IP.
Rotating with curl
On macOS, Linux or Git Bash on Windows:
export PROXY="http://USER:PASS@HOST:PORT"
export ROTATE_URL="YOUR_ROTATION_LINK"
old=$(curl -s -m 15 -x "$PROXY" https://api.ipify.org)
curl -s -m 60 "$ROTATE_URL" > /dev/null
# Re-check every 3 seconds, for about a minute at most
for i in $(seq 1 20); do
sleep 3
new=$(curl -s -m 10 -x "$PROXY" https://api.ipify.org)
if [ -n "$new" ] && [ "$new" != "$old" ]; then
echo "$old -> $new"
break
fi
doneOn Windows PowerShell
In Windows PowerShell, curl is an alias for a different command (Invoke-WebRequest), so type curl.exe — the real curl that ships with Windows 10 and 11:
$proxy = "http://USER:PASS@HOST:PORT"
$rotate = $env:ROTATE_URL
$old = curl.exe -s -m 15 -x $proxy https://api.ipify.org
curl.exe -s -m 60 $rotate | Out-Null
foreach ($i in 1..20) {
Start-Sleep -Seconds 3
$new = curl.exe -s -m 10 -x $proxy https://api.ipify.org
if ($new -and $new -ne $old) { "$old -> $new"; break }
}Python with requests
Install the library with pip install requests, set the PROXY and ROTATE_URL environment variables, and run:
import os
import time
import requests
PROXY = os.environ["PROXY"] # http://USER:PASS@HOST:PORT
ROTATE_URL = os.environ["ROTATE_URL"] # the rotation link from My proxies
PROXIES = {"http": PROXY, "https": PROXY}
def current_ip() -> str:
r = requests.get("https://api.ipify.org", proxies=PROXIES, timeout=15)
r.raise_for_status()
return r.text.strip()
def rotate(wait: float = 60) -> str:
"""Call the rotation link, then wait until the proxy has a new IP."""
old = current_ip()
requests.get(ROTATE_URL, timeout=60).raise_for_status()
deadline = time.monotonic() + wait
while time.monotonic() < deadline:
time.sleep(3)
try:
new = current_ip()
except requests.RequestException:
continue # the proxy is reconnecting
if new != old:
return new
raise TimeoutError("IP did not change within %d seconds" % wait)
if __name__ == "__main__":
print("New IP:", rotate())To use SOCKS5 instead of HTTP: also run pip install "requests[socks]" and set PROXY to socks5h://USER:PASS@HOST:PORT. The h makes the proxy resolve DNS, which avoids DNS leaks — explained in SOCKS5 vs HTTP proxies.
Setting environment variables in Windows PowerShell: $env:PROXY = "http://USER:PASS@HOST:PORT"; on macOS and Linux: export PROXY="http://USER:PASS@HOST:PORT".
Node.js with undici
Node.js's built-in fetch doesn't go through a proxy on its own. The undici library (from the Node.js project itself) has a ProxyAgent that accepts a proxy with the username and password in its URL. Install it with npm install undici, save the file as rotate.mjs and run node rotate.mjs:
import { fetch, ProxyAgent } from "undici";
const proxy = new ProxyAgent(process.env.PROXY); // http://USER:PASS@HOST:PORT
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function currentIp() {
const res = await fetch("https://api.ipify.org", { dispatcher: proxy, signal: AbortSignal.timeout(15000) });
return (await res.text()).trim();
}
async function rotate(waitMs = 60000) {
const old = await currentIp();
const res = await fetch(process.env.ROTATE_URL, { signal: AbortSignal.timeout(60000) });
if (!res.ok) throw new Error("Rotation link answered " + res.status);
for (const end = Date.now() + waitMs; Date.now() < end; ) {
await sleep(3000);
try {
const ip = await currentIp();
if (ip !== old) return ip;
} catch {
// the proxy is reconnecting
}
}
throw new Error("The IP did not change in time");
}
console.log("New IP:", await rotate());Playwright and Puppeteer
When you drive a browser from code, use an HTTP proxy: Chromium doesn't support SOCKS5 with a password.
Playwright (Python) takes the proxy, username and password when the browser launches (install with pip install playwright, then playwright install chromium):
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(proxy={
"server": "http://HOST:PORT",
"username": "USER",
"password": "PASS",
})
page = browser.new_page()
page.goto("https://api.ipify.org")
print(page.inner_text("body"))
browser.close()Puppeteer (Node.js) takes the proxy address as a launch argument and the username and password through page.authenticate:
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({ args: ["--proxy-server=http://HOST:PORT"] });
const page = await browser.newPage();
await page.authenticate({ username: "USER", password: "PASS" });
await page.goto("https://api.ipify.org");
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();To give each browser session its own IP: close the browser, rotate, then launch again. The browser keeps its connections to the proxy open, so rotating while it's running can break pages that are loading.
Tips for automation
- One proxy is one IP at any moment. Every parallel worker on the same proxy shares the IP and gets cut off together when it rotates. Rotate only when no requests are in flight; for several IPs at once, use several proxies.
- Always set timeouts and retries. Mobile networks have slow moments; one request hanging forever stalls the whole script.
- Log the old and new IP on every rotation — invaluable when debugging.
- Don't rotate constantly for no reason. Each rotation is a disconnect; rotate when the job calls for it, such as switching accounts or when a site starts limiting you.
- Respect the sites you visit: read their terms and keep your request rate reasonable.
Not sure how rotation works under the hood? Read rotating proxies explained. Script failing to connect? See common proxy errors.