Use one Puppeteer browser, process a URL list with bounded concurrency, give every page a deterministic filename, and isolate failures per URL. Set the viewport before navigation, choose a readiness condition that fits each site, capture with page.screenshot(), and close pages in finally blocks. The workflow below produces full-page images without allowing one timeout to discard the rest of the batch.
What a bulk screenshot script must do
Puppeteer does not provide a single “bulk screenshots” command. Its documented capture primitive is Page.screenshot(). Bulk work is application code around that primitive:
- Read URLs from an explicit input list.
- Launch a browser once for the batch.
- Create a page (or a small, bounded set of pages) for each job.
- Set the viewport before navigation.
- Navigate, wait for a site-appropriate readiness signal, and capture.
- Write a unique, stable output path.
- Record success or failure for each URL and continue.
- Close pages and the browser even when jobs throw errors.
The official guides demonstrate networkidle2 as one possible navigation wait, not as proof that every dynamic widget is visually complete. For applications with known loading states, wait for a meaningful selector or an application-specific signal instead.
Install Puppeteer and prepare an input list
In a new Node.js project, install Puppeteer:
npm install puppeteer
Create urls.txt with one absolute URL per line. Blank lines and lines beginning with # are ignored by the example below.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
https://example.com/
https://example.com/pricing
https://example.com/docs
Do not use a page title directly as a filename. Titles can contain slashes, duplicate names, or characters that are illegal on some operating systems. An index plus a sanitized slug is predictable and prevents accidental overwrites.
A dependable sequential batch script
Start sequentially when correctness matters more than throughput. This complete script uses one browser, one page at a time, per-URL error handling, a fixed viewport, full-page PNG output, and a JSON report.
const fs = require('node:fs/promises');
const path = require('node:path');
const puppeteer = require('puppeteer');
function slugFromUrl(rawUrl) {
const u = new URL(rawUrl);
const text = `${u.hostname}${u.pathname}`
.replace(/[^a-z0-9]+/gi, '-')
.replace(/^-+|-+$/g, '')
.toLowerCase();
return text || 'page';
}
async function main() {
const input = await fs.readFile('urls.txt', 'utf8');
const urls = input.split(/\r?\n/)
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
await fs.mkdir('screenshots', { recursive: true });
const browser = await puppeteer.launch();
const results = [];
try {
for (const [index, url] of urls.entries()) {
const filename = `${String(index + 1).padStart(4, '0')}-${slugFromUrl(url)}.png`;
const output = path.join('screenshots', filename);
const page = await browser.newPage();
try {
await page.setViewport({ width: 1365, height: 900 });
await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 45_000
});
await page.screenshot({
path: output,
fullPage: true,
type: 'png'
});
results.push({ url, status: 'ok', output });
console.log(`OK ${url} -> ${output}`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
results.push({ url, status: 'error', error: message });
console.error(`FAIL ${url}: ${message}`);
} finally {
await page.close();
}
}
} finally {
await browser.close();
await fs.writeFile('screenshots/results.json', JSON.stringify(results, null, 2));
}
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});
Run it with node bulk-screenshots.js. A failed navigation is recorded and the next URL is attempted. The outer finally prevents an abandoned browser process when an unexpected error escapes the loop.
Bounded parallelism for larger lists
Parallel pages can reduce wall-clock time, but Puppeteer’s documentation does not define a universal safe concurrency number or a throughput benchmark. The right limit depends on page weight, JavaScript activity, available memory and CPU, and the sites being contacted. Begin with a small limit such as 2–4 workers, observe resource use and failure rates, then adjust.
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 minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallThe worker-pool pattern below keeps at most CONCURRENCY pages active. It preserves per-URL isolation while sharing one browser.
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
const fs = require('node:fs/promises');
const path = require('node:path');
const puppeteer = require('puppeteer');
const CONCURRENCY = 3;
const NAVIGATION_TIMEOUT = 45_000;
function safeName(rawUrl, index) {
const u = new URL(rawUrl);
const slug = `${u.hostname}${u.pathname}`
.replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase() || 'page';
return `${String(index + 1).padStart(4, '0')}-${slug}.webp`;
}
async function capture(browser, url, index) {
const page = await browser.newPage();
const output = path.join('screenshots', safeName(url, index));
try {
await page.setViewport({ width: 1365, height: 900, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle2', timeout: NAVIGATION_TIMEOUT });
await page.screenshot({ path: output, fullPage: true, type: 'webp', quality: 82 });
return { url, status: 'ok', output };
} catch (error) {
return { url, status: 'error', error: error instanceof Error ? error.message : String(error) };
} finally {
await page.close();
}
}
async function main() {
const lines = (await fs.readFile('urls.txt', 'utf8')).split(/\r?\n/);
const urls = lines.map(x => x.trim()).filter(x => x && !x.startsWith('#'));
await fs.mkdir('screenshots', { recursive: true });
const browser = await puppeteer.launch();
const results = new Array(urls.length);
let next = 0;
async function worker() {
while (true) {
const index = next++;
if (index >= urls.length) return;
results[index] = await capture(browser, urls[index], index);
console.log(results[index]);
}
}
try {
await Promise.all(Array.from(
{ length: Math.min(CONCURRENCY, urls.length) },
() => worker()
));
} finally {
await browser.close();
await fs.writeFile('screenshots/results.json', JSON.stringify(results, null, 2));
}
}
main().catch(error => { console.error(error); process.exitCode = 1; });
This is a workload pattern, not an official Puppeteer recommendation. If memory rises, pages crash, or timeouts increase, lower the limit. If the target servers throttle requests, lower it further and consider a delay between jobs.
Choose the capture and rendering options
Viewport or full page
fullPage: false (the default) captures the current viewport. Use fullPage: true for a document-length image. Full-page captures can be very tall and memory-intensive; viewport captures are preferable for visual regression at a fixed fold.
Whole page or one element
Use page.screenshot() for the document. When the requirement is a card, chart, or other element, obtain an element handle and call its screenshot method. Puppeteer scrolls the element into view; the operation fails if the element has been detached.
const card = await page.waitForSelector('[data-report-card]', { timeout: 15_000 });
if (!card) throw new Error('Report card was not found');
await card.screenshot({ path: 'screenshots/report-card.png' });
PNG, JPEG, or WebP
PNG is the default and is lossless. JPEG and WebP are lossy formats; their quality setting does not apply to PNG. Use a lossy format when smaller files matter more than pixel-perfect fidelity.
await page.screenshot({ path: 'page.jpg', type: 'jpeg', quality: 85 });
await page.screenshot({ path: 'page.webp', type: 'webp', quality: 82 });
Transparent backgrounds and clipping
omitBackground: true enables transparency where the page supports it. The clip option captures a specified rectangle instead of the entire page.
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
await page.screenshot({
path: 'region.png',
clip: { x: 0, y: 0, width: 800, height: 600 },
omitBackground: true
});
Consistent desktop or emulated device output
Set the viewport before goto(); some sites react to viewport changes during layout. A page has its own viewport, so each worker must configure it explicitly. For responsive checks, run separate jobs with deliberately chosen dimensions rather than changing a viewport after the page has rendered.
Wait for the page you actually need
waitUntil: 'networkidle2' waits for low network activity, but analytics, polling, advertisements, and long-lived connections can make network state a poor proxy for visual readiness. For a known component, wait for its selector:
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45_000 });
await page.waitForSelector('[data-page-ready="true"]', { timeout: 20_000 });
await page.screenshot({ path: output, fullPage: true });
A short delay can help when an animation finishes after a selector appears, but use it as a targeted fallback rather than a universal guarantee:
await page.waitForTimeout(750);
For lazy-loaded images, full-page scrolling or the site’s own “loaded” signal may be required. If you control the application, expose a deterministic readiness marker instead of guessing from timing.
Retries, records, and safe reruns
Retry transient failures, not every failure. A timeout caused by a permanently invalid URL will not improve on the second attempt, while a temporary connection reset might. Keep the original error and attempt count in your report. Write to a temporary filename and rename it after a successful capture if downstream jobs must never see partial files.
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
For reruns, use a stable identifier derived from the input index and normalized URL. If duplicate URLs are meaningful separate jobs, keep the index in the name; if they should deduplicate, hash or canonicalize them before scheduling. Avoid putting credentials or access tokens in filenames or logs.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Navigation timeout | Slow page, blocked request, or an overly strict readiness condition. | Set a justified timeout, use domcontentloaded plus a readiness selector, inspect the URL, and retry only transient cases. |
| Blank or incomplete image | Capture ran before client-side rendering or lazy content finished. | Wait for a meaningful selector or application signal; verify that the selected viewport and full-page mode match the requirement. |
| Element not found | Selector changed, content is inside a frame, or rendering is conditional. | Wait for the selector, check the frame context, and log the page URL and HTML state for diagnosis. |
| Element detached | A framework re-render replaced the node between lookup and capture. | Find the element again immediately before screenshot(), and wait for the component to settle. |
| Files overwrite one another | Names were based only on a page title or hostname. | Include the input index and a sanitized path slug, as in the examples. |
| Browser processes remain | An exception bypassed cleanup. | Put page closure in an inner finally and browser closure in an outer finally. |
| Out-of-memory or renderer crashes | Too many pages, very tall documents, or heavy assets. | Lower concurrency, prefer viewport captures, reduce unnecessary resources, and process the list in smaller batches. |
| Different pixels between runs | Responsive layout, fonts, animation, time, locale, or changing remote data. | Fix viewport and relevant browser settings, wait for stable UI state, disable or hide known animations where appropriate, and capture the same test data. |
Performance, reliability, and cost considerations
- Concurrency: there is no documented universal safe number. Measure your pages and host, then keep a conservative bound.
- Browser lifetime: one browser per batch avoids repeated startup overhead; close it at the end. For very long jobs, planned browser recycling can limit accumulated process state, but it adds startup work.
- Page lifetime: close each page after capture. Reusing a small page pool can reduce creation overhead, but requires careful state cleanup between URLs.
- Network control: blocking unnecessary ads, trackers, or resource types can reduce work, but may also change the page you intend to document. Apply such rules only when the altered rendering is acceptable.
- Output size: PNG preserves detail; JPEG/WebP and a controlled viewport usually produce smaller artifacts. Keep format and quality consistent for comparisons.
- Observability: save URL, start/end time, status, output path, and error text. This makes it possible to rerun only failed jobs.
- Version behavior: the reviewed official pages cover Puppeteer documentation versions in the 25.9.0–25.12.0 range. Check the current API documentation when upgrading because defaults and supported options can change.
Or skip the browser setup
If you need an HTTP endpoint rather than maintaining Chromium workers, ScreenshotNeo returns a screenshot or PDF from one GET request. It can accept cookie and consent banners before capture and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
For a single URL, the request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for parameters. The same endpoint accepts options for full-page capture, CSS-element selection, dark mode, device and retina settings, PDF output, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL-based caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, easing migrations.
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try the endpoint.
FAQ
Can I use several pages in one Puppeteer browser?
Yes. A browser can contain multiple Page instances. Keep the number bounded and close every page when its job ends.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsDoes full-page mode include content below the fold?
It requests a document-length screenshot, but whether every lazy asset is ready still depends on the site’s loading behavior. Wait for the application’s own readiness signal when completeness matters.
Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
Should I capture each URL in a new browser?
Usually no for a batch: one browser with isolated pages avoids repeated launches. Use separate browser processes only when you have an explicit isolation or stability reason and have accepted the extra overhead.
Frequently Asked Questions
Can I use several pages in one Puppeteer browser?
Yes. A browser can contain multiple Page instances. Keep the number bounded and close every page when its job ends.
Does full-page mode include content below the fold?
It requests a document-length screenshot, but whether every lazy asset is ready still depends on the site’s loading behavior. Wait for the application’s own readiness signal when completeness matters.
Should I capture each URL in a new browser?
Usually no for a batch: one browser with isolated pages avoids repeated launches. Use separate browser processes only when you have an explicit isolation or stability reason and have accepted the extra overhead.
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.

