New Residential Premium, our top-tier pool for the toughest sites
JS

Scraping & code · Setup guide

How to use a proxy in Node.js

Node’s fetch doesn’t take a proxy URL directly. Give it an undici ProxyAgent as the dispatcher, and your requests go through ZenProxy with your username and password.

Broken on our side? Fixed in 48h or refunded.

Quick setup

  1. 1Install undici.
  2. 2Create new ProxyAgent("http://USERNAME:PASSWORD@HOST:PORT").
  3. 3Pass it as dispatcher to fetch, or set it globally.
  4. 4Create a new agent per request to rotate, one per sticky line to keep an IP.

Your proxy details

After you buy a plan, open My proxies. Choose the Proxy Type (Rotating or Sticky), the Protocol (HTTP or SOCKS5) and a Format, then copy a line. The generator offers three formats:

HOST:PORT:USERNAME:PASSWORD

Paste into apps and anti-detect browsers that split the fields or auto-fill them.

USERNAME:PASSWORD@HOST:PORT

Tools that want credentials before the address.

http://USERNAME:PASSWORD@HOST:PORT

Code and command-line tools. This is the proxy URL used in the examples below.

In the examples on this page, replace USERNAME, PASSWORD, HOST and PORT with the values from your line. SOCKS5_PORT is the port the generator shows when you pick SOCKS5.

Residential lines are either rotating (a new IP on new connections) or sticky (one IP for the time you choose). Datacenter and ISP plans give you a list of dedicated IPs, one per line as IP:PORT:USERNAME:PASSWORD, and each line is one fixed IP.

To test a line, open https://ipinfo.io/json through it: it shows the exit IP and country. For HTTP lines you can also use the free proxy checker.

fetch through an authenticated proxy

Install
npm install undici
proxy.mjs
import { fetch, ProxyAgent } from 'undici';

const dispatcher = new ProxyAgent('http://USERNAME:PASSWORD@HOST:PORT');

const res = await fetch('https://ipinfo.io/json', { dispatcher });
console.log(await res.json());

undici reads the username and password from the URL and sends them as Proxy-Authorization. Importing fetch from undici as well keeps the agent and fetch on the same version.

The example uses top-level await, so save it as an .mjs file or set "type": "module" in package.json.

Send every request through the proxy

setGlobalDispatcher makes the agent the default for undici’s fetch and request, so you don’t pass it on each call.

JavaScript
import { fetch, setGlobalDispatcher, ProxyAgent } from 'undici';

setGlobalDispatcher(new ProxyAgent('http://USERNAME:PASSWORD@HOST:PORT'));

const res = await fetch('https://ipinfo.io/json');
console.log(await res.json());

On Node.js 22.21+ and 24+, the built-in fetch can also follow the standard environment variables when you enable it: run NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://USERNAME:PASSWORD@HOST:PORT node app.mjs.

Rotating and sticky IPs

Rotation happens per connection. A rotating line gets a new IP when your client opens a new connection; requests sent through the same open HTTPS tunnel leave from the same IP. A ProxyAgent keeps connections open, so requests through one agent can share an IP. Create a new agent when you want a fresh IP, and one agent per sticky line when you want to keep it.

To make sticky lines, open the generator, set Proxy Type to Sticky, choose a Sticky Duration if your plan offers one, set how many lines you want in Sticky Amount, then copy or download them. Each line keeps its IP for the chosen time. The session part of a sticky username differs by plan, so always generate sticky lines instead of editing usernames by hand.

A new IP per request
import { fetch, ProxyAgent } from 'undici';

const PROXY = 'http://USERNAME:PASSWORD@HOST:PORT'; // rotating line

for (let i = 0; i < 3; i++) {
  const dispatcher = new ProxyAgent(PROXY); // new agent, new connection, new IP
  const res = await fetch('https://ipinfo.io/json', { dispatcher });
  console.log((await res.json()).ip);
  await dispatcher.close();
}

Target a country

On residential plans, add -country- and a two-letter country code to the end of the username, for example USERNAME-country-us or USERNAME-country-gb. To combine a country with a state, a city or a sticky session, build the line in the generator, which shows only the options your plan supports. Datacenter and ISP IPs have a fixed location that you choose when you buy them.

JavaScript
const dispatcher = new ProxyAgent('http://USERNAME-country-us:PASSWORD@HOST:PORT');

SOCKS5

For SOCKS5, the long-standing option is socks-proxy-agent with Node’s https module (or with Axios). fetch ignores the agent option, so socks-proxy-agent doesn’t work with fetch.

Install
npm install socks-proxy-agent
socks.mjs
import https from 'node:https';
import { SocksProxyAgent } from 'socks-proxy-agent';

const agent = new SocksProxyAgent('socks5h://USERNAME:PASSWORD@HOST:SOCKS5_PORT');

https.get('https://ipinfo.io/json', { agent }, (res) => {
  let body = '';
  res.on('data', (chunk) => (body += chunk));
  res.on('end', () => console.log(JSON.parse(body)));
});

socks5h:// lets the proxy resolve hostnames; socks5:// resolves them on your machine first.

Other Node.js tools

HTTP clients: Axios. Browsers: Puppeteer and Playwright. Crawling: Crawlee.

Tips

  • fetch failed hides the real reason. Log err.cause, and that error’s own cause, to see the proxy’s answer, such as a 407.
  • Close agents you no longer need with await dispatcher.close() so open connections don’t pile up.

Troubleshooting

  • TypeError: fetch failed, with “Proxy response (407) !== 200 when HTTP Tunneling” further down the cause chain

    The proxy rejected the credentials. Copy a fresh line and URL-encode special characters.More on this error →

  • The proxy is ignored

    fetch doesn’t use agent. Pass an undici agent as dispatcher, or use setGlobalDispatcher.

  • The IP doesn’t change on a rotating line

    Your client is reusing an open connection. Requests that share a tunnel share its IP. Open a new connection per request, or use one sticky line per task.More on this error →

  • Connection refused

    Check the host and port. SOCKS5 uses its own port, shown in the generator when you pick SOCKS5.More on this error →

Seeing another error? Read proxy errors and how to fix them, or contact support.

Questions

Does Node.js fetch support proxies?

Yes, through undici: pass new ProxyAgent(url) as the dispatcher option. On Node.js 22.21+ and 24+, setting NODE_USE_ENV_PROXY=1 also makes the built-in fetch use HTTP_PROXY and HTTPS_PROXY.

How do I use a proxy with a username and password in Node.js?

Put them in the proxy URL: new ProxyAgent("http://USERNAME:PASSWORD@HOST:PORT"). undici sends them as the Proxy-Authorization header.

How do I use SOCKS5 in Node.js?

Use socks-proxy-agent with the https module or Axios, and a socks5h:// URL so the proxy resolves hostnames.

How do I rotate IPs in Node.js?

Use a rotating line and a new connection per request, for example a new ProxyAgent per request. To keep an IP, reuse one agent per sticky line.

Try it with Node.js for $0.49

Get 1 GB of Residential Lite, then copy your rotating or sticky lines from the generator and paste them into the steps above.

Broken on our side? Fixed in 48h or refunded.

Related guides

All integrations →