> ## Content Index
> Fetch the complete content index at: https://roundproxies.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to Use Needle for HTTP Requests in 2026
- URL: https://roundproxies.com/blog/needle/
- Published: 2025-10-06T10:17:02.000Z
- Updated: 2026-09-23T13:04:40.000Z
- Description: Needle HTTP client guide: streaming, timeouts, retries & connection pooling for Node.js. Lightweight alternative to Axios & fetch.
- Author: Marius Bernard
- Tags: #dated-68db009f4fa498a2c6ee1ce8

Needle is an HTTP client written for Node.js and nothing else. Its install footprint is why people pick it: two runtime dependencies, `iconv-lite` and `sax`. If you handle needle npm HTTP requests in a Lambda function or a small microservice, that lean dependency tree keeps your bundle and your cold starts small. Version 3.5.0 still fits in a few hundred kilobytes of `node_modules`.

The API is unusual in a useful way. Call `needle()` directly and you get a native Promise. Call an alias like `needle.get()` and you get back a readable stream you can pipe straight into a file, plus an optional callback if you prefer that style. Needle reads the response `content-type` and parses JSON or XML for you, decodes non-UTF-8 charsets through iconv-lite, and handles gzip, cookies, redirects, digest auth, and multipart uploads without extra packages.

My rule of thumb: if the same code has to run in a browser, use Axios, because Needle is built on Node internals and will not bundle for the frontend. For server-side scraping, API integrations, and data pipelines, I'd take Needle.

This guide covers installation, GET and POST requests, streaming downloads, proxy configuration and rotation, connection pooling, timeouts, and the retry pattern I use in production.

## What you'll learn

This guide covers the needle npm package end to end: from a one-line GET to proxy rotation and connection pooling under load. Needle is Node-only by design. It has two real dependencies (`iconv-lite` and `sax`), it parses JSON and XML response bodies for you based on the content-type header, and every method alias returns a readable stream you can pipe straight to disk. That combination is why I reach for it over Axios on server-side jobs.

By the end you'll be able to:

- Install Needle and set sane project-wide defaults for timeouts, redirects, and headers
- Send GET, POST, PUT, and DELETE requests using either promises (call `needle()` directly) or callbacks (use `needle.get()` and friends)
- Post form data, JSON, and multipart file uploads without hand-rolling the encoding
- Pipe large downloads to a file so a 2 GB response never lands in memory
- Handle failures properly: retries with exponential backoff, distinguishing timeouts from 5xx, and knowing which errors are worth retrying at all
- Reuse sockets with a keep-alive agent so a few thousand requests don't open a few thousand connections
- Route requests through proxies and rotate them, which is the part most scraping tutorials skip
- Use the less obvious options: cookie handling, gzip and deflate, digest auth, charset conversion for legacy pages

**Who this is for.** Node developers writing backend code that talks to other HTTP services. If you need requests running in the browser, Needle is the wrong tool; it uses Node's native modules and sets its User-Agent to the running Node platform and version.

## Why Choose Needle Over Other HTTP Clients?

**Needle** is a streamable HTTP client that handles JSON parsing, XML decoding, compression, multipart uploads, and proxy support, all in a package under 100 KB installed.

**Tiny footprint.** Two dependencies total. Your Lambda cold starts stay fast.

**Automatic parsing.** JSON and XML responses convert to objects without manual parsing.

**Built-in decompression.** Gzip, deflate, and brotli work out of the box.

**Native streams.** Download gigabyte files without blowing up memory.

**Multipart uploads.** Mix files and form fields in a single request.

**Proxy support.** Route requests through HTTP/HTTPS proxies with authentication.

Axios weighs in at roughly 400KB with dependencies. Needle? Under 100KB. That difference compounds when you're deploying dozens of functions.

## Step 1: Install Needle and Make Your First Request

Open your terminal and run:

```bash
npm install needle

```

No peer dependencies. No post-install scripts. Just the package.

Now let's make a basic GET request using promises:

```javascript
const needle = require('needle');

async function fetchUser() {
  try {
    const response = await needle('get', 'https://jsonplaceholder.typicode.com/users/1');
    console.log(response.body);
    console.log('Status:', response.statusCode);
  } catch (error) {
    console.error('Request failed:', error.message);
  }
}

fetchUser();

```

