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)

- cURL - Node.js - Python


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/bulk

The bulk endpoint only accepts POST requests with a JSON body.


Request Format

The request body is a JSON object with the following structure:

json
{
  "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:

json
{
  "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:

json
{
  "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:

json
[
  { "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:

json
[
  {
    "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:

FieldTypeDescription
urlstringThe constructed screenshot URL
response.is_successfulbooleanWhether the screenshot was captured successfully
response.statusintegerHTTP status code of the individual screenshot request
response.bodystringBase64-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

LimitValue
Maximum items per request100
Recommended for synchronous10
Recommended for asyncUp 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:

json
{
  "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:

  1. The API returns 202 Accepted immediately
  2. All screenshots in the batch are processed in the background
  3. When the entire batch is complete, a webhook is sent to your webhook_url
  4. Use external_identifier to 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.

ConsiderationDetails
ConcurrencyEach item uses one concurrent slot during processing
ThrottlingIf your concurrency limit is reached, remaining items are queued
TimeoutThe global timeout applies to the entire batch, not individual items
Async advantageAsync mode processes items as concurrency slots become available

Check your current concurrency limits with the /usage endpoint:

bash
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.

ScenarioCredits Used
Bulk with 5 items5
Bulk with 50 items50
Bulk with 100 items100
Failed items0 (not charged)
Cached items (cache=true in options)0 (cache hits are free)

Parameters Reference

ParameterTypeDefaultDescription
executebooleanfalsetrue = execute all screenshots before responding (eager). false = return URLs that trigger capture on access (lazy).
optimizebooleanfalseOptimize when multiple requests target the same URL with different parameters. Requires execute=true.
optionsobject--Default screenshot parameters applied to every item in the batch. Accepts any valid /take parameter.
requestsarray--Array of individual screenshot parameter objects. Each can override the defaults from options.
asyncbooleanfalseRun the entire batch asynchronously. Returns 202 Accepted immediately.
webhook_urlstring--URL to POST results to when the batch completes (requires async=true).
external_identifierstring--Custom identifier for correlating webhook callbacks.

Code Examples

cURL

bash
# 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

javascript
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

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 options for shared settings. Put common parameters (format, viewport, blocking) in options and only override what differs per request.
  • Enable optimize=true when 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_successful for 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_identifier to track each batch.
  • Combine with caching. Set cache=true in options to 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.