The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →How do I make multiple HTTP requests at the same time in Node.js? Start each independent request without awaiting it immediately, then await the group. For the built-in HTTP client, use an http.Agent (or https.Agent) to reuse connections and set a per-host socket ceiling with maxSockets. These are separate controls: your code decides how many tasks are scheduled, while the Agent decides how many connections to a host can be active and queues the rest.
What “concurrent” means in Node.js
Node.js can have many network operations in flight while one JavaScript thread continues processing events. A request is concurrent when independent work is started before earlier work has finished. It is not the same as opening one socket per promise, nor does a socket limit automatically cap every promise or application task.
The low-level Node.js HTTP API documentation (v26.10.0) describes HTTP handling in terms of messages and streams; it does not parse your application payloads for you. You read response data, assemble it, and decide whether the result is valid.
Two controls you must keep separate
| Control | What it governs | Typical effect |
|---|---|---|
| Application scheduling | How many URL-processing tasks your code starts | A task limiter can keep a large input list from creating thousands of promises at once. |
http.Agent/https.Agent |
Connection persistence, reuse, and sockets per host | maxSockets caps active sockets for each host; additional requests wait in the Agent queue. |
| Remote server behavior | Whether an existing connection remains reusable | An idle connection can be closed or reuse can be refused, forcing a new connection. |
For example, you might schedule 20 URL tasks but configure maxSockets: 4. The first four eligible requests can use sockets to a host; the remainder are pending until a socket becomes available. If your tasks target several hosts, the Agent applies its socket limit per host rather than as one universal promise limit.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems#1 Best Overall
A complete concurrent HTTPS example
The following ES module uses Node.js’s built-in https client. It reuses connections, limits each host to four active sockets, collects response bodies, and cleans up the Agent when the batch is complete.
import https from 'node:https';
const agent = new https.Agent({
keepAlive: true,
maxSockets: 4
});
function getText(url) {
return new Promise((resolve, reject) => {
const req = https.get(url, { agent }, (res) => {
const chunks = [];
res.setEncoding('utf8');
res.on('data', (chunk) => chunks.push(chunk));
res.on('end', () => {
const body = chunks.join('');
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve({ url, statusCode: res.statusCode, body });
} else {
reject(new Error(`${url} returned HTTP ${res.statusCode}`));
}
});
});
req.on('error', reject);
req.setTimeout(15_000, () => {
req.destroy(new Error(`Timed out: ${url}`));
});
});
}
const urls = [
'https://example.com/',
'https://nodejs.org/',
'https://www.iana.org/domains/example'
];
try {
const results = await Promise.all(urls.map(getText));
for (const result of results) {
console.log(result.statusCode, result.url, result.body.length);
}
} catch (error) {
console.error('At least one request failed:', error);
} finally {
agent.destroy();
}
Why this starts requests concurrently
urls.map(getText) invokes getText for every URL before Promise.all waits for results. Each invocation enters the HTTP client and returns a promise while the response stream is still pending. Promise.all resolves when every operation succeeds and rejects as soon as one rejects, so the example treats the batch as a unit.
What the Agent is doing
The Agent keeps eligible connections alive for reuse and allows up to four active sockets per host. When all four are occupied, later requests are queued by the Agent and become active when a socket is free. This is connection-level back-pressure; it is not a general scheduler for unrelated asynchronous work.
Always release the Agent
Call agent.destroy() when the batch or service no longer needs it. Unused sockets consume operating-system resources. In a long-running process that makes repeated batches, keep one Agent for the intended lifetime and destroy it during shutdown rather than creating a new Agent for every request.
When a large list needs an application-level limit
Promise.all(urls.map(getText)) is convenient for a short list, but a list containing thousands of items schedules all tasks immediately. Use a small worker pool when you need to control task creation independently of socket reuse.
async function mapConcurrent(items, limit, worker) {
if (!Number.isInteger(limit) || limit < 1) {
throw new RangeError('limit must be a positive integer');
}
const output = new Array(items.length);
let next = 0;
async function run() {
while (true) {
const index = next++;
if (index >= items.length) return;
output[index] = await worker(items[index], index);
}
}
const workerCount = Math.min(limit, items.length);
await Promise.all(Array.from({ length: workerCount }, run));
return output;
}
const results = await mapConcurrent(urls, 8, getText);
Here, at most eight worker tasks are processing items at once. The Agent can still impose a lower per-host socket ceiling, so eight scheduled tasks might result in four active sockets to one host and four queued requests. Conversely, a high maxSockets does not force your worker pool to start more tasks.
Rank #2
Choosing maxSockets responsibly
Use the default when you have no measured reason to change it
The Agent’s per-host limit is a safety and resource setting, not a promise of faster completion. Raising it can increase simultaneous connections, local file-descriptor use, TLS work, and pressure on the remote service. Lowering it increases queuing and can reduce burst pressure.
Match the limit to the target and workload
- For a small interactive batch, a modest value keeps latency predictable without creating a connection burst.
- For a crawler or ingestion job, combine a task limiter with a per-host socket limit and respect the target service’s policies.
- For multiple hosts, remember that the Agent limit is evaluated per host; your total open sockets can therefore exceed the value for any one host.
Do not assume every connection will be reused
Reuse depends on the server. A server may close an idle connection or decline to keep it reusable. The next request then needs a new connection even though your Agent is configured for persistence.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Handling partial failures instead of losing the whole batch
When each URL is independent, use a settled-result pattern so one failure does not hide successful responses.
const settled = await Promise.allSettled(urls.map(getText));
for (const [index, result] of settled.entries()) {
if (result.status === 'fulfilled') {
console.log('OK', urls[index], result.value.statusCode);
} else {
console.error('FAILED', urls[index], result.reason.message);
}
}
Keep the request function responsible for transport and status handling, and let the caller decide whether one failure should abort the operation, be logged, or be retried under an explicit policy. Do not treat an HTTP error status as a successful payload merely because a TCP connection completed.
Timeouts, streams, and response size
Set a bounded wait
A request can remain pending if a peer stops responding. The example uses req.setTimeout and destroys the request, causing its promise to reject. Choose a timeout appropriate to the endpoint and workload; a single value is not universally correct.
Consume the response stream
Reading data and waiting for end allows the response to complete and the connection to become eligible for reuse. If you only inspect headers and abandon the stream, you can interfere with reuse and leave work unfinished.
Recommended Free Tools
Rank #3
Protect memory for untrusted sizes
The example buffers the complete body for clarity. For large or untrusted responses, count bytes while streaming and destroy the response when a configured limit is exceeded, or process chunks incrementally. Buffering every response from a large concurrent batch can exhaust memory even when socket counts are modest.
Common failure modes and fixes
“Everything is still sequential”
Check for an await inside a loop that starts and finishes one request before creating the next. Build the promises first (for example, with map) or use a worker pool.
More requests are queued than expected
Inspect the Agent configuration and the destination host. maxSockets is per host, and requests above that ceiling wait in the Agent’s pending queue. Increase it only when the remote service and your process can handle the additional connections.
Connections are not reused
The server may close idle connections or refuse reuse. Confirm that the same Agent is passed to each request and that its lifetime spans the batch. Even then, server behavior can require new connections.
The process keeps resources after the batch
Destroy an Agent that is no longer needed. In a service, perform that cleanup in your shutdown path after stopping new work.
One bad URL rejects the whole result
Use Promise.allSettled for independent work, or catch errors per item in your worker function. Keep the rejected reason associated with its URL so operators can diagnose it.
Rank #4
Memory rises during a large batch
Reduce the application worker limit, cap response sizes, and process streams incrementally. A low socket limit alone does not prevent many queued promises from retaining input and result data.
Observability and safe tuning
Record the URL (or a redacted host and path), start and finish times, status code, response bytes, timeout or socket errors, and whether the item was queued by your own worker pool. Compare completion time and error rate before changing limits. A faster batch that triggers more remote failures or local resource exhaustion is not an improvement.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Keep credentials out of URLs and logs. Use HTTPS for sensitive requests, validate redirects and response content for your application, and avoid sending a concurrency burst to a service that has not authorized it.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your concurrent jobs are website screenshots rather than API payloads, ScreenshotNeo provides a single HTTP endpoint and an MCP server for AI clients. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers.
Use the API directly from Node.js (see the ScreenshotNeo API documentation):
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
The same request with cURL is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
And with Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
ScreenshotNeo supports PNG, JPEG, WebP, and PDF output; full-page captures with lazy images, CSS-selector element captures, dark mode, device presets, arbitrary viewports, retina scale, PDF paper and page options, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify migration.
An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Frequently asked questions
Does maxSockets limit all Node.js promises?
No. It limits concurrent sockets per host for requests using that Agent. Promises for other work require their own scheduling control.
Should I create one Agent per request?
No. Reuse an Agent for the lifetime of a related batch or service so connections can persist, then destroy it when that lifetime ends.
Why can a request wait even when my code started it?
The Agent may have reached its per-host socket ceiling, placing the request in a pending queue until a socket is available.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Frequently Asked Questions
Can I use this pattern with HTTP as well as HTTPS?
Yes. Use Node’s http module and an http.Agent for HTTP URLs; use https and https.Agent for TLS URLs.
Is a higher socket limit always faster?
No. It can increase local and remote resource pressure, while server connection policies may prevent reuse. Measure the actual workload and tune conservatively.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




