ScreenshotNeo

BlogHow-to

Screenshotlayer Bulk Screenshots: Capture Multiple URLs with a Script

Screenshotlayer’s documented workflow captures URLs one request at a time. Use a script to queue, limit, and retry independent screenshot jobs.

By the ScreenshotNeo team4 October 202610 min read

Direct answer: The Screenshotlayer material reviewed for this guide documents one target URL per capture request, not a batch endpoint that accepts an array of URLs. To capture multiple URLs, read them from a file and submit one request for each, recording each result independently so a failure does not stop the rest. You can run jobs sequentially or cap concurrency to match your account’s worker capacity.

The examples below are implementation patterns based on Screenshotlayer’s documented request parameters; they have not been executed against a live account. Check the current API documentation and account terms before processing a large list because service behavior, quotas, and prices can change.

What you need

  • A Screenshotlayer access key and the documented /api/capture endpoint.
  • A text file containing one fully qualified URL per line. Each URL must include https:// or http://.
  • A destination directory with enough space for the resulting images, or an export destination if using the API’s documented export option.

Keep the access key in an environment variable or secret store instead of committing it to source control. The API documentation describes the key as a personal password. The examples use an environment variable named SCREENSHOTLAYER_ACCESS_KEY.

Build a bulk workflow in Python

This script reads urls.txt, validates URL schemes, sends one capture request per URL, saves successful image responses, and writes a JSON Lines report. A failed URL is recorded without preventing later URLs from being processed. It runs sequentially, which is the simplest way to stay within a plan’s worker capacity.

import json
import os
import re
import sys
from pathlib import Path
from urllib.parse import urlparse

import requests

ENDPOINT = "https://api.screenshotlayer.com/api/capture"
API_KEY = os.environ.get("SCREENSHOTLAYER_ACCESS_KEY")
INPUT_FILE = Path("urls.txt")
OUTPUT_DIR = Path("screenshots")
REPORT_FILE = Path("screenshot-results.jsonl")

# Screenshotlayer documents these request options. Adjust them for your job.
OPTIONS = {
    "fullpage": "1",
    "format": "PNG",
    "viewport": "1440x900",
    # "width": "1200",
    # "delay": "2",
    # "ttl": "86400",
    # "force": "1",
    # "user_agent": "Your user agent string",
    # "accept_lang": "en-US,en",
    # "css_url": "https://example.com/print.css",
    # "secret_key": "YOUR_SECRET_KEY",
    # "export": "...",
}


def valid_url(value: str) -> bool:
    parsed = urlparse(value)
    return parsed.scheme in {"http", "https"} and bool(parsed.netloc)


def filename_for(url: str, index: int) -> str:
    # Index avoids collisions when different URLs have the same final path.
    host = urlparse(url).netloc.replace(":", "_")
    safe_host = re.sub(r"[^A-Za-z0-9._-]", "_", host)
    return f"{index:04d}-{safe_host}.png"