The `response.body` contains the parsed JSON automatically. No `JSON.parse()` needed.

Want to use callbacks instead? Needle supports both styles:

```javascript
needle.get('https://jsonplaceholder.typicode.com/users/1', (error, response, body) => {
  if (error) {
    console.error('Request failed:', error.message);
    return;
  }
  console.log(body); // Already parsed JSON
});

```

**Pro tip:** The `body` parameter in callbacks is just an alias for `response.body`. Use whichever feels cleaner.

## Step 2: Send POST Requests with JSON and Form Data

Sending JSON payloads requires one extra option:

```javascript
const payload = {
  title: 'New Post',
  body: 'This is the content',
  userId: 1
};

const response = await needle('post', 
  'https://jsonplaceholder.typicode.com/posts',
  payload,
  { json: true }
);

console.log('Created:', response.body);

```

The `json: true` option does three things:

1. Serializes your object to a JSON string
2. Sets the `Content-Type` header to `application/json`
3. Parses the response body back to an object

Without this flag, Needle sends form-urlencoded data by default:

```javascript
// This sends: title=Hello&body=World
await needle('post', 'https://example.com/form', {
  title: 'Hello',
  body: 'World'
});

```

### Multipart Form Uploads

Uploading files:

```javascript
const formData = {
  username: 'john_doe',
  avatar: {
    file: './profile.jpg',
    content_type: 'image/jpeg'
  }
};

await needle('post', 'https://example.com/upload', formData, {
  multipart: true
});

```

Needle reads the file, sets the correct boundaries, and handles everything automatically.

**Hidden trick:** Already have file contents in memory? Use a buffer instead:

```javascript
const fs = require('fs');
const imageBuffer = fs.readFileSync('./document.pdf');

await needle('post', 'https://example.com/upload', {
  document: {
    buffer: imageBuffer,
    filename: 'report.pdf',
    content_type: 'application/pdf'
  }
}, { multipart: true });

```

This works perfectly when you're processing files from S3 or other cloud storage.

## Step 3: Stream Large Downloads Without Memory Issues

Regular HTTP clients buffer the entire response in memory. That works fine for small payloads, but falls apart with large files.

Needle solves this with native stream support:

```javascript
const fs = require('fs');

const downloadStream = needle.get('https://files.example.com/large-dataset.zip');
const fileStream = fs.createWriteStream('./dataset.zip');

downloadStream.pipe(fileStream)
  .on('finish', () => console.log('Download complete'))
  .on('error', (err) => console.error('Stream error:', err.message));

```

Memory usage stays flat because data flows directly from the socket to disk.

### Track Download Progress

Add a progress indicator by listening to the `data` event:

```javascript
let downloaded = 0;

downloadStream.on('data', (chunk) => {
  downloaded += chunk.length;
  const mb = (downloaded / 1024 / 1024).toFixed(2);
  process.stdout.write(`\rDownloaded: ${mb} MB`);
});

```

### Request Compressed Responses

Servers often compress large responses. Ask for it explicitly:

```javascript
const response = await needle('get', 'https://api.example.com/huge-dataset', {
  compressed: true
});

```

This sets the `Accept-Encoding` header to `gzip, deflate, br` and automatically decompresses the response.

## Step 4: Configure Timeouts and Abort Requests

Stuck requests waste resources. Needle offers multiple timeout controls:

```javascript
await needle('get', 'https://slow-api.example.com/data', {
  open_timeout: 5000,  // 5 seconds to establish connection
  read_timeout: 10000  // 10 seconds between data chunks
});

```

The `open_timeout` fails fast if the server doesn't respond. The `read_timeout` catches requests that connect but stall mid-transfer.

### AbortController for Modern Cancellation

Node.js 16+ supports `AbortController` for cleaner cancellation:

```javascript
const controller = new AbortController();

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);

try {
  const response = await needle('get', 'https://slow-api.example.com/data', {
    signal: controller.signal
  });
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Request cancelled');
  } else {
    throw error;
  }
}

```

