ScreenshotNeo

BlogHow-to

How to Capture Screenshots for a List of URLs with ScreenshotMachine CLI

Capture a list of URLs from the command line with a Bash loop around ScreenshotMachine’s API. Get runnable code, settings, and fixes for common errors.

By the ScreenshotNeo team4 October 20268 min read

To capture screenshots for a list of URLs with ScreenshotMachine from the command line, put one URL on each line of a text file and run a Bash loop that makes one ScreenshotMachine API request per URL. The vendor documents a Bash and curl example for a single URL; the loop below is a practical wrapper, not a separate official list CLI command.

Each request needs your ScreenshotMachine customer key and a target URL. ScreenshotMachine recommends URL encoding the URL parameter. The official API endpoint is https://api.screenshotmachine.com.

1. Prepare a URL list and API key

Create urls.txt with one complete HTTP or HTTPS URL per line:

https://example.com/
https://www.wikipedia.org/
https://stripe.com/

Set the customer key in your shell environment. This keeps it out of the script and makes it less likely to be committed to source control:

export SCREENSHOTMACHINE_KEY='YOUR_CUSTOMER_KEY'

Obtain the key from your ScreenshotMachine account. Do not put a real key in a shared script, terminal transcript, or public repository. The API also supports a secret phrase and URL-based hash authentication; keep that secret private too.

2. Capture each URL with a Bash and curl loop

Save this as capture-list.sh. It uses the vendor’s documented GET request pattern and sample settings, while generating a distinct numbered filename for each nonblank input line. The loop and filename scheme are illustrative shell code, not vendor-prescribed CLI syntax.

#!/usr/bin/env bash
set -u

: "${SCREENSHOTMACHINE_KEY:?Set SCREENSHOTMACHINE_KEY first}"
input=${1:-urls.txt}
output_dir=${2:-screenshots}

if [[ ! -r "$input" ]]; then
  printf 'Cannot read input file: %s\n' "$input" >&2
  exit 1
fi
mkdir -p -- "$output_dir"

