New v5.5.1: balance-first checkout — add funds once, buy instantly. Read more
Integrations

Puppeteer & Playwright

Run headless Chromium, Firefox and WebKit through authenticated mobile, residential and static ISP proxies with Puppeteer and Playwright, per browser or per context.

On this page

Chromium does not accept credentials in the --proxy-server flag. Both tools solve this differently: Puppeteer answers the proxy's authentication challenge with page.authenticate(), Playwright takes username and password in its proxy option.

Puppeteer#

Node.js
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://fr.mobile.gw.proxuno.com:10421"],
});
const page = await browser.newPage();
await page.authenticate({ username: "m_k3v9q2xa", password: "Hq7Lr2Nw8Zt4Bc6D" });

await page.goto("https://api.ipify.org?format=json", { waitUntil: "domcontentloaded", timeout: 60_000 });
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
  • Call authenticate() on every new page before navigating.
  • One --proxy-server per browser process. For several proxies, launch several browsers, or use one incognito context per proxy (Puppeteer ≥ 22): browser.createBrowserContext({ proxyServer: "http://host:port" }), then authenticate() on each page of that context.
  • SOCKS5 with authentication is not supported by Chromium. Use the HTTP port, or IP whitelist with --proxy-server=socks5://host:port.

Playwright#

Node.js
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server: "http://fr.mobile.gw.proxuno.com:10421",
    username: "m_k3v9q2xa",
    password: "Hq7Lr2Nw8Zt4Bc6D",
  },
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org?format=json");
console.log(await page.textContent("body"));
await browser.close();

One proxy per context#

Playwright can give each browser context its own proxy — ideal for running several identities in one browser process:

Node.js
const browser = await chromium.launch();

const contexts = await Promise.all(
  ["fr", "de", "it"].map((cc) =>
    browser.newContext({
      proxy: {
        server: "http://resi.gw.proxuno.com:7000",
        username: `px4k2m9q7a-country-${cc}-session-${Math.random().toString(36).slice(2, 10)}-ttl-30`,
        password: process.env.RESI_PASSWORD,
      },
      locale: { fr: "fr-FR", de: "de-DE", it: "it-IT" }[cc],
      timezoneId: { fr: "Europe/Paris", de: "Europe/Berlin", it: "Europe/Rome" }[cc],
    })
  )
);

The sticky session keeps each context on one residential IP for 30 minutes. Match locale and timezoneId to the exit country.

Python#

Python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.firefox.launch(proxy={
        "server": "http://203.0.113.47:8000",
        "username": "s_r7d2k9mw",
        "password": "Lp4Xv8Qc2Nh6Jt3B",
    })
    page = browser.new_page()
    page.goto("https://api.ipify.org")
    print(page.inner_text("body"))
    browser.close()

Rotating between page loads#

With a mobile proxy, rotate between tasks rather than mid-page: open connections are dropped when the modem reconnects.

Node.js
async function rotate(link) {
  const res = await fetch(link);                 // the proxy's rotation link
  if (res.status === 429) {
    await new Promise((r) => setTimeout(r, Number(res.headers.get("retry-after")) * 1000));
    return rotate(link);
  }
  return (await res.json()).ip;
}

for (const url of urls) {
  const context = await browser.newContext();    // fresh cookies per task
  const page = await context.newPage();
  await page.goto(url, { timeout: 60_000 });
  // … work …
  await context.close();
  await rotate(process.env.ROTATION_LINK);
}

Performance tips#

  • Block heavy resources you do not need — images, media, fonts — with page.route(); on mobile proxies this often halves page-load time.
  • Use waitUntil: "domcontentloaded" instead of "networkidle" on sites with long-polling or analytics beacons.
  • Raise navigation timeouts to 60 s on mobile proxies at peak hours.
  • Keep browser and context counts in line with Limits & best practices: 5–10 concurrent pages per mobile proxy is a sensible ceiling.

Troubleshooting#

Error Cause
net::ERR_INVALID_AUTH_CREDENTIALS Wrong credentials, expired proxy, or residential flags with a typo.
net::ERR_TUNNEL_CONNECTION_FAILED The proxy could not reach the target (502), or the modem was rotating.
net::ERR_NO_SUPPORTED_PROXIES A socks5:// server with credentials in Chromium — use HTTP.
Puppeteer shows an auth dialog then hangs page.authenticate() was not called before goto().

Something unclear, outdated or wrong on this page? Tell us — documentation issues are fixed in the next release at the latest.

Report an issue with this page