Skip to content
NextProxyNextProxyDocs

Node.js

Proxy integration for undici, axios, got and native fetch, with connection pool control and rotation.

Node's built-in fetch and http modules do not read HTTP_PROXY and cannot be pointed at a proxy directly. You must supply an agent or dispatcher explicitly.

Node 18+ bundles undici as the fetch implementation, and its ProxyAgent is the cleanest option available today.

shell
npm install undici
javascript
import { ProxyAgent, request } from 'undici';

const PROXY = 'http://USERNAME-country-US-session-job1-time-30:PASSWORD@GATEWAY_HOST:58971';

const dispatcher = new ProxyAgent(PROXY);

const { statusCode, body } = await request('https://api.ipify.org?format=json', {
  dispatcher,
});
console.log(statusCode, await body.json());

await dispatcher.close();

With global fetch

javascript
import { ProxyAgent, setGlobalDispatcher } from 'undici';

setGlobalDispatcher(
  new ProxyAgent('http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971'),
);

// Every fetch from here on goes through the proxy
const response = await fetch('https://api.ipify.org');
console.log(await response.text());

Rotating IPs

Omit session, and use a fresh dispatcher each time:

javascript
import { ProxyAgent, request } from 'undici';

const PROXY = 'http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971';

for (let i = 0; i < 5; i += 1) {
  const dispatcher = new ProxyAgent(PROXY);
  const { body } = await request('https://api.ipify.org', { dispatcher });
  console.log(await body.text());
  await dispatcher.close();
}

Concurrency with per-task sessions

javascript
import { ProxyAgent, request } from 'undici';

const GATEWAY = 'GATEWAY_HOST:58971';
const USER = 'USERNAME';
const PASSWORD = 'PASSWORD';

function proxyFor(sessionId, country = 'US', minutes = 30) {
  const user = `${USER}-country-${country}-session-${sessionId}-time-${minutes}`;
  return `http://${user}:${PASSWORD}@${GATEWAY}`;
}

async function fetchWith(sessionId) {
  const dispatcher = new ProxyAgent(proxyFor(sessionId));
  try {
    const { body } = await request('https://api.ipify.org', { dispatcher });
    return await body.text();
  } finally {
    await dispatcher.close();
  }
}

const results = await Promise.all(
  // session values allow only [A-Za-z0-9.-], so use dots rather than underscores
  Array.from({ length: 8 }, (_, i) => fetchWith(`task.${i}`)),
);
console.log(results);

Controlling the connection pool

javascript
import { ProxyAgent } from 'undici';

const dispatcher = new ProxyAgent({
  uri: 'http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971',
  connections: 16,
  // The gateway's idle timeout is 5 minutes; going shorter avoids reusing a closed connection
  keepAliveTimeout: 60_000,
  keepAliveMaxTimeout: 240_000,
});

axios

axios handles its proxy option poorly for HTTPS requests; in practice use httpsAgent:

shell
npm install axios https-proxy-agent
javascript
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const PROXY = 'http://USERNAME-country-US-session-job1-time-30:PASSWORD@GATEWAY_HOST:58971';
const agent = new HttpsProxyAgent(PROXY);

const client = axios.create({
  httpAgent: agent,
  httpsAgent: agent,
  // Required, otherwise axios adds its own proxy handling and conflicts with the agent
  proxy: false,
  timeout: 30_000,
});

const { data } = await client.get('https://api.ipify.org?format=json');
console.log(data);

got

shell
npm install got hpagent
javascript
import got from 'got';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';

const PROXY = 'http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971';

const client = got.extend({
  agent: {
    http: new HttpProxyAgent({ proxy: PROXY, keepAlive: true }),
    https: new HttpsProxyAgent({ proxy: PROXY, keepAlive: true }),
  },
  timeout: { request: 30_000 },
  retry: {
    limit: 3,
    // Distinguish proxy handshake failures from destination responses; retry only safe-to-replay requests
    statusCodes: [429, 500, 502, 503, 504],
  },
});

console.log(await client('https://api.ipify.org').text());

SOCKS5

shell
npm install socks-proxy-agent
javascript
import { SocksProxyAgent } from 'socks-proxy-agent';
import { request } from 'undici';

// socks5h lets the proxy resolve the hostname
const agent = new SocksProxyAgent(
  'socks5h://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971',
);

// With node:https
import https from 'node:https';

https.get('https://api.ipify.org', { agent }, (response) => {
  response.setEncoding('utf8');
  response.on('data', (chunk) => process.stdout.write(chunk));
});

Native http module with CONNECT

When you need full control over the protocol exchange (implementing your own fingerprint, for instance), send CONNECT by hand:

javascript
import http from 'node:http';
import tls from 'node:tls';

const USER = 'USERNAME-country-US';
const PASSWORD = 'PASSWORD';
const auth = Buffer.from(`${USER}:${PASSWORD}`).toString('base64');

const req = http.request({
  host: 'GATEWAY_HOST',
  port: 58971,
  method: 'CONNECT',
  path: 'api.ipify.org:443',
  headers: { 'Proxy-Authorization': `Basic ${auth}` },
});

req.on('connect', (res, socket) => {
  if (res.statusCode !== 200) {
    console.error('tunnel failed:', res.statusCode);
    socket.destroy();
    return;
  }

  const tlsSocket = tls.connect({ socket, servername: 'api.ipify.org' }, () => {
    tlsSocket.write('GET / HTTP/1.1\r\nHost: api.ipify.org\r\nConnection: close\r\n\r\n');
  });
  tlsSocket.pipe(process.stdout);
});

req.end();

Building a pool from the Extract API

javascript
const API = 'https://api.example.com/api/v1/proxy/extract';

async function fetchProxyPool(apiKey, count = 10) {
  const params = new URLSearchParams({
    apikey: apiKey,
    num: String(count),
    type: 'json',
    country: 'US',
    session: 'sticky',
    time: '10',
  });

  const response = await fetch(`${API}?${params}`);
  if (!response.ok) {
    throw new Error(`extraction failed: ${response.status} ${await response.text()}`);
  }
  const { data } = await response.json();
  // Passwordless ports, no credentials needed
  return data.map((item) => `http://${item.ip}:${item.port}`);
}

const pool = await fetchProxyPool('YOUR_API_KEY', 5);
console.log(pool);

Troubleshooting checklist

SymptomLikely cause
Requests bypass the proxy entirelyNative fetch without a dispatcher; axios missing proxy: false
407Wrong credentials; the - in the username mishandled
400invalid_proxy_parameters: option syntax, order or dependency
402traffic_exhausted / account_quota_exhausted / quota_exhausted: traffic or quota exhausted
403account_disabled / account_expired / access_denied / target_denied: account or policy denial
429connection_limit: reduce concurrency and connection rate
502Exit connection failed
503No eligible route or service temporarily unavailable
504Proxy connection establishment timed out
Frequent ECONNRESETPool keepAlive exceeds the gateway's 5-minute idle timeout
IP never changesDispatcher/agent reused, so the connection was never rebuilt

For HTTP / CONNECT failures, inspect X-NextProxy-Error and JSON error.code. Handle destination statuses and CONNECT handshake exceptions separately. See the error reference for the full mapping.

Did this page solve your problem?