Best Screenshot API for Bulk URL Screenshots
Compare screenshot APIs for bulk URL capture, understand batch and billing limits, and choose a workflow that fits your pages and volume.
Short answer: ScreenshotNeo is the first API to try for bulk URL screenshots when you want a simple capture workflow, clean pages, and clear billing outcomes. It supports bulk capture for up to 100 URLs per call, and only clean screenshots are billed. Its free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. See ScreenshotNeo and its API documentation.
For other workloads, ScreenshotOne documents a dedicated bulk endpoint, Urlbox documents synchronous and asynchronous rendering workflows, and Browserless is relevant when screenshots are part of a broader browser automation flow. No evidence here establishes a universally fastest or most reliable provider. Test the same representative URLs and compare successful output, queue behavior, latency, and billed usage before committing.
What to check before choosing a bulk screenshot API
A batch endpoint is only one part of a bulk workflow. Define these details before comparing plans:
- Volume: URLs per batch, screenshots per month, and the largest expected import.
- Peak rate: how many captures must start in a minute or second, and whether they can be queued.
- Page behavior: JavaScript rendering, lazy-loaded images, long pages, authentication, cookies, and consent prompts.
- Output: PNG, JPEG, WebP, or PDF; full page or viewport; required resolution and file size.
- Delivery: synchronous responses, job IDs, webhooks, artifact URLs, and how long outputs remain available.
- Cost unit: successful render, request, file-size or duration increment, cache miss, or another unit.
- Failure policy: how timeouts, blocked pages, invalid options, and rate limits are reported and billed.
Estimate cost from the workload you expect to run, not the largest monthly screenshot number in a plan table. Slow, image-heavy pages and advanced rendering options can consume more resources or billing units on some services.
Screenshot API comparison for bulk capture
ScreenshotNeo ranks first here because its clean-capture behavior, no-charge outcomes for failed or non-clean captures, bulk capture, and entry-level pricing address common bulk workflow concerns. The other services below may fit better when their particular queue model or browser controls match your system.
| Service | Bulk workflow | Limits and billing details to examine | Good fit when |
|---|---|---|---|
| 1. ScreenshotNeo | Bulk capture supports up to 100 URLs per call. It also offers synchronous single-URL capture and async jobs with signed webhooks. | Only clean shots are billed; bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Free: 1,000/month; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free. All features are on every plan. | You want clean screenshots, explicit page verdict and billing headers, bulk capture, and a low-cost way to start. |
| 2. ScreenshotOne | Its documented bulk endpoint accepts an array of requests. By default, captures are lazy: the screenshot is taken when the returned screenshot URL is downloaded. Set execute to true to execute requests before the bulk response returns. |
Bulk calls use the same one-minute request bucket as regular screenshot calls. Its documentation recommends checking remaining concurrency and reset values before draining a large queue. The pricing page accessed in 2026 listed 100 free screenshots/month; Basic at $17/month for 2,000 and 40 requests/minute; Growth at $79 for 10,000 and 80/minute; Scale at $259 for 50,000 and 150/minute. It says successful non-cached renders count, with cache misses potentially counting again. Recheck current plan details. | You need an explicitly documented multi-request endpoint and can schedule work around its documented request bucket. |
| 3. Urlbox | It documents synchronous and asynchronous JSON API requests, render links, and batch-processing and webhook guides. | Pricing depends on the applicable plan and render mechanics. Its pricing page says each 30-second period of request duration and each 5 MB file-size period uses a render; GPU acceleration uses twice the renders of non-GPU requests, and some advanced features use additional renders. Failed requests are not charged, and cached Render Link screenshots do not count against quota under the stated conditions. Recheck route and plan details. | You need its rendering and artifact workflows and can model costs using duration, output size, and options. |
| 4. Browserless | The reviewed screenshot documentation describes a screenshot API, but does not establish a dedicated bulk endpoint. | Supports screenshot controls such as full-page capture, viewport, clipping, selectors, waits, and scroll behavior. Confirm the current plan’s concurrency and queue handling for your workload. | You need browser interaction controls alongside screenshot capture and are prepared to orchestrate batches yourself. |
These plan figures are vendor-published snapshots accessed in 2026, not a fixed price guarantee. Confirm live prices, quotas, endpoints, and terms before purchase. The figures have different units and billing rules, so the table is not a direct price-per-identical-render benchmark.
How bulk requests, queues, and rate limits work
Batch size is different from throughput
A request that contains 100 URLs can reduce client-side overhead, but it does not mean all 100 pages will render at once. The service may queue work, apply concurrency limits, or count every URL against a rate bucket. Find out whether the response means “accepted,” “started,” or “finished.”
Understand lazy and eager execution
With ScreenshotOne’s documented default bulk behavior, the bulk response can contain screenshot URLs before the captures run. The actual capture occurs when those URLs are downloaded. Its execute: true option runs captures before returning the response with a status summary. Account for that distinction when designing monitoring: receiving URLs is not proof that every render has completed.
Read the limit’s unit
Request-per-minute, concurrency, requests-per-second, and burst limits describe different constraints. ScreenshotOne says bulk calls consume the same one-minute bucket as normal calls and advises checking remaining concurrency and reset information before submitting a large queue. ApiFlash documents a leaky-bucket limit of 20 requests per second with a burst size of 400; excess traffic is delayed until the burst limit is exceeded, after which requests receive HTTP 429. This is a published limit, not a measured throughput result.
Build a queue with a configurable worker limit. On a rate-limit response, honor any retry guidance, wait with exponential backoff and jitter, and cap retries. Do not repeatedly resend a whole batch if the API can have already completed some URLs.
Run a representative evaluation before migrating
- Choose a test set. Include ordinary pages, JavaScript-heavy pages, long pages, lazy-loaded images, authenticated pages if needed, and pages with consent banners.
- Fix capture settings. Use the same viewport, full-page behavior, output format, wait rule, and authentication assumptions across providers.
- Record per-URL outcomes. Track HTTP status, provider verdict, whether the page is complete, output bytes, and elapsed time.
- Test the queue. Increase concurrency gradually and record accepted, completed, delayed, and rate-limited work.
- Test retries and idempotency. Simulate a client timeout after submission. Determine whether retrying creates a duplicate capture or can retrieve the original job.
- Calculate actual cost. Compare the billed amount with successful, unique outputs, including cache behavior and any duration or file-size multipliers.
- Repeat at expected peak load. A small serial test does not reveal queue or burst behavior.
This evaluation is more useful than a generic speed ranking: provider behavior depends on the pages, capture options, and workload you actually send.
DIY bulk capture with ScreenshotNeo
ScreenshotNeo provides a bulk capture feature for up to 100 URLs per call. The following examples show a simple client-side queue that sends one request per URL with a bounded concurrency. They use the documented single-shot API call so the capture parameters are explicit. For the exact bulk request schema and other options, use the ScreenshotNeo API documentation.
cURL: one URL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python: bounded concurrent URL queue
import concurrent.futures
import os
from pathlib import Path
from urllib.parse import urlparse
import requests
API_KEY = os.environ["SCREENSHOTNEO_API_KEY"]
URLS = ["https://stripe.com", "https://example.com"]
OUT = Path("screenshots")
OUT.mkdir(exist_ok=True)
def capture(url):
response = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": API_KEY, "url": url},
timeout=90,
)
response.raise_for_status()
name = (urlparse(url).hostname or "page").replace(".", "_")
path = OUT / f"{name}.webp"
path.write_bytes(response.content)
return url, path, response.headers.get("X-Page-Verdict"), response.headers.get("X-Billed")
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool:
for result in pool.map(capture, URLS):
print(result)
Install the dependency with python -m pip install requests, set SCREENSHOTNEO_API_KEY in the environment, then run the script. The worker count is an example, not a provider throughput guarantee. Set it to suit your workload and current account limits.
Node.js: bounded concurrent URL queue
import { mkdir, writeFile } from 'node:fs/promises';
const accessKey = process.env.SCREENSHOTNEO_API_KEY;
if (!accessKey) throw new Error('Set SCREENSHOTNEO_API_KEY');
const urls = ['https://stripe.com', 'https://example.com'];
const outputDir = 'screenshots';
await mkdir(outputDir, { recursive: true });
async function capture(url) {
const q = new URLSearchParams({ access_key: accessKey, url });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`, {
signal: AbortSignal.timeout(90_000),
});
if (!res.ok) throw new Error(`${url}: HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
const host = new URL(url).hostname.replaceAll('.', '_');
await writeFile(`${outputDir}/${host}.webp`, bytes);
return {
url,
verdict: res.headers.get('X-Page-Verdict'),
billed: res.headers.get('X-Billed'),
};
}
const concurrency = 5;
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(urls[index]);
}
}
await Promise.all(Array.from({ length: Math.min(concurrency, urls.length) }, worker));
console.log(results);
Save as an ES module and run with Node.js. For production, persist each URL’s state and output path so a process restart does not require resubmitting completed work. The API key is sent as a query parameter in the documented example; avoid logging full request URLs.
cURL loop for a small list
while IFS= read -r url; do
[ -z "$url" ] && continue
name=$(printf '%s' "$url" | sed 's#https\?://##; s#[/?&]#_#g')
curl --fail --silent --show-error -G \
"https://api.screenshotneo.com/v1/shot" \
-d access_key="$SCREENSHOTNEO_API_KEY" \
--data-urlencode "url=$url" \
-o "${name}.webp" || printf 'Capture failed: %s\n' "$url" >&2
done < urls.txt
For a large list, use a queue with bounded concurrency and per-URL retry state rather than starting an unbounded background process for every line.
Or skip the browser setup
ScreenshotNeo accepts a URL and returns a screenshot with one API request:
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 API documentation for capture parameters. ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the capture; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Start with 1,000 free screenshots a month, no card required.
Capture options that matter at scale
Apply only the options that improve the output you need. Extra waits, full-page rendering, high pixel density, and large output dimensions can increase capture time and transferred bytes.
| Need | Relevant control | Bulk consideration |
|---|---|---|
| Long page | Full-page capture; lazy-image loading | Long documents can take longer and produce larger files. |
| One component | CSS selector or element capture | Check behavior when the selector is missing or matches multiple nodes. |
| Dynamic content | Wait for selector, delay, or network idle | A fixed delay may waste time on fast pages and still be too short for slow pages. |
| Authenticated pages | Cookies, custom headers, user agent, or Authorization | Keep secrets out of logs and avoid sharing signed capture URLs. |
| Consistent display | Viewport/device preset, retina scale, timezone, geolocation, dark mode | Use consistent settings across the batch so outputs can be compared. |
| Smaller files | WebP or JPEG, resizing, viewport capture | Balance file size against text and image legibility. |
| Remove page clutter | Hide selectors, click an element, block ads/trackers/requests/resource types, custom CSS/JavaScript | Check that blocked resources are not required for the target content. |
| PDF output | Paper size, margins, landscape, page ranges | PDF generation is a different output path; validate pagination and page count. |
| Public previews | Signed links | Use expiring or controlled access for pages that should not be public. |
| Repeat captures | Cache with a chosen TTL | Cache hits reduce duplicate work; compare freshness needs with cost. |
ScreenshotNeo also supports HTML/CSS-to-image, async jobs with signed webhooks, bulk capture up to 100 URLs per call, usage API, and an OpenAPI spec. It accepts parameter names used by other screenshot APIs to ease migrations. See the docs for exact parameter names and response shapes.
Reliability, performance, and cost planning
Reliability
- Store a stable record for each input URL and capture configuration.
- Separate submission failures from render failures and successful output delivery.
- Retry transient network errors and timeouts with backoff; do not retry permanent invalid-URL or invalid-option errors unchanged.
- Make retries idempotent where the provider supports it. Otherwise, keep a deduplication key in your own job store.
- For asynchronous jobs, verify webhook signatures, handle duplicate deliveries, and provide a polling or reconciliation path.
- Keep a small sample of outputs for visual inspection when changing browser settings or provider.
Performance
Throughput depends on target-site response time, page complexity, capture options, provider concurrency, and file transfer. A useful measure is completed usable screenshots per minute, alongside median and tail latency. Report the number of failed or incomplete pages too; raw request rate can look good while useful output is poor.
Use bounded concurrency, reuse a job queue, and avoid requesting duplicate URLs with identical settings when caching can serve the need. For pages with lazy-loaded content, ensure scrolling or the provider’s relevant lazy-load behavior is enabled. Browserless documentation notes that lazy-loaded content may render only after it scrolls into view.
Cost
Use this estimate as a starting point:
monthly cost = plan cost + overage + option-specific render charges
Then adjust the estimate for expected unique URLs, retry rate, cache hits or misses, average output size, page duration, and required advanced options. ScreenshotOne’s published pricing excludes unsuccessful renders and cached results subject to its cache-miss caveat. Urlbox describes duration, file-size, GPU, and advanced-feature render multipliers. ScreenshotNeo bills only clean shots and does not bill bot checks, CAPTCHAs, blank pages, timeouts, failed loads, or cache hits, according to its product details.
Troubleshooting bulk screenshot jobs
| Symptom | Likely cause | What to do |
|---|---|---|
| HTTP 429 or delayed submissions | Request bucket, concurrency, or burst limit reached. | Reduce worker count, queue the remaining URLs, and honor reset or retry guidance. Check whether limits apply per request or per URL. |
| Bulk response succeeds but images are missing | Submission was accepted but captures are lazy or asynchronous. | Download returned screenshot URLs when required, or poll the job/webhook flow until each capture is complete. |
| Some pages are blank | Navigation timeout, bot check, consent interstitial, or page content not ready. | Inspect status and page-verdict data, test a longer or condition-based wait, and determine whether the target page blocks automated browsers. |
| Images or below-fold sections are absent | Lazy loading did not trigger before capture. | Use full-page mode with lazy-image loading or scroll behavior where available; verify the resulting screenshot. |
| Selector capture returns no useful output | Selector is absent, delayed, or ambiguous. | Wait for the selector, verify it against the rendered page, and define behavior for zero or multiple matches. |
| Repeated captures cost more than expected | Cache miss, changed settings, short TTL, or provider-specific duration/file-size multipliers. | Review cache keys and TTL, compare all capture parameters, and estimate using the plan’s actual billing unit. |
| Downloads are truncated or invalid | Client timeout, interrupted transfer, or an error body saved as an image. | Check HTTP status and content type before saving; use an appropriate timeout and retry only the affected URL. |
| Duplicate screenshots after retry | Client lost the response after the service completed the job. | Persist job IDs or a URL/configuration key and reconcile status before resubmitting. |
| API key appears in logs | Full request URL was logged while the key was a query parameter. | Redact query strings and keep keys in environment-backed secret storage; rotate a key if it was exposed. |
FAQ
How do I take screenshots of multiple URLs?
Use a documented batch endpoint if its queue and result semantics fit your workflow, or submit individual captures through a bounded worker queue. Store each URL’s status so retries do not restart completed work.
Which screenshot API can handle bulk requests?
ScreenshotNeo supports bulk capture for up to 100 URLs per call. ScreenshotOne documents an array-based bulk endpoint. Urlbox documents sync and async workflows and batch-processing guidance. Browserless can support screenshot automation, but the reviewed documentation does not establish a dedicated bulk endpoint.
Should every URL use the same wait time?
Use a shared baseline when pages behave consistently. For mixed sites, condition-based waits or URL-specific settings can avoid wasting time on fast pages while still allowing dynamic pages to finish.
Is a screenshot cache always safe?
No. Cache only when the URL and relevant capture settings represent the same desired output and the content freshness window is acceptable. Personalized or frequently changing pages may need a short TTL or no cache.
Can I compare vendors by requests per second?
Only if the limit units and test conditions match. A request rate does not account for page completion, queue depth, capture success, or billing. Compare completed usable images and cost on your own URL set.
Recommendation
Try ScreenshotNeo first if your bulk job benefits from clean shots, explicit billed-versus-not-billed outcomes, and a free starting allowance. Use ScreenshotOne when its bulk endpoint’s lazy or eager execution behavior fits your pipeline. Consider Urlbox when its render-link and asynchronous workflows suit delivery needs, and Browserless when screenshot capture must sit alongside browser automation. In every case, validate the same target URLs, concurrency, output settings, and cost model before routing production volume.