line_number=0
successes=0
failures=0
while IFS= read -r url || [[ -n "$url" ]]; do
  line_number=$((line_number + 1))
  # Ignore blank lines and full-line comments.
  [[ -z "${url//[[:space:]]/}" ]] && continue
  [[ "$url" =~ ^[[:space:]]*# ]] && continue

  # Require an explicit scheme so the saved input is easy to audit.
  if [[ ! "$url" =~ ^https?:// ]]; then
    printf 'Line %d: expected an http:// or https:// URL; skipped\n' "$line_number" >&2
    failures=$((failures + 1))
    continue
  fi

  file=$(printf '%s/%04d.png' "$output_dir" "$line_number")
  tmp="$file.part"
  printf 'Capturing line %d: %s\n' "$line_number" "$url"

  # --fail makes HTTP error responses produce a nonzero curl exit status.
  # --data-urlencode safely encodes the target URL as a query parameter.
  if curl --fail --silent --show-error --get \
      'https://api.screenshotmachine.com' \
      --data-urlencode "key=$SCREENSHOTMACHINE_KEY" \
      --data-urlencode "url=$url" \
      --data-urlencode 'dimension=1366x768' \
      --data-urlencode 'device=desktop' \
      --data-urlencode 'format=png' \
      --data-urlencode 'cacheLimit=0' \
      --data-urlencode 'delay=2000' \
      --data-urlencode 'zoom=100' \
      --output "$tmp"; then
    if [[ -s "$tmp" ]]; then
      mv -- "$tmp" "$file"
      successes=$((successes + 1))
    else
      printf 'Line %d: response file is empty\n' "$line_number" >&2
      rm -f -- "$tmp"
      failures=$((failures + 1))
    fi
  else
    printf 'Line %d: request failed\n' "$line_number" >&2
    rm -f -- "$tmp"
    failures=$((failures + 1))
  fi
done < "$input"

printf 'Finished: %d successful, %d failed or skipped. Files: %s\n' \
  "$successes" "$failures" "$output_dir"
(( failures == 0 ))

Run it with:

chmod +x capture-list.sh
./capture-list.sh urls.txt screenshots

The script runs requests sequentially. It writes each response to a temporary file and renames it only if curl reports success and the file is nonempty, so a failed request does not leave a partial image under the final filename. A nonempty response alone does not prove that it is a valid screenshot; inspect a sample, especially when changing options or handling unusual failures.

3. Choose dimensions, format, and rendering settings

These are the ScreenshotMachine settings most useful for a URL list. Confirm defaults against the live vendor documentation when publishing or relying on a default, since API defaults may change.

Setting What it controls Practical guidance
dimension Viewport width and height, written as WIDTHxHEIGHT. The documentation gives width limits of 100–1920 and height limits of 100–9999. The example uses 1366x768. For a full-length capture, use 1366xfull.
format Output image type. Documented formats are JPG, PNG, and GIF. Match the file extension to the requested format. PNG is the sample choice; JPG may suit smaller photographic captures.
cacheLimit How old a cached capture may be, in days; the documented range is 0–14. 0 requests a fresh screenshot. A nonzero value can reuse a recent capture where a cached result is acceptable.
delay Extra time before capture, in milliseconds. The vendor sample uses 2000. Increase it for pages that need more time for images or animation to render.
zoom Page zoom percentage. The sample uses 100. Change it only when you need a different rendered scale.
device Device rendering choice. The sample uses desktop; use a supported device value from the vendor docs if your capture needs another profile.
Cookies and request headers Cookie, accepted language, and user-agent settings affect the page response. Use these when the target varies by locale, session, or browser identity; treat cookie values as credentials.
CSS selector and crop Targeted element selection and crop controls. Use these when you need a specific region rather than the whole viewport; check the resulting image dimensions and content.

The documented sample uses dimension=1366x768, device=desktop, format=png, cacheLimit=0, delay=2000, and zoom=100. Those are example choices, not universal requirements. Pass parameters with --data-urlencode so URLs containing query strings, ampersands, or other reserved characters are encoded safely.

4. Verify the output and adapt the workflow

  1. Check the script’s summary and confirm the expected number of image files exists.
  2. Check file sizes and open a few captures. A successful HTTP transfer can still produce an error response or an unexpected page image.
  3. If a page is visibly incomplete, increase delay and repeat the capture. If the page changes frequently, keep cacheLimit=0; if reuse is suitable, select a cache age within the documented 0–14 day range.
  4. For large lists, estimate request volume before running. This script sends one request for every accepted URL and runs them one at a time. The reviewed sources do not establish a vendor batch endpoint or a safe concurrency limit.

Numbered names avoid collisions and unsafe filename characters. If you want names based on hostnames, derive them with a URL parser and still add a sequence number: different URLs can share a host, and filenames created directly from arbitrary URLs can contain slashes or reserved characters.

5. Troubleshoot common problems

Symptom Likely cause Fix
The script exits immediately saying the key is unset. SCREENSHOTMACHINE_KEY is not exported in the current shell. Export the customer key in the same shell, then rerun. Avoid hard-coding it into the script.
curl says it cannot read the input file. The path is wrong or the file is not readable from the current directory. Pass the correct input path as the first argument and check its permissions.
Rows are skipped as invalid. A line is blank, a comment, or does not start with http:// or https://. Use one complete URL with a scheme per line. Remove accidental spaces and malformed entries.
The API rejects the request or returns an error. The key may be incorrect, a parameter may be invalid, or the target URL may not be accepted. Check the key and URL, review the vendor’s current parameter documentation, and try one known URL before rerunning the list.
The image is blank or missing late-loading content. The page may need more render time, or its content may depend on cookies, language, or user-agent. Increase delay and configure the relevant documented cookie or request settings.
Every run appears to return an older page. A cached capture may be reused. Set cacheLimit=0 to request a fresh screenshot.
The output file exists but cannot be opened as an image. A nonempty response is not necessarily a valid image; an error body may have been saved. Inspect the response and API error details. Do not treat file existence alone as proof of a successful capture.
Later images overwrite earlier ones. The output naming logic reused a filename. Keep a sequence number or otherwise guarantee a unique filename for every input row.

The vendor’s documentation does not define a complete failure-handling contract for every API response in the reviewed material. The example therefore checks curl’s HTTP failure status and whether a response file is empty, then leaves visual or format validation as an explicit follow-up.

6. Performance, reliability, and cost considerations

  • Runtime: With sequential requests, total time grows with the number of URLs and each page’s capture/render time. Delays improve the chance that late content is present but add that wait to each request.
  • Reliability: Keep the original URL list so failed rows can be retried selectively. The sample records failures by line number, uses temporary files to avoid presenting partial downloads as final images, and does not claim retries or parallel execution.
  • Caching: A zero cache limit requests a fresh screenshot; allowing a positive cache limit can avoid recapturing pages when a recent image is acceptable. Choose based on freshness needs.
  • Cost: The reviewed ScreenshotMachine sources establish that an account key is required but do not provide pricing, so this guide makes no price comparison. Check the vendor’s current plan terms before estimating a production batch.
  • Input hygiene: Treat URLs and credentials as untrusted or sensitive input. Keep keys and session cookies out of logs and source control, and do not feed arbitrary unreviewed URL lists to a production capture job.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns a PNG, JPEG, WebP, or PDF. Its clean capture flow accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before the screenshot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server exposes screenshot, page-info, and PDF tools to AI agents. Every plan includes every feature; the free plan includes 1,000 shots a month with no card, and paid plans start at $5 for 3,000.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. To capture a list, repeat the request for each URL and save each response under a unique name.

Sign up free for 1,000 screenshots a month, with no card required.

Frequently asked questions

Does ScreenshotMachine provide an official CLI command that accepts a URL list?

The reviewed official material documents a Bash/curl request for one URL and official examples, but does not establish a dedicated official list CLI. The shell loop here repeats the documented API call.

Can I provide URLs without a scheme?

The API documentation says the protocol prefix is optional. This example deliberately requires complete HTTP or HTTPS URLs so the input list is explicit and easy to validate.

Is there a ScreenshotMachine batch endpoint or documented concurrency limit?

The reviewed sources do not verify either for the official API. The example processes entries sequentially. A separate D3 Security integration documents an action that accepts multiple URLs, but that is a third-party workflow, not the official shell interface.

Where can I check the official parameters?

See ScreenshotMachine’s website screenshot API documentation. Its parameter section states: “Url percent-encoding is strongly recommended.”