How to Screenshot Multiple URLs with a Bulk Capture API in India
Capture many URLs reliably with a bulk screenshot API: batch requests, pace work, retry failures, and check what India-region rendering actually means.
To screenshot multiple URLs in India, submit a list of URL-specific capture requests to a bulk screenshot endpoint, choose whether captures run immediately or when returned screenshot links are fetched, and pace large jobs with a queue, usage checks, and retries. Keep credentials on your server and verify that your provider account supports the required India geography. A listed India option does not guarantee access for every plan or identical results for every target site.
This guide uses ScreenshotOne as the documented bulk API example. Its endpoint is POST https://api.screenshotone.com/bulk. The request can include shared options and per-URL requests; individual request settings can override shared defaults. Read the bulk endpoint documentation before deploying, since provider options, account limits, and pricing can change.
1. Choose a bulk execution mode
There are two execution modes in ScreenshotOne’s documented bulk workflow:
- Lazy mode (
execute: false): returns screenshot URLs. The capture happens when a returned URL is downloaded. This can defer work until a consumer needs the image, but it also means the response is not proof that all images have already rendered. - Execute mode (
execute: true): performs the requests before returning. Allow enough time for the whole batch to finish. A slow page or a large batch can make this unsuitable for a short-lived HTTP request handler.
Use lazy mode when downstream code can fetch the returned links and immediate image bytes are unnecessary. Use execute mode when the caller needs capture work completed before it receives the bulk response. For long-running jobs, accept work into your own queue and let a worker call the provider rather than keeping a web request open.
2. Send a bulk request with cURL
The example below sends common options plus two URL requests. Store the API key in a server-side environment variable. The exact response shape depends on the endpoint and selected mode; inspect the response and handle each request result individually rather than assuming the entire batch succeeded.
curl --fail-with-body --silent --show-error \
-X POST "https://api.screenshotone.com/bulk" \
-H "Content-Type: application/json" \
-H "X-Access-Key: ${SCREENSHOTONE_ACCESS_KEY}" \
--data '{
"execute": false,
"options": {
"format": "webp",
"full_page": true,
"geo": "in"
},
"requests": [
{ "url": "https://example.com/" },
{ "url": "https://example.org/", "options": { "format": "png" } }
]
}'
The shared options apply to every capture unless a request supplies an override. Here, the second request overrides the output format. The geo: "in" setting is documented as an India geography value; confirm that it is enabled for your account and test representative sites before relying on it for regional fidelity.
3. Build a controlled Python batch worker
This Python example submits one batch and prints the JSON response. Install the dependency with python -m pip install requests. Set SCREENSHOTONE_ACCESS_KEY in the process environment or your secrets manager; do not hard-code it or commit it.
import json
import os
import time
import requests
API_URL = "https://api.screenshotone.com/bulk"
API_KEY = os.environ["SCREENSHOTONE_ACCESS_KEY"]
payload = {
"execute": False,
"options": {
"format": "webp",
"full_page": True,
"geo": "in",
},
"requests": [
{"url": "https://example.com/"},
{
"url": "https://example.org/",
"options": {"format": "png"},
},
],
}
response = requests.post(
API_URL,
headers={"X-Access-Key": API_KEY},
json=payload,
timeout=(10, 180),
)
response.raise_for_status()
result = response.json()
print(json.dumps(result, indent=2))
The connection timeout and response timeout are examples, not guarantees about provider completion time. Set them to match your job deadline and batch size. For a production worker, persist the input URLs and a per-URL status before submitting; record returned results, retry only eligible failures, and make retries safe to run after a worker restart.
4. Submit the same kind of batch from Node.js
This example uses the built-in fetch available in current Node.js releases. It checks HTTP failure and parses the bulk response as JSON.
const apiKey = process.env.SCREENSHOTONE_ACCESS_KEY;
if (!apiKey) throw new Error('Set SCREENSHOTONE_ACCESS_KEY');
const payload = {
execute: false,
options: { format: 'webp', full_page: true, geo: 'in' },
requests: [
{ url: 'https://example.com/' },
{ url: 'https://example.org/', options: { format: 'png' } },
],
};
const res = await fetch('https://api.screenshotone.com/bulk', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Access-Key': apiKey,
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(180_000),
});
if (!res.ok) {
throw new Error(`Bulk request failed: HTTP ${res.status}: ${await res.text()}`);
}
const result = await res.json();
console.log(JSON.stringify(result, null, 2));
5. Pace large jobs and recover from failures
A bulk call groups capture requests; it does not remove the need to manage throughput. ScreenshotOne documents that bulk requests use the same one-minute request bucket as regular screenshot requests. Before draining a large backlog, inspect the provider usage response, especially concurrency.remaining and concurrency.reset. The guide explains that these values describe how many requests can be started in the current minute bucket, not how many renders are actively running.
- Split the URL list into bounded batches. Begin with small batches and increase only after observing response time, errors, and your account’s limits.
- Check available request capacity before starting each batch. Pause until the bucket resets when capacity is exhausted.
- Save each URL’s state and result separately. A failed item should not force successful items to be repeated.
- Retry transient failures with bounded exponential backoff and jitter. Set a maximum attempt count, then surface permanent failures for review.
- Use an in-process queue for a small single-worker task. For jobs that need to survive restarts or run across workers, use a durable queue such as Redis/BullMQ or SQS and store job state independently.
Retries should be selective: distinguish a provider or network error from a valid capture of a page that is blank, blocked, or otherwise not the expected content. A retry cannot fix a consistently inaccessible target. Avoid launching every URL at once; bursts can exhaust the minute bucket and make failures harder to isolate.
6. Choose output and storage deliberately
The provider’s options reference lists PNG, JPEG, WebP, GIF, TIFF, AVIF, HEIF, PDF, HTML, and Markdown output formats. Choose the output that matches the next step: compressed image formats reduce transfer and storage, while PDF is useful for document-like capture workflows. Confirm exact format support and response behavior against the current options reference.
The bulk documentation includes an S3-compatible storage example using store: true, response_type: "empty", and a storage path per request. Use that pattern only after confirming the current storage configuration and required credentials in the vendor documentation. If you need to retain captures, define a retention policy and deterministic object naming scheme so retries do not create confusing duplicates.
7. Account for India geography and page differences
The ScreenshotOne options reference lists in as a geography value. That establishes that India is a documented option; it does not establish that every account or plan can use it, nor that every site will return the same content as a person browsing from India. Geo-dependent pages can also vary due to cookies, login state, language, IP-based routing, consent state, and experiments.
Test a small representative set of URLs with the same geography, headers, cookies, and other relevant settings that production will use. Compare the output to the expected regional page and document any site-specific exceptions. Do not claim India-local results to downstream users until you have verified account access and the target behavior.
8. Keep API credentials private
Call the provider over HTTPS. Keep the key in an environment variable or secrets manager, restrict access to the worker that needs it, and rotate it if it is exposed. ScreenshotOne documents API keys in the request body, an X-Access-Key header, or a query parameter; a header or POST body avoids putting the credential in a URL that may appear in logs or browser history. Never put a provider key in frontend JavaScript or a public repository.
9. Performance, reliability, and cost planning
- Batch size: larger batches reduce orchestration overhead but make a single response slower and can complicate recovery. Tune batch size using your own pages and account limits; no universal throughput figure follows from the API documentation.
- Page weight: full-page captures and media-heavy pages can take longer and produce larger outputs. Select only the output and capture scope your workflow needs.
- Worker design: use bounded concurrency, request deadlines, per-URL results, and a durable queue when losing in-memory work is unacceptable.
- Cost: check the live provider plan, included renders, request limits, and storage terms before estimating a job. Vendor pricing and entitlements can change. Do not infer a price per screenshot from a plan headline without checking its current allowance and overage rules.
- Reliability: provider documentation describes supported workflows, not a measured success rate or guarantee for every website. Expect individual targets to timeout, block automation, or change content.
10. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Authentication failure | Missing, invalid, revoked, or incorrectly transmitted API key. | Check the server environment variable and account key; send it in the header or POST body over HTTPS. Do not expose it in a URL or client code. |
| Batch rejected | Malformed JSON, missing URL/input, or unsupported option/value. | Validate the payload before submission, test one request, and compare option names and values with the current API reference. |
| Some URLs fail while others work | Per-site timeout, bot check, redirect, or page-specific rendering issue. | Track results per URL, inspect the failing target and its options, and retry only if the cause may be transient. |
| Work starts slowly or requests are limited | The shared one-minute request bucket is depleted. | Check usage capacity and reset time; pause, then resume through a queue at a controlled rate. |
| Worker times out waiting for execute mode | The batch took longer than the client or hosting request deadline. | Reduce batch size or use lazy mode; for long-running work, submit from a background worker with a suitable deadline. |
| Capture does not look like an India visitor’s page | Geography access may be unavailable, or the site varies by cookies, IP, login, or other context. | Verify account entitlement and test representative targets with the intended geography and request context. |
| Returned links are not image bytes yet | Lazy mode defers capture until the returned URL is downloaded. | Fetch the returned URL when needed, or choose execute mode if capture must finish before the bulk response. |
| Stored objects are missing | Storage settings, response mode, or per-request path may not match the current storage configuration. | Check the current S3-compatible storage guide and confirm each request’s storage path and credentials. |
11. When another orchestration model fits
Urlbox documents GET render links for synchronous rendering and POST requests that can be synchronous or asynchronous, with polling or webhooks for completion. Its product page says POST can render multiple screenshots in parallel. This is a different execution model to evaluate if asynchronous completion or parallel rendering is central to your workflow. Verify current geography support, limits, and plan terms directly; the cited product material does not establish those details for India.
For a self-managed approach, keep individual captures as queue jobs and control concurrency, retries, storage, and progress in your application. This gives you direct control over durable state and recovery, while requiring you to build and maintain that orchestration. Neither API documentation nor product descriptions establish comparative performance; benchmark your own representative URLs if latency matters.
12. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It can capture URLs as PNG, JPEG, WebP, or PDF. Make one GET request per URL, and use your own queue or batch worker when you need to process many URLs. 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 are accepted and removed before capture, along with known newsletter popups and chat widgets; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, and failed loads are never billed, and cache hits cost nothing. Responses identify the page verdict and billing status in headers.
- An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.
13. Frequently asked questions
Can one bulk request use different options for different URLs?
Yes. The documented bulk shape supports shared options and individual request options, with a request’s settings able to override shared values.
Does a bulk API guarantee every URL succeeds?
No. Handle results per URL and design retries and reporting around partial failures.
Does the India geography setting prove the render came from India?
No. It is a documented option. Confirm account access and verify the result against representative targets.
Should I use a database queue or an in-memory list?
An in-memory queue can suit a short, single-worker run. Use durable job storage when work must survive restarts, be shared by workers, or support reliable retry and audit history.


