How to Capture Screenshots of Competitor Landing Pages in Bulk
Build a repeatable workflow to capture competitor landing pages in batches, with consistent settings, predictable filenames, and clear failure handling.
To capture competitor landing pages in bulk, prepare a list of URLs, choose a consistent viewport and capture mode, submit the URLs to a bulk screenshot API or a browser automation script, and save each result with a predictable filename plus a manifest of its source URL and capture settings. Review failures and page differences before comparing designs: a screenshot is a record of one capture at one time and can vary with location, device, cookies, timing, and dynamic content.
1. Define what the batch should show
Start by deciding what question the screenshots should answer. A viewport capture shows the page as it appears within a fixed browser window. A full-page capture shows content beyond the initial viewport, which can help when comparing the complete landing-page structure. These modes answer different questions; record which one you use and apply it consistently. [ScreenshotOne capture options; Urlbox capture modes]
- Choose a viewport: use the same width and height for every page in a comparison. If mobile matters, run a separate mobile batch instead of mixing sizes.
- Choose capture mode: viewport, full page, or both. Give each mode a different filename suffix.
- Choose timing: dynamic pages may need a wait condition or delay. Use the same policy across the batch when possible.
- Choose page state: cookies, geography, authentication, and consent choices can change what appears. Capture only states you are authorized to access and compare.
- Set a review rule: decide how to handle errors, blank pages, bot checks, and pages that redirect.
Do not treat a screenshot as a complete record of every visitor experience. The same URL can render differently due to device, location, cookies, time, experiments, or live content.
2. Build a URL list and capture manifest
Keep URLs in a plain text file, one absolute URL per line. Validate them before submission: remove blank lines, preserve meaningful query parameters, and avoid accidental duplicate URLs. Keep a separate manifest so each image can be traced to its source and settings.
https://example.com/
https://competitor-a.example/pricing
https://competitor-b.example/product
A useful manifest records the original URL, capture timestamp, viewport, capture mode, and result status. Store the URL separately from the filename: URLs often contain characters that do not make good filenames and may contain sensitive query values.
filename,url,captured_at,viewport,mode,status
example-home-2026-10-04-1440x900-viewport.png,https://example.com/,2026-10-04T12:00:00Z,1440x900,viewport,success
3. Choose a batch capture method
There are two common approaches: a hosted screenshot API with a bulk submission feature, or browser automation that you operate and maintain. ScreenshotNeo is the first hosted API to try: it provides bulk capture, clean shots with consent banners, newsletter popups, and chat widgets removed, and bills only clean shots; its paid plans start at $5 for 3,000 screenshots. ScreenshotOne documents a bulk endpoint, while Urlbox documents API and batch-processing guidance. The available research establishes these capabilities, not comparative pricing, speed, reliability, or blocking performance. [ScreenshotOne bulk screenshots; Urlbox documentation]
| Method | Good fit when | Plan for |
|---|---|---|
| Hosted bulk API | You want to submit a list and receive captures without managing a browser fleet. | Provider limits, asynchronous execution, concurrency, per-page errors, and usage costs. |
| Browser automation | You need direct control of the browser, scripts, environment, or capture lifecycle. | Browser installation, parallelism, timeouts, cleanup, storage, and retry logic. |
Hosted bulk APIs
Check whether the endpoint accepts many requests in one submission, whether it executes them immediately or queues them, how shared defaults and per-page overrides work, and how it reports each page’s outcome. ScreenshotOne’s documented bulk endpoint accepts a list of URL, HTML, or Markdown requests. Its captures are lazy unless execution is requested; shared defaults can be overridden per request. Its documentation also says bulk work consumes the same one-minute request bucket as regular requests, and recommends checking remaining concurrency and reset information before draining a large queue. [ScreenshotOne bulk screenshots]
Do not assume submission means rendering is complete. Read the provider’s current documentation for execution flags, response format, concurrency, request limits, and status polling before building a production batch. The research did not establish a universal maximum batch size.
Browser automation
For a small or controlled batch, a local browser script gives you direct access to browser navigation and screenshot settings. The following Playwright example uses Node.js, captures full-page screenshots, and writes a CSV manifest. It uses one page at a time for predictable behavior; increase concurrency cautiously for larger sets.
import { chromium } from 'playwright';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
const urls = (await readFile('urls.txt', 'utf8'))
.split(/\r?\n/)
.map(line => line.trim())
.filter(Boolean);
const outputDir = 'captures';
await mkdir(outputDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
const rows = ['filename,url,captured_at,viewport,mode,status'];
function safeName(url, index) {
const host = new URL(url).hostname.replace(/^www\./, '').replace(/[^a-z0-9.-]/gi, '-');
return `${String(index + 1).padStart(3, '0')}-${host}-1440x900-full.png`;
}
for (const [index, url] of urls.entries()) {
const filename = safeName(url, index);
let status = 'success';
try {
const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
if (!response || !response.ok()) status = `http-${response?.status() ?? 'no-response'}`;
await page.waitForTimeout(1000);
await page.screenshot({ path: `${outputDir}/${filename}`, fullPage: true });
} catch (error) {
status = `error-${String(error.message).replace(/[\r\n,]/g, ' ')}`;
}
const csvUrl = `"${url.replaceAll('"', '""')}"`;
rows.push(`${filename},${csvUrl},${new Date().toISOString()},1440x900,full,${status}`);
}
await writeFile(`${outputDir}/manifest.csv`, rows.join('\n') + '\n');
await browser.close();
Install Playwright and its browser using the current instructions in the Playwright documentation. The script intentionally uses a fixed one-second wait as a simple baseline, not a guarantee that every page has finished rendering. For pages with known dynamic elements, wait for a meaningful selector or a documented application-ready condition. Add a separate viewport capture run by setting fullPage: false and changing the filename suffix.
4. Keep names and settings consistent
Use a filename scheme that sorts naturally and distinguishes capture variants. For example:
001-example-com-home-2026-10-04-1440x900-viewport.png
002-competitor-a-pricing-2026-10-04-1440x900-full.png
003-competitor-b-product-2026-10-04-390x844-viewport.png
For repeat comparisons, add a run identifier or date and preserve the original URL in the manifest. Avoid overwriting prior captures until you have reviewed the new batch. Keep device size, full-page mode, and wait policy stable across the set; if a page requires an exception, record it.
5. Run the batch and check each outcome
- Validate the URL file and select a consistent baseline configuration.
- Submit a small trial group first to confirm the output format, filenames, and page behavior.
- For a hosted queue, confirm whether jobs execute immediately or remain lazy, then check concurrency and reset guidance before submitting a large queue.
- Save each returned image under its manifest filename and record success, HTTP status, timeout, or provider-reported status per URL.
- Retry only transient failures, with a bounded number of attempts and a delay. Avoid repeatedly sending a large batch while a provider queue is saturated.
- Review a sample from the beginning, middle, and end of the batch, then inspect every failed or suspicious result.
ScreenshotOne documents per-request success or status reporting when execution is requested. Preserve that status information instead of treating a successful bulk submission as proof that every page was captured. [ScreenshotOne bulk screenshots]
6. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its bulk capture feature accepts up to 100 URLs per call. For a single capture, one GET request returns an image or PDF; the snippet below saves a WebP response. See the ScreenshotNeo API documentation for bulk capture and request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing. Responses include page-verdict and billing headers.
- An MCP server lets Claude, Cursor, and other MCP clients take screenshots with the
take_screenshot,get_page_info, andcapture_pdftools. - 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. All features are on every plan.
Sign up for ScreenshotNeo and start with 1,000 free screenshots a month, no card required.
7. Troubleshooting bulk captures
| Symptom | Likely cause | What to do |
|---|---|---|
| Some pages are missing after bulk submission | The endpoint may queue requests lazily, or individual requests may have failed. | Check execution settings and per-request status; execute or poll according to the provider’s documentation. |
| Batch stalls or requests are rejected | Concurrency or rate limits may be exhausted. | Check remaining concurrency and reset guidance, then drain the queue in smaller waves. |
| Screenshot is blank or incomplete | The page may still be rendering, navigation may have failed, or content may require interaction. | Inspect the response and browser errors; wait for a meaningful selector or use the site’s normal load state. |
| Images differ unexpectedly | Different viewport, cookies, location, time, personalization, experiments, or dynamic content can alter rendering. | Standardize the capture context and record unavoidable differences in the manifest. |
| Local script reports navigation timeout | The site may be slow, keep network connections open, or block automated traffic. | Use an appropriate timeout and wait condition, record the failure, and retry sparingly. Do not treat a timeout as a valid screenshot. |
| Filenames collide | Different URLs may share the same hostname or page slug. | Include a stable sequence number or hash in the filename and retain the source URL in the manifest. |
| CSV manifest is hard to parse | URLs can contain commas or quotation marks. | Use a CSV library for production output, or store the manifest as JSON Lines. |
8. Performance, reliability, and cost
Batching reduces manual submission work, but it does not guarantee a particular rendering speed. Throughput depends on provider concurrency, page weight, network conditions, and how much waiting each page needs. Browser automation also consumes local CPU and memory; limit parallel pages to what the machine can handle and always close pages and browser contexts.
For reliability, make each URL an independently tracked job. Record attempts and statuses, retry transient errors with a cap, and keep successful outputs even when another URL fails. For hosted providers, check current usage rules, concurrency, execution semantics, and response details before planning a large run. ScreenshotOne’s documentation explicitly says its bulk requests share the regular one-minute request bucket; the dossier did not establish comparative limits or prices for the providers mentioned here. [ScreenshotOne bulk screenshots]
Estimate cost from the number of pages, how often you recapture them, and whether you need multiple viewports or full-page variants. Verify current provider pricing and limits directly before committing; the research gathered here did not establish current ScreenshotOne or Urlbox prices or comparative cost. With ScreenshotNeo, the stated plans are Free for 1,000 shots/month, Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free.
9. FAQ
How many competitor pages should I capture in one batch?
There is no universal batch size in the research. Follow the API’s current limits and concurrency guidance, or start with a small group and scale in controlled waves.
Should I capture both viewport and full-page screenshots?
Capture both when the comparison needs to cover the initial impression and the complete page. Keep them as separate variants so readers can tell which view they are seeing.
Can I compare screenshots captured on different days?
Yes, but include the capture date and context in the manifest. Promotions, experiments, and live content can change, so date differences may explain visual changes.
Does a screenshot prove what every visitor sees?
No. It documents one rendered result under one set of conditions. Location, device, cookies, timing, and dynamic page behavior can produce other results.


