Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Use Puppeteer’s Page.screenshot() in a loop: navigate to each URL, save the viewport image to a unique filename, and record the URL-to-file mapping. The browser work is straightforward; safe naming, page readiness, failure handling, and cleanup are the parts that make a batch reliable.
What the batch workflow does
Puppeteer’s screenshot guide documents the single-page sequence: launch a browser, create a page, navigate to a URL, call page.screenshot(), then close the browser. There is no special batch screenshot method in that workflow; your script repeats those operations for each URL. See the Puppeteer screenshots guide.
The example below reads URLs from an array, captures the initial viewport as PNG, and writes a JSON manifest mapping each original URL to its image path. It processes URLs sequentially so one failure can be recorded without ending the rest of the batch.
Free tools Windows power users keep installed
One-click scans. No signup required.
Install Puppeteer
In a new project directory, initialize a Node.js project and install Puppeteer:
#1 Best Overall
npm init -ynpm install puppeteer
The official API reference currently identifies Puppeteer 25.12.0; version details can change, so consult the current Page API reference and your installed package version when adapting the example.
Runnable Node.js example for a URL list
Save this as capture-thumbnails.js. Replace the sample URLs or load them from your own input source. It creates an output directory, names each file with an index plus a sanitized host/path, and writes one manifest entry per URL.
const puppeteer = require('puppeteer');
const fs = require('node:fs/promises');
const path = require('node:path');
const { createHash } = require('node:crypto');
const urls = [
'https://example.com/',
'https://www.wikipedia.org/',
'https://nodejs.org/en',
];
function makeFilename(url, index) {
const parsed = new URL(url);
const readable = `${parsed.hostname}${parsed.pathname}`
.replace(/[^a-z0-9.-]+/gi, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 70) || 'page';
const hash = createHash('sha256').update(url).digest('hex').slice(0, 10);
return `${String(index + 1).padStart(3, '0')}-${readable}-${hash}.png`;
}
async function main() {
const outputDir = path.resolve('output');
await fs.mkdir(outputDir, { recursive: true });
const results = [];
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 1 });
for (const [index, value] of urls.entries()) {
let url;
try {
url = new URL(value);
if (!['http:', 'https:'].includes(url.protocol)) {
throw new Error('Only http and https URLs are allowed');
}
} catch (error) {
results.push({ input: value, status: 'error', error: `Invalid URL: ${error.message}` });
continue;
}
const filename = makeFilename(url.href, index);
const outputPath = path.join(outputDir, filename);
try {
await page.goto(url.href, {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
await page.screenshot({ path: outputPath, type: 'png' });
results.push({ url: url.href, status: 'ok', file: outputPath });
console.log(`Saved ${url.href} -> ${outputPath}`);
} catch (error) {
results.push({ url: url.href, status: 'error', error: error.message });
console.error(`Failed ${url.href}: ${error.message}`);
}
}
await fs.writeFile(
path.join(outputDir, 'manifest.json'),
JSON.stringify(results, null, 2),
'utf8'
);
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run it with node capture-thumbnails.js. Successful images and manifest.json appear in output; failures remain listed with an error instead of silently disappearing.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesRank #2
- Intuitive interface of a conventional FTP client
- Easy and Reliable FTP Site Maintenance.
- FTP Automation and Synchronization
Choose the right readiness condition
The example uses waitUntil: 'domcontentloaded' as a practical starting point, not a guarantee that every site’s visible content is ready. Puppeteer’s screenshot guide demonstrates networkidle2, but that condition is not universal: long-polling or persistent network requests may prevent idleness, while lazy-loaded images or client-rendered content may appear after it. Pick the signal that fits the pages you capture.
domcontentloaded: Useful when the page’s initial markup is enough, but may precede client-side rendering or image completion.networkidle2: Can suit pages that settle after network activity; persistent requests can make it wait longer than intended.- Selector wait: If a particular element indicates that the preview is ready, wait for that selector before taking the screenshot.
- Bounded delay: Use a short, explicit delay only for a known site-specific rendering need; a delay alone is less tied to actual page state.
When waiting on a selector or adding a delay, keep the wait bounded so a missing element or stalled page does not hold the entire batch indefinitely.
Filenames, image dimensions, and capture scope
Make output names unique and safe
Do not use a URL directly as a filesystem path. URLs can contain characters unsuitable for filenames, and truncating a host/path alone can make distinct URLs collide. The example combines a sequence number, a sanitized readable fragment, and a short hash of the full URL. The manifest preserves the exact mapping for later processing.
Rank #3
Viewport thumbnail or full-page image?
For an at-a-glance thumbnail, the default viewport capture is generally the relevant choice. fullPage defaults to false; set fullPage: true when you want the full document instead. A full-page image can be much taller and larger, and it does not by itself guarantee that below-the-fold lazy content has loaded. Puppeteer also supports clip for a selected region, type for image format, quality for supported lossy formats, and omitBackground for a transparent capture; see the ScreenshotOptions API.
await page.screenshot({
path: outputPath,
type: 'jpeg',
quality: 80,
fullPage: false,
});
For a single component rather than the page viewport, locate it and use ElementHandle.screenshot(). The screenshots guide notes that this method attempts to scroll an element into view if it is hidden.
Sequential processing, concurrency, and cleanup
The example reuses one page and captures URLs sequentially. This is simple to reason about and limits simultaneous browser work, but total completion time grows with the number of URLs and each site’s navigation time. If you need parallel processing, use a controlled worker pool rather than opening an unbounded number of pages. The cited Puppeteer documentation does not establish a universally safe concurrency limit: browser memory use and site behavior depend on the pages and environment.
Rank #4
For larger jobs, decide whether to reuse a page or create one per task based on isolation needs. Reusing a page avoids repeatedly creating pages; separate pages can keep per-URL state more isolated but consume additional browser resources. In either arrangement, catch errors per URL and close the browser in a finally path. The API reference notes that selected page creation and close operations wait for an in-progress screenshot in the same browser context; do not treat that as a substitute for explicit cleanup in your own control flow.
Common problems and fixes
- A file is missing after a URL fails: Navigation or capture may have thrown. Check that URL’s manifest entry and use the recorded error to distinguish a navigation timeout from a screenshot or filesystem failure.
- Different URLs overwrite one file: The filename scheme is not unique enough. Include an index or a hash of the complete URL, as in the example, and retain a URL-to-file manifest.
- The thumbnail is blank or missing rendered content: The page may need more than
domcontentloaded. Wait for a page-specific selector or use an appropriate readiness condition; do not assume a fixed delay works across all sites. - The script hangs on a page: Navigation can exceed the timeout or an idle condition can be unsuitable for persistent requests. Keep navigation waits bounded and choose a condition that matches the target.
- The output looks like a document rather than a thumbnail: Check whether
fullPage: truewas set. The default is a viewport capture. - The browser process remains after an error: Ensure
browser.close()runs from afinallyblock, including when output writing or screenshot capture fails. - Invalid or unsafe targets are being processed: Validate the URL scheme and restrict the allowed destinations if the input can come from untrusted users. A URL list is also a potential server-side request risk; only visit destinations your application is authorized to access.
Or skip the browser setup
If you want a screenshot for each URL without managing Puppeteer and a browser process, ScreenshotNeo takes a URL in one GET request and returns an image or PDF. Its options include bulk capture of up to 100 URLs per call. For a single URL, the cURL command below saves a WebP image; see the ScreenshotNeo API documentation for request options and response details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents 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.
Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month without a card.
Best Value
Frequently Asked Questions
Can I name each screenshot after its URL?
Yes, but do not use the raw URL as a path. Sanitize a readable part, add a uniqueness component such as an index or hash, and keep a manifest mapping each URL to its filename.
Does Puppeteer have a built-in batch screenshot method?
The documented workflow is to repeat page navigation and screenshot calls for each URL; the loop and batch handling belong to your script.
Can I save screenshots as JPEG instead of PNG?
Yes. Set the screenshot option type to a supported format such as jpeg; the quality option applies to supported lossy formats.
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.