def main() -> int:
    if not API_KEY:
        print("Set SCREENSHOTLAYER_ACCESS_KEY before running.", file=sys.stderr)
        return 2
    if not INPUT_FILE.exists():
        print(f"Input file not found: {INPUT_FILE}", file=sys.stderr)
        return 2

    urls = [line.strip() for line in INPUT_FILE.read_text().splitlines()]
    urls = [line for line in urls if line and not line.startswith("#")]
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

    with REPORT_FILE.open("w", encoding="utf-8") as report:
        for index, url in enumerate(urls, start=1):
            result = {"url": url, "status": "failed"}
            if not valid_url(url):
                result["error"] = "URL must include http:// or https:// and a host"
                report.write(json.dumps(result) + "\n")
                continue

            params = {"access_key": API_KEY, "url": url, **OPTIONS}
            try:
                response = requests.get(ENDPOINT, params=params, timeout=120)
                content_type = response.headers.get("Content-Type", "")
                if response.ok and content_type.startswith("image/"):
                    path = OUTPUT_DIR / filename_for(url, index)
                    path.write_bytes(response.content)
                    result.update({"status": "ok", "file": str(path), "http_status": response.status_code})
                else:
                    # Screenshotlayer failures may return structured error data.
                    try:
                        result["error"] = response.json()
                    except ValueError:
                        result["error"] = response.text[:1000]
                    result["http_status"] = response.status_code
            except requests.RequestException as exc:
                result["error"] = str(exc)
            report.write(json.dumps(result) + "\n")

    print(f"Processed {len(urls)} URLs. Results: {REPORT_FILE}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Install the dependency with python -m pip install requests, put one URL per line in urls.txt, set the environment variable, then run python capture_many.py. For example, in a POSIX shell:

export SCREENSHOTLAYER_ACCESS_KEY='your_access_key'
python capture_many.py

The script treats successful image responses as image files. If you choose the API’s export option, check the current response format and adapt the success handling to store the returned export location or data rather than assuming the body is an image.

Minimal cURL request

Each URL needs its own request. This example captures one URL; a shell loop can repeat it, but use a script with per-item error handling and a results log for longer lists.

curl --get 'https://api.screenshotlayer.com/api/capture' \
  --data-urlencode 'access_key=YOUR_ACCESS_KEY' \
  --data-urlencode 'url=https://example.com' \
  --data-urlencode 'fullpage=1' \
  --data-urlencode 'format=PNG' \
  --output example.png

Run sequentially or with limited concurrency

Sequential processing sends the next URL after the previous request finishes. It is easier to reason about, naturally limits simultaneous work to one request, and makes failures straightforward to associate with an input item.

Screenshotlayer’s FAQ describes dedicated workers as server entities assigned to capture screenshots, with one screenshot per worker at a time; worker capacity depends on the account plan. If you add concurrency, set a small explicit limit that fits your subscribed capacity. The sources provide no latency or throughput measurements, so do not infer a completion-time estimate from worker count alone.

For a large list, persist a job manifest and mark each URL pending, succeeded, or failed. Resume only failed or pending items after an interruption. Avoid retrying every successful URL: the documented default cache period is 2,592,000 seconds (30 days), and caching affects repeat-run behavior. The API also documents a custom ttl and a force option for refresh behavior.

Screenshotlayer request options

These are controls documented for an individual capture request, not bulk-level settings. Supply only options relevant to your use case, and confirm accepted values and current behavior in the provider’s specification.

Parameter Purpose Considerations
access_key Required account credential. Keep it out of source control and logs.
url Required target page. Include the protocol, such as https://.
fullpage Request a full-page capture. Long pages may take longer and produce larger files.
width Set capture width. Use with the documented viewport behavior for the desired layout.
viewport Set viewport dimensions. Check the required format in current documentation.
format Choose image output format. Make the filename extension match the actual response format.
delay Wait before capture. A fixed delay can help with rendering that completes after initial load, but adds time to every request.
ttl Set cache lifetime. The documented default is 2,592,000 seconds; verify live behavior before depending on it.
force Request a forced refresh. Use when a fresh capture is needed instead of a cached result.
user_agent Set the browser user agent. Some sites render differently for different agents.
accept_lang Set the accepted language. Useful when pages vary by locale.
css_url Apply an external CSS resource. Ensure the resource is reachable to the rendering service.
secret_key Optional request security parameter. Follow the provider’s signing instructions; do not guess its construction.
placeholder Configure placeholder behavior. Consult the specification for supported values and response implications.
export Use the documented export option. Check whether the response is an image or an export result before saving it.

Validate inputs and plan the run

  1. Normalize the input list: trim whitespace, ignore blank lines and comments, and remove accidental duplicates if duplicate captures are not intended.
  2. Validate every URL before spending requests. Screenshotlayer requires a fully qualified URL with its HTTP protocol.
  3. Choose capture settings once, then apply them consistently. Keep an input manifest so the output can be traced to its source URL.
  4. Run a small sample first and inspect file types, dimensions, and page content before queuing the full list.
  5. Check plan allowance and overage terms before a large run. Screenshotlayer’s FAQ states that usage notifications occur at 75%, 90%, and 100% of the monthly allowance and that overage fees apply after 100%; recheck current account terms.
  6. Use conservative concurrency based on the worker capacity included in your plan. No throughput benchmark is available in the research for this guide.

Reliability, performance, and cost

Reliability

Make each URL an independent job. Record the URL, status, HTTP result, error payload, and saved path or export reference. The specification describes structured errors with an error code, type, and explanatory info field, and lists failures such as missing or invalid access keys, invalid API function, usage limit reached, and invalid URL.

Retry transient network failures with a bounded retry policy and a delay between attempts. Do not endlessly retry invalid URLs, authentication failures, or usage-limit errors; fix the input, credential, or account limit first. Write output to a temporary file and rename it only after a complete response has been received to avoid treating a partial download as a finished screenshot.

Performance

Total run time depends on page rendering, capture options, network conditions, cache behavior, and available workers. Full-page capture, added delay, and resource-heavy pages can increase work. Concurrency may improve throughput when worker capacity is available, but aggressive parallel requests can exceed plan capacity. Measure a representative small run in your own account before scheduling a larger batch.

Cost

Budget by request count and your current plan’s monthly allowance and overage terms. A script that loops over 500 URLs issues 500 capture requests; the reviewed specification does not establish a native batch request that makes those URLs one unit. Screenshotlayer’s official pricing pages surfaced Free at 100 monthly snapshots, Basic at $19.99/month for 10,000, and Professional at $59.99/month for 30,000 in 2026 research. These prices and plan details can change, so verify the current pricing page before purchase.

Common errors and fixes

Symptom Likely cause Fix
Invalid URL response The scheme is missing, the URL is malformed, or the target is not a valid web address. Require http:// or https:// and validate the host before sending requests.
Missing or invalid access key The key was omitted, mistyped, or not loaded into the environment. Check the environment variable and account key; do not print the credential into the report.
Usage limit reached The account has exhausted its available request allowance. Stop the run, check account usage and current plan terms, then resume only when allowance is available.
HTTP response is not an image The request failed or an export mode returned a different response shape. Inspect status, content type, and structured error body before writing a file with an image extension.
Page looks incomplete Rendering may need more time or the page loads content after initial navigation. Use the documented delay option where appropriate, verify the viewport and full-page setting, and test the target individually.
Repeated run returns old content The documented default cache period is 30 days. Choose an appropriate custom TTL or use the documented force-refresh option after confirming its current behavior.
Some URLs were not processed The script stopped on an exception or the run was interrupted. Keep per-URL status records and resume failed or pending entries rather than starting over.
Requests time out A target or rendering request took longer than the client timeout. Set a suitable client timeout, record the failure, and retry selectively with a bounded policy.

Local alternative: shot-scraper

If you want the browser rendering to run in your own environment, shot-scraper documents a YAML configuration for multiple screenshot jobs and a multi command. That is a local command-line workflow, separate from Screenshotlayer’s hosted API; you take responsibility for local installation, runtime, storage, and maintenance. Consult the project’s primary documentation for current setup and YAML syntax.

Or skip the browser setup

With ScreenshotNeo, a single GET request captures a URL; to process a list, repeat the request for each URL. See the ScreenshotNeo API documentation.

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)
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}`);
  • Cookie banners, popups, and chat widgets are removed before the screenshot; each cleanup step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed, and response headers report the page verdict and billing status.
  • An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf.
  • 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.

Start with 1,000 free screenshots a month, with no card required.

FAQ

Does Screenshotlayer accept a list of URLs in one request?

The reviewed API specification documents a target URL per capture request and does not document an array-based batch endpoint. The script pattern here loops over URLs and submits individual requests.

Can I safely run a batch overnight?

Only after checking account allowance, overage terms, and worker capacity, and after a small sample confirms the output handling you need. Keep a durable per-URL report so an interrupted run can resume.

Should every failed URL be retried?

No. Retry likely transient failures selectively. Correct malformed URLs and credentials, and address usage limits before resuming.

Sources