Screenshot -- Bulk Screenshots
Capture multiple screenshots in a single API request.
Base URL: https://api.nodium.io/api/v1/screenshot
Table of Contents
- Default Options - Per-Request Overrides
- Lazy Mode (default) - Eager Mode (execute=true)
Overview
The /bulk endpoint lets you submit an array of screenshot configurations in one request instead of making separate API calls for each screenshot. This is useful for:
- Site audits -- capture every page on a website in one call
- Competitive monitoring -- screenshot multiple competitor pages at once
- Multi-device testing -- capture the same URL across different viewport devices
- Batch thumbnail generation -- create thumbnails for a list of URLs
- Regression testing -- capture a set of pages before and after a deploy
Endpoint
POST /bulk -- Batch Screenshots
POST https://api.nodium.io/api/v1/screenshot/bulkThe bulk endpoint only accepts POST requests with a JSON body.
Request Format
The request body is a JSON object with the following structure:
{
"access_key": "YOUR_API_KEY",
"execute": true,
"optimize": true,
"options": {
"format": "png",
"viewport_width": 1280,
"viewport_height": 1024,
"block_ads": true
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.com/about" },
{ "url": "https://example.com/pricing" }
]
}Default Options
The options object defines default parameters applied to every item in the requests array. Any valid screenshot parameter from the /take endpoint can be used here:
{
"options": {
"format": "png",
"viewport_width": 1440,
"viewport_height": 900,
"block_ads": true,
"block_cookie_banners": true,
"dark_mode": true,
"full_page": true
}
}Per-Request Overrides
Each item in the requests array can override the defaults from options. Only the url (or html/markdown) source parameter is required per item:
{
"options": {
"format": "png",
"viewport_width": 1280
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.com/about", "format": "jpeg", "image_quality": 90 },
{ "url": "https://example.com/pricing", "viewport_device": "iphone_16_pro" },
{ "url": "https://example.com/blog", "full_page": true, "dark_mode": true }
]
}In this example:
- The first request uses all defaults (PNG, 1280px wide)
- The second request overrides the format to JPEG with custom quality
- The third request uses iPhone 16 Pro device emulation (overriding the viewport width)
- The fourth request adds full-page capture and dark mode
Response Format
The response format depends on the execute parameter.
Lazy Mode (default)
When execute is false (or omitted), the API returns an array of pre-signed URLs. Each URL, when accessed, triggers the screenshot capture:
[
{ "url": "https://api.nodium.io/api/v1/screenshot/take?..." },
{ "url": "https://api.nodium.io/api/v1/screenshot/take?..." },
{ "url": "https://api.nodium.io/api/v1/screenshot/take?..." }
]Lazy mode is useful when:
- You want to defer the actual rendering (e.g. loading screenshots on demand in a UI)
- You want to distribute the URLs to different consumers
- You need to control when each screenshot is captured
Eager Mode (execute=true)
When execute=true, all screenshots are captured immediately and the results are returned in the response:
[
{
"url": "https://api.nodium.io/api/v1/screenshot/take?...",
"response": {
"is_successful": true,
"status": 200,
"body": "<base64-encoded screenshot>"
}
},
{
"url": "https://api.nodium.io/api/v1/screenshot/take?...",
"response": {
"is_successful": true,
"status": 200,
"body": "<base64-encoded screenshot>"
}
},
{
"url": "https://api.nodium.io/api/v1/screenshot/take?...",
"response": {
"is_successful": false,
"status": 500,
"body": "{\"error_code\":\"timeout_error\",\"message\":\"Operation timed out\"}"
}
}
]Each item in the response includes:
| Field | Type | Description |
|---|---|---|
url | string | The constructed screenshot URL |
response.is_successful | boolean | Whether the screenshot was captured successfully |
response.status | integer | HTTP status code of the individual screenshot request |
response.body | string | Base64-encoded screenshot data (on success) or JSON error (on failure) |
Optimize Mode
When optimize=true (requires execute=true), the API optimizes requests that target the same URL with different parameters. For example, capturing the same page at multiple viewport sizes will load the page once and render at each size, reducing total processing time.
Maximum Batch Size
| Limit | Value |
|---|---|
| Maximum items per request | 100 |
| Recommended for synchronous | 10 |
| Recommended for async | Up to 100 |
For batches larger than 10 items, use async mode to avoid HTTP timeout issues. For batches larger than 100, split them into multiple requests.
Async Mode for Bulk
For large batches, combine bulk with async mode. The API accepts the batch immediately and delivers results via webhook when all items are complete:
{
"access_key": "YOUR_API_KEY",
"execute": true,
"async": true,
"webhook_url": "https://hooks.example.com/bulk-complete",
"external_identifier": "batch-2026-03-05",
"options": {
"format": "png"
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.com/about" },
{ "url": "https://example.com/pricing" },
{ "url": "https://example.com/blog" },
{ "url": "https://example.com/contact" }
]
}When async mode is enabled:
- The API returns
202 Acceptedimmediately - All screenshots in the batch are processed in the background
- When the entire batch is complete, a webhook is sent to your
webhook_url - Use
external_identifierto correlate the webhook with your batch
Recommendation: Always use async mode for bulk requests with more than 10 items. See Async & Webhooks for full details on webhook configuration and security.
Rate Limiting
Bulk requests are subject to your plan's concurrency limits. Each item in the batch counts as one concurrent request while it is being processed.
| Consideration | Details |
|---|---|
| Concurrency | Each item uses one concurrent slot during processing |
| Throttling | If your concurrency limit is reached, remaining items are queued |
| Timeout | The global timeout applies to the entire batch, not individual items |
| Async advantage | Async mode processes items as concurrency slots become available |
Check your current concurrency limits with the /usage endpoint:
curl "https://api.nodium.io/api/v1/screenshot/usage?access_key=YOUR_API_KEY"Cost
Each item in a bulk request costs 1 credit, the same as a single /take request. There is no additional cost or discount for using bulk.
| Scenario | Credits Used |
|---|---|
| Bulk with 5 items | 5 |
| Bulk with 50 items | 50 |
| Bulk with 100 items | 100 |
| Failed items | 0 (not charged) |
Cached items (cache=true in options) | 0 (cache hits are free) |
Parameters Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
execute | boolean | false | true = execute all screenshots before responding (eager). false = return URLs that trigger capture on access (lazy). |
optimize | boolean | false | Optimize when multiple requests target the same URL with different parameters. Requires execute=true. |
options | object | -- | Default screenshot parameters applied to every item in the batch. Accepts any valid /take parameter. |
requests | array | -- | Array of individual screenshot parameter objects. Each can override the defaults from options. |
async | boolean | false | Run the entire batch asynchronously. Returns 202 Accepted immediately. |
webhook_url | string | -- | URL to POST results to when the batch completes (requires async=true). |
external_identifier | string | -- | Custom identifier for correlating webhook callbacks. |
Code Examples
cURL
# Lazy mode -- returns URLs for on-demand capture
curl -X POST "https://api.nodium.io/api/v1/screenshot/bulk" \
-H "X-Access-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{
"options": {
"format": "png",
"viewport_width": 1280,
"block_ads": true
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.com/about" },
{ "url": "https://example.com/pricing" }
]
}'
# Eager mode -- captures all screenshots immediately
curl -X POST "https://api.nodium.io/api/v1/screenshot/bulk" \
-H "X-Access-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{
"execute": true,
"optimize": true,
"options": {
"format": "png",
"viewport_width": 1280,
"block_ads": true,
"block_cookie_banners": true
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.com/about" },
{ "url": "https://example.com/pricing" }
]
}'
# Async bulk with webhook
curl -X POST "https://api.nodium.io/api/v1/screenshot/bulk" \
-H "X-Access-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{
"execute": true,
"async": true,
"webhook_url": "https://hooks.example.com/bulk-done",
"external_identifier": "site-audit-001",
"options": {
"format": "png",
"full_page": true
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.com/about" },
{ "url": "https://example.com/pricing" },
{ "url": "https://example.com/blog" },
{ "url": "https://example.com/contact" }
]
}'
# Multi-device capture of the same URL
curl -X POST "https://api.nodium.io/api/v1/screenshot/bulk" \
-H "X-Access-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{
"execute": true,
"optimize": true,
"options": {
"url": "https://example.com",
"format": "png"
},
"requests": [
{ "viewport_device": "desktop_1080p" },
{ "viewport_device": "macbook_air" },
{ "viewport_device": "ipad_air" },
{ "viewport_device": "iphone_16_pro" },
{ "viewport_device": "galaxy_s24" }
]
}'Node.js
const fs = require("fs");
async function bulkScreenshots() {
const response = await fetch(
"https://api.nodium.io/api/v1/screenshot/bulk",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Access-Key": "YOUR_API_KEY",
},
body: JSON.stringify({
execute: true,
optimize: true,
options: {
format: "png",
viewport_width: 1280,
viewport_height: 1024,
block_ads: true,
block_cookie_banners: true,
},
requests: [
{ url: "https://example.com" },
{ url: "https://example.com/about" },
{ url: "https://example.com/pricing" },
],
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`${error.error_code}: ${error.message}`);
}
const results = await response.json();
results.forEach((result, index) => {
if (result.response.is_successful) {
const buffer = Buffer.from(result.response.body, "base64");
fs.writeFileSync(`screenshot-${index}.png`, buffer);
console.log(`Screenshot ${index} saved (${buffer.length} bytes)`);
} else {
console.error(`Screenshot ${index} failed: ${result.response.body}`);
}
});
}
bulkScreenshots();Python
import base64
import json
import requests
def bulk_screenshots():
response = requests.post(
"https://api.nodium.io/api/v1/screenshot/bulk",
headers={"X-Access-Key": "YOUR_API_KEY"},
json={
"execute": True,
"optimize": True,
"options": {
"format": "png",
"viewport_width": 1280,
"viewport_height": 1024,
"block_ads": True,
"block_cookie_banners": True,
},
"requests": [
{"url": "https://example.com"},
{"url": "https://example.com/about"},
{"url": "https://example.com/pricing"},
],
},
)
if response.status_code != 200:
error = response.json()
print(f"Error: {error['error_code']} - {error['message']}")
return
results = response.json()
for index, result in enumerate(results):
if result["response"]["is_successful"]:
image_data = base64.b64decode(result["response"]["body"])
with open(f"screenshot-{index}.png", "wb") as f:
f.write(image_data)
print(f"Screenshot {index} saved ({len(image_data)} bytes)")
else:
print(f"Screenshot {index} failed: {result['response']['body']}")
bulk_screenshots()Best Practices
- Use
optionsfor shared settings. Put common parameters (format, viewport, blocking) inoptionsand only override what differs per request. - Enable
optimize=truewhen multiple requests target the same URL. The API loads the page once and renders at each configuration, reducing total time. - Use async mode for large batches. Synchronous bulk requests with more than 10 items risk timing out. Async mode processes items in the background.
- Monitor your concurrency limits. Each bulk item uses a concurrent slot. If you hit the limit, items are queued but the total request time increases.
- Handle partial failures. In eager mode, check
is_successfulfor each item. Some items may fail while others succeed. - Split very large jobs. The maximum batch size is 100. For larger jobs, split them into multiple requests and use
external_identifierto track each batch. - Combine with caching. Set
cache=trueinoptionsto cache all results. Subsequent identical batches return cached results at 0 credits. - Use lazy mode for deferred rendering. If you do not need all screenshots immediately, lazy mode returns URLs that trigger capture on first access, spreading the load over time.