Developers
Proxy setup cookbook
Short, working recipes for sending traffic through an authenticated HTTP or SOCKS5 proxy from the tools developers actually use. Every recipe reads the same five environment variables and checks the exit IP, so you can copy one, run it and see the result.
Each recipe links to a longer guide with tested versions, error handling and edge cases. For device and app setup instead of code, use the proxy setup tutorial by device.
Set the credentials once
Each Coronium line is one physical device with its own SIM, with HTTP and SOCKS5 access. The dashboard shows the host, the port for each protocol, and the username and password. Put them in environment variables so no recipe hard-codes a secret:
# Copy the values from your dashboard. Keep them out of source control.
export PROXY_HOST='your-line-host'
export PROXY_HTTP_PORT='http-port'
export PROXY_SOCKS_PORT='socks5-port'
export PROXY_USER='your-username'
export PROXY_PASS='your-password'Use HTTP for browsers and most HTTP clients. Use SOCKS5 with remote DNS (socks5h) when a tool supports it and you want DNS resolved on the proxy side.
curl
The fastest way to prove a line works before you touch any code.
HTTP proxy with a login
curl -sS --proxy "http://$PROXY_HOST:$PROXY_HTTP_PORT" \
--proxy-user "$PROXY_USER:$PROXY_PASS" \
https://api.ipify.orgSOCKS5, with DNS resolved on the proxy side (socks5h)
curl -sS --proxy "socks5h://$PROXY_HOST:$PROXY_SOCKS_PORT" \
--proxy-user "$PROXY_USER:$PROXY_PASS" \
https://api.ipify.org- --proxy-user keeps special characters in the password from breaking the URL.
- socks5h sends the hostname to the proxy, so DNS lookups do not leave from your own network.
Full walkthrough: curl proxy checks on Linux
Python requests
One proxies dictionary covers HTTP and HTTPS targets.
python -m pip install "requests[socks]"import os
from urllib.parse import quote
import requests
user = quote(os.environ["PROXY_USER"], safe="")
password = quote(os.environ["PROXY_PASS"], safe="")
proxy = f"http://{user}:{password}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_HTTP_PORT']}"
# SOCKS5 instead: f"socks5h://{user}:{password}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_SOCKS_PORT']}"
response = requests.get(
"https://api.ipify.org?format=json",
proxies={"http": proxy, "https": proxy},
timeout=20,
)
response.raise_for_status()
print(response.json()["ip"])- The "https" key is the proxy for HTTPS destinations; it still points at the http:// proxy URL, which tunnels with CONNECT.
- quote() encodes characters such as @ or : in the login.
Full walkthrough: Python Requests proxy authentication
Python httpx (sync and async)
httpx takes a single proxy= argument (the older proxies= argument was removed in 0.28).
python -m pip install "httpx[socks]"import asyncio
import os
import httpx
proxy = httpx.Proxy(
f"http://{os.environ['PROXY_HOST']}:{os.environ['PROXY_HTTP_PORT']}",
auth=(os.environ["PROXY_USER"], os.environ["PROXY_PASS"]),
)
# Synchronous
with httpx.Client(proxy=proxy, timeout=20) as client:
print(client.get("https://api.ipify.org?format=json").json()["ip"])
# Asynchronous
async def main():
async with httpx.AsyncClient(proxy=proxy, timeout=20) as client:
response = await client.get("https://api.ipify.org?format=json")
print(response.json()["ip"])
asyncio.run(main())- trust_env defaults to True, so HTTP_PROXY variables in the environment can still apply to clients created without proxy=.
Full walkthrough: HTTPX async proxy setup
Node.js fetch (undici)
Node's built-in fetch accepts an undici dispatcher; ProxyAgent sends the login as a Proxy-Authorization header.
npm install undici// check.mjs
import { fetch, ProxyAgent } from 'undici';
const { PROXY_HOST, PROXY_HTTP_PORT, PROXY_USER, PROXY_PASS } = process.env;
const token = Buffer.from(`${PROXY_USER}:${PROXY_PASS}`).toString('base64');
const dispatcher = new ProxyAgent({
uri: `http://${PROXY_HOST}:${PROXY_HTTP_PORT}`,
token: `Basic ${token}`,
});
try {
const response = await fetch('https://api.ipify.org?format=json', {
dispatcher,
signal: AbortSignal.timeout(20_000),
});
console.log((await response.json()).ip);
} finally {
await dispatcher.close();
}- Import fetch from undici as well, so the dispatcher and fetch come from the same version.
Full walkthrough: Node.js fetch proxy setup
Playwright
Playwright takes the proxy and its login at launch, or per browser context.
npm install playwright
npx playwright install chromium// playwright-check.mjs
import { chromium } from 'playwright';
const { PROXY_HOST, PROXY_HTTP_PORT, PROXY_USER, PROXY_PASS } = process.env;
const browser = await chromium.launch({
proxy: {
server: `http://${PROXY_HOST}:${PROXY_HTTP_PORT}`,
username: PROXY_USER,
password: PROXY_PASS,
},
});
try {
const page = await browser.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.locator('body').innerText());
} finally {
await browser.close();
}- Python uses the same keys: p.chromium.launch(proxy={"server": ..., "username": ..., "password": ...}).
- Chromium does not support SOCKS5 with a login, so browsers use the HTTP port.
Full walkthrough: Playwright proxy rotation and sessions
Puppeteer
The proxy goes in a Chrome flag; the login goes through page.authenticate().
// puppeteer-check.mjs (npm install puppeteer)
import puppeteer from 'puppeteer';
const { PROXY_HOST, PROXY_HTTP_PORT, PROXY_USER, PROXY_PASS } = process.env;
const browser = await puppeteer.launch({
args: [`--proxy-server=http://${PROXY_HOST}:${PROXY_HTTP_PORT}`],
});
try {
const page = await browser.newPage();
await page.authenticate({ username: PROXY_USER, password: PROXY_PASS });
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
} finally {
await browser.close();
}- Chrome ignores user:password inside --proxy-server; authenticate() answers the 407 challenge instead.
Full walkthrough: Puppeteer proxy guide
Selenium (Python, Chrome)
Chrome needs the proxy as a flag and a DevTools handler that answers the proxy's login challenge.
python -m pip install seleniumimport os
from selenium import webdriver
from selenium.webdriver.common.by import By
options = webdriver.ChromeOptions()
options.add_argument(f"--proxy-server=http://{os.environ['PROXY_HOST']}:{os.environ['PROXY_HTTP_PORT']}")
driver = webdriver.Chrome(options=options)
try:
devtools, connection = driver.start_devtools()
def on_auth(event):
if event.auth_challenge.source == "Proxy":
answer = devtools.fetch.AuthChallengeResponse(
response="ProvideCredentials",
username=os.environ["PROXY_USER"],
password=os.environ["PROXY_PASS"],
)
else:
answer = devtools.fetch.AuthChallengeResponse(response="CancelAuth")
connection.execute(devtools.fetch.continue_with_auth(event.request_id, answer))
connection.add_callback(devtools.fetch.AuthRequired, on_auth)
connection.add_callback(
devtools.fetch.RequestPaused,
lambda event: connection.execute(devtools.fetch.continue_request(event.request_id)),
)
connection.execute(devtools.fetch.enable(handle_auth_requests=True))
driver.get("https://api.ipify.org?format=json")
print(driver.find_element(By.TAG_NAME, "body").text)
finally:
driver.quit()- Condensed from the tested example in the full guide, which also checks the challenger's host and answers each challenge once.
- This uses Chrome DevTools, so it is Chrome-specific and follows the installed Chrome version.
Full walkthrough: Selenium proxy authentication
Scrapy
Set the proxy per request in meta; Scrapy's HttpProxyMiddleware turns the login in the URL into a Proxy-Authorization header.
# ip_spider.py (python -m pip install scrapy)
import os
from urllib.parse import quote
import scrapy
PROXY = "http://{}:{}@{}:{}".format(
quote(os.environ["PROXY_USER"], safe=""),
quote(os.environ["PROXY_PASS"], safe=""),
os.environ["PROXY_HOST"],
os.environ["PROXY_HTTP_PORT"],
)
class IpSpider(scrapy.Spider):
name = "ip"
def start_requests(self):
yield scrapy.Request("https://api.ipify.org?format=json", meta={"proxy": PROXY})
def parse(self, response):
yield {"ip": response.json()["ip"]}scrapy runspider ip_spider.py -O ip.json- For many requests, set meta["proxy"] in a downloader middleware instead of in every Request.
Full walkthrough: Scrapy proxy configuration
Go (net/http)
http.ProxyURL with credentials in the URL; Go sends them on the CONNECT request.
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
func main() {
proxyURL := &url.URL{
Scheme: "http",
User: url.UserPassword(os.Getenv("PROXY_USER"), os.Getenv("PROXY_PASS")),
Host: os.Getenv("PROXY_HOST") + ":" + os.Getenv("PROXY_HTTP_PORT"),
}
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
Timeout: 20 * time.Second,
}
resp, err := client.Get("https://api.ipify.org")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}- url.UserPassword encodes special characters for you. For a headless browser in Go, see chromedp.
Full walkthrough: ChromeDP automation guide
Rotate the IP from code
A dedicated line changes its IP when you ask: from the dashboard, through the API, with a rotation link or on a timer. From a script, the rotation link is the simplest. Rules that matter in code:
- At least 60 seconds between rotations.
- Some device types rotate on demand only.
- A rotation link rotates on every GET request, including link previews.
# The rotation link from your dashboard. Every GET rotates the line.
curl -sS "https://api.coronium.io/api/v3/modems/rotate-modem-by-token/$ROTATION_TOKEN"Then wait for the new address before sending work through the line:
import os
import time
from urllib.parse import quote
import requests
proxy = "http://{}:{}@{}:{}".format(
quote(os.environ["PROXY_USER"], safe=""), quote(os.environ["PROXY_PASS"], safe=""),
os.environ["PROXY_HOST"], os.environ["PROXY_HTTP_PORT"],
)
proxies = {"http": proxy, "https": proxy}
rotate_url = "https://api.coronium.io/api/v3/modems/rotate-modem-by-token/" + os.environ["ROTATION_TOKEN"]
def exit_ip():
return requests.get("https://api.ipify.org", proxies=proxies, timeout=20).text
before = exit_ip()
requests.get(rotate_url, timeout=90)
for _ in range(12):
time.sleep(10)
try:
now = exit_ip()
except requests.RequestException:
continue # the line reconnects while it rotates
if now != before:
print("new IP:", now)
break
else:
print("IP unchanged; check the line in the dashboard")The full API is documented at the API reference. AI agents can do the same through the Coronium MCP server; see also mobile proxies for AI agents.
Errors and fixes
| Error | Meaning | Fix |
|---|---|---|
| 407 Proxy Authentication Required | The proxy did not receive a valid login. | Check the username and password, URL-encode special characters, and in browsers answer the challenge (authenticate(), the Playwright proxy option or the Selenium handler). |
| Connection refused or timed out | The client cannot reach the proxy host and port. | Copy host and port again, check the line status in the dashboard, and make sure HTTP code uses the HTTP port and SOCKS code the SOCKS5 port. |
| ERR_TUNNEL_CONNECTION_FAILED | Chrome reached the proxy but the CONNECT tunnel failed. | Usually a SOCKS5 port used as HTTP, or a rejected login. Test the same values with curl first. |
| SOCKS5 login ignored in a browser | Chromium-based browsers do not support SOCKS5 with a username and password. | Use the HTTP port for browsers, Playwright, Puppeteer and Selenium. |
| Same IP after rotation | The check ran before the line reconnected, or two rotations were less than 60 seconds apart. | Wait and poll the exit IP as in the rotation recipe; space rotations at least 60 seconds apart. |
Every proxy status code is explained in proxy error codes. Lines, countries and prices are on mobile proxies.