Coronium Mobile Proxies

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:

bash
# 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

bash
curl -sS --proxy "http://$PROXY_HOST:$PROXY_HTTP_PORT" \
  --proxy-user "$PROXY_USER:$PROXY_PASS" \
  https://api.ipify.org

SOCKS5, with DNS resolved on the proxy side (socks5h)

bash
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.

bash
python -m pip install "requests[socks]"
python
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).

bash
python -m pip install "httpx[socks]"
python
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.

bash
npm install undici
javascript
// 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.

bash
npm install playwright
npx playwright install chromium
javascript
// 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().

javascript
// 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.

bash
python -m pip install selenium
python
import 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.

python
# 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"]}
bash
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.

go
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.
bash
# 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:

python
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

The errors these recipes most often hit.
ErrorMeaningFix
407 Proxy Authentication RequiredThe 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 outThe 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_FAILEDChrome 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 browserChromium-based browsers do not support SOCKS5 with a username and password.Use the HTTP port for browsers, Playwright, Puppeteer and Selenium.
Same IP after rotationThe 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.