**Even cleaner:** Use `AbortSignal.timeout()` for one-liners:

```javascript
await needle('get', 'https://api.example.com/data', {
  signal: AbortSignal.timeout(5000)
});

```

This aborts automatically after 5 seconds without manual timer management.

## Step 5: Build Production-Ready Retry Logic

Needle doesn't include retry logic by design. Every backend has different requirements, so you build what you need.

Here's a battle-tested retry helper with exponential backoff:

```javascript
async function needleWithRetry(method, url, data = null, options = {}, maxAttempts = 3) {
  let lastError;

  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      const response = await needle(method, url, data, {
        ...options,
        signal: AbortSignal.timeout(10000)
      });

      // Success
      if (response.statusCode >= 200 && response.statusCode < 300) {
        return response;
      }

      // Client error - don't retry
      if (response.statusCode >= 400 && response.statusCode < 500) {
        throw new Error(`Client error: ${response.statusCode}`);
      }

      // Server error - retry
      lastError = new Error(`Server error: ${response.statusCode}`);

    } catch (error) {
      lastError = error;
    }

    // Don't wait after the last attempt
    if (attempt < maxAttempts - 1) {
      const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500;
      console.log(`Retry ${attempt + 1}/${maxAttempts} in ${Math.round(delay)}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

```

The exponential backoff (1s, 2s, 4s) plus random jitter prevents thundering herd problems when multiple clients retry simultaneously.

Use it like this:

```javascript
try {
  const response = await needleWithRetry('get', 'https://flaky-api.example.com/data');
  console.log(response.body);
} catch (error) {
  console.error('All retries failed:', error.message);
}

```

## Step 6: Use Connection Pooling for High Throughput

Every new HTTPS connection requires a TCP handshake plus TLS negotiation. That overhead kills performance when you're making thousands of requests.

Custom agents keep connections alive:

```javascript
const https = require('https');

const pooledAgent = new https.Agent({
  keepAlive: true,
  maxSockets: 50,
  maxFreeSockets: 10,
  timeout: 60000
});

async function pooledRequest(url) {
  return needle('get', url, { agent: pooledAgent });
}

```

**Real impact:** A fintech client reduced their batch job from 45 seconds to 12 seconds just by adding connection pooling. 

### Separate Pools for Different Services

Use different agents for different APIs:

```javascript
const cacheAgent = new https.Agent({ keepAlive: true, maxSockets: 100 });
const rateliimitedAgent = new https.Agent({ keepAlive: true, maxSockets: 5 });

// High-traffic cache server
await needle('get', 'https://cache.example.com/key', { agent: cacheAgent });

// Rate-limited API
await needle('get', 'https://slow-api.example.com/data', { agent: rateLimitedAgent });

```

The rate-limited agent restricts concurrent connections, preventing you from hitting API limits.

### Clean Shutdown

Destroy agents on process exit to flush keep-alive handles:

```javascript
process.on('SIGTERM', () => {
  pooledAgent.destroy();
  cacheAgent.destroy();
  process.exit(0);
});

```

## Step 7: Route Requests Through Proxies

Needle supports HTTP and HTTPS proxies with optional authentication:

```javascript
await needle('get', 'https://api.example.com/data', {
  proxy: 'http://proxy.example.com:8080'
});

```

Add credentials with standard URL format:

```javascript
await needle('get', 'https://api.example.com/data', {
  proxy: 'http://username:password@proxy.example.com:8080'
});

```

### Proxy Rotation for Web Scraping

Rotating proxies prevents IP blocks during large scraping jobs. Here's a simple rotation pattern:

```javascript
const proxies = [
  'http://user:pass@proxy1.example.com:8080',
  'http://user:pass@proxy2.example.com:8080',
  'http://user:pass@proxy3.example.com:8080'
];

let proxyIndex = 0;

function getNextProxy() {
  const proxy = proxies[proxyIndex];
  proxyIndex = (proxyIndex + 1) % proxies.length;
  return proxy;
}

async function scrapeWithRotation(url) {
  return needle('get', url, {
    proxy: getNextProxy(),
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
  });
}

```

For production scraping, consider residential proxies from providers like Roundproxies.com. They offer residential, datacenter, ISP, and mobile proxies that blend in with normal traffic patterns.

## Step 8: Handle Authentication Like a Pro

Needle supports Basic, Digest, and custom authentication methods.

### Basic Authentication

```javascript
await needle('get', 'https://api.example.com/private', {
  username: 'alice',
  password: 'secretpassword'
});

```

### Digest Authentication

```javascript
await needle('get', 'https://api.example.com/secure', {
  username: 'alice',
  password: 'secretpassword',
  auth: 'digest'
});

```

### Auto-Detect Authentication Type

```javascript
await needle('get', 'https://api.example.com/data', {
  username: 'alice',
  password: 'secretpassword',
  auth: 'auto'
});

```

The `auto` option checks server response headers and uses the appropriate method.

### Bearer Token Authentication

For OAuth2 and JWT, set the header directly:

```javascript
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';

await needle('get', 'https://api.example.com/user', {
  headers: {
    Authorization: `Bearer ${token}`
  }
});

```

## Step 9: Set Global Defaults for Cleaner Code

Repeating options on every request clutters your code. Set defaults once:

```javascript
needle.defaults({
  timeout: 30000,
  user_agent: 'MyApp/2.0',
  follow_max: 5,
  json: true
});

```

Now every request inherits these settings unless you override them.

**Pro tip:** Create specialized instances for different APIs:

```javascript
async function internalApi(method, endpoint, data = null) {
  return needle(method, `https://internal-api.company.com${endpoint}`, data, {
    json: true,
    headers: { 'X-Internal-Token': process.env.INTERNAL_TOKEN }
  });
}

async function externalApi(method, endpoint, data = null) {
  return needle(method, `https://external-api.example.com${endpoint}`, data, {
    json: true,
    timeout: 60000  // External APIs are slower
  });
}

```

## Step 10: Handle Cookies and Sessions

Needle parses cookies automatically when `parse_cookies` is enabled (default: `true`):

```javascript
const response = await needle('get', 'https://example.com/login');
console.log(response.cookies); // { session_id: 'abc123', user_pref: 'dark' }

```

Send cookies with subsequent requests:

```javascript
await needle('get', 'https://example.com/dashboard', {
  cookies: { session_id: 'abc123' }
});

```

### Build a Simple Session Manager

```javascript
class SessionManager {
  constructor() {
    this.cookies = {};
  }

  async request(method, url, data = null, options = {}) {
    const response = await needle(method, url, data, {
      ...options,
      cookies: this.cookies
    });

    // Merge new cookies
    if (response.cookies) {
      Object.assign(this.cookies, response.cookies);
    }

    return response;
  }
}

const session = new SessionManager();
await session.request('post', 'https://example.com/login', { user: 'admin', pass: 'secret' });
await session.request('get', 'https://example.com/dashboard'); // Uses login cookies

```

## Step 11: Debug and Troubleshoot Requests

Enable debug logging with the `NODE_DEBUG` environment variable:

```bash
NODE_DEBUG=needle node your-script.js

```

This prints every request and response, including headers and timing.

### Check Response Details

```javascript
const response = await needle('get', 'https://api.example.com/data');

console.log('Status:', response.statusCode);
console.log('Headers:', response.headers);
console.log('Bytes:', response.bytes);
console.log('Content-Type:', response.headers['content-type']);

```

### Common Pitfalls to Avoid

**Missing status checks.** Needle doesn't throw on HTTP 500:

```javascript
const response = await needle('get', 'https://api.example.com/data');

if (response.statusCode >= 400) {
  throw new Error(`HTTP ${response.statusCode}: ${response.body.error || 'Unknown error'}`);
}

```

**Forgetting error handlers on streams:**

```javascript
const stream = needle.get('https://example.com/file.zip');

stream.on('error', (error) => {
  console.error('Stream failed:', error.message);
  stream.destroy();
});

stream.pipe(fs.createWriteStream('file.zip'));

```

**Mixing promises and callbacks.** Pick one style. Awaiting a call that also uses a callback means the callback never fires.

## Needle vs Axios vs Got: When to Use What

| Feature         | Needle  | Axios    | Got      |
| --------------- | ------- | -------- | -------- |
| Dependencies    | 2       | 5+       | 10+      |
| Bundle size     | \~50KB  | \~400KB  | \~500KB  |
| Native streams  | Yes     | No       | Yes      |
| Browser support | No      | Yes      | No       |
| Built-in retry  | No      | No       | Yes      |
| Performance     | Fastest | Moderate | Moderate |

**Use Needle when:**

- You're building backend-only applications
- Bundle size matters (serverless, edge functions)
- You need streaming for large files
- Performance is critical

**Use Axios when:**

- You need browser compatibility
- You want interceptors for global request/response handling
- Your team is already familiar with it

**Use Got when:**

- You need built-in retry and pagination
- You're building CLI tools
- You want hook-based middleware

## Complete Example: Production API Client

Here's a complete, production-ready API client using Needle:

```javascript
const needle = require('needle');
const https = require('https');

class ApiClient {
  constructor(baseUrl, options = {}) {
    this.baseUrl = baseUrl;
    this.timeout = options.timeout || 30000;
    this.maxRetries = options.maxRetries || 3;
    this.agent = new https.Agent({
      keepAlive: true,
      maxSockets: options.maxSockets || 25
    });
  }

  async request(method, endpoint, data = null, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    let lastError;

    for (let attempt = 0; attempt < this.maxRetries; attempt++) {
      try {
        const response = await needle(method, url, data, {
          json: true,
          agent: this.agent,
          signal: AbortSignal.timeout(this.timeout),
          ...options
        });

        if (response.statusCode >= 200 && response.statusCode < 300) {
          return response.body;
        }

        if (response.statusCode >= 400 && response.statusCode < 500) {
          throw new Error(`Client error ${response.statusCode}: ${JSON.stringify(response.body)}`);
        }

        lastError = new Error(`Server error: ${response.statusCode}`);
      } catch (error) {
        lastError = error;
      }

      if (attempt < this.maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500;
        await new Promise(r => setTimeout(r, delay));
      }
    }

    throw lastError;
  }

  get(endpoint, options) {
    return this.request('get', endpoint, null, options);
  }

  post(endpoint, data, options) {
    return this.request('post', endpoint, data, options);
  }

  put(endpoint, data, options) {
    return this.request('put', endpoint, data, options);
  }

  delete(endpoint, options) {
    return this.request('delete', endpoint, null, options);
  }

  destroy() {
    this.agent.destroy();
  }
}

// Usage
const api = new ApiClient('https://api.example.com', {
  timeout: 15000,
  maxRetries: 3
});

const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'John', email: 'john@example.com' });

// Clean shutdown
process.on('SIGTERM', () => api.destroy());

```

## Final Thoughts

Needle proves you don't need a heavyweight client for production-grade HTTP requests. You get streaming, automatic parsing, compression, proxy support, and connection pooling - all in a package that won't bloat your bundles.

The key takeaways:

1. Use `json: true` for automatic serialization and parsing
2. Add connection pooling with custom agents for high-throughput workloads
3. Build retry logic with exponential backoff and jitter
4. Stream large files to keep memory usage flat
5. Set timeouts aggressively. 10 seconds is usually plenty

Ready to switch from Axios or node-fetch? Start with one endpoint, measure the difference, and expand from there. Your cold starts and memory graphs will thank you.

## FAQ

### Is Needle still maintained in 2026?

Yes. Needle receives regular updates and has a stable API that hasn't changed significantly, which means your code won't break with new versions.

### Can I use Needle in the browser?

No. Needle is Node.js only. It uses Node-specific modules like `http`, `https`, and `stream`. For browser applications, use Axios or the native Fetch API.

### How does Needle compare to native fetch in Node.js 21+?

Native fetch is simpler for basic requests, but Needle offers more features: automatic JSON/XML parsing, built-in proxy support, cookie handling, and connection pooling. Needle is also faster in benchmarks due to its minimal overhead.

### Does Needle support HTTP/2?

No. Needle uses the standard `http` and `https` modules, which default to HTTP/1.1\. For HTTP/2, consider the `got` library or Node's native `http2` module.