ScreenshotNeo

BlogHow-to

How to Capture Screenshots of Multiple URLs with GrabzIt

Batch capture a URL list with GrabzIt using its import tools or API. Compare the workflows, configure captures, and handle results reliably.

By the ScreenshotNeo team4 October 202612 min read

To capture many URLs with GrabzIt, choose between its no-code batch tools and an API loop. For a prepared list, use the GrabzIt bulk capture tools: the page currently recommends its Quick Batch Capture tool for fewer than 5,000 URLs and its CSV importer for larger jobs. For repeatable application workflows, submit one URL-to-image request per URL with the GrabzIt SDK or REST API, then save each result. The API documentation describes individual capture requests; do not assume one request accepts an arbitrary URL list.

Choose a batch method

Method Use it when Trade-offs
Quick Batch Capture You have a specific URL list or sitemap and a small or medium batch. The current page recommends this for fewer than 5,000 URLs. Minimal code. Inspect current task options and output settings before submitting.
CSV importer You have a large job and want it processed asynchronously in the background. GrabzIt currently recommends it for more than 5,000 URLs. Requires a template task and CSV preparation. Importing replaces all scheduled tasks and deletes archived screenshots.
SDK or REST API loop You need repeatable integration, custom naming, retries, storage, or application-level tracking. You must handle credentials, per-URL failures, completion, and result storage.

The bulk page says its tools can process “hundreds or thousands” of URLs and lists image outputs as well as PDF, DOCX, and MP4 conversions. That is a vendor description, not a throughput guarantee. Its instructions also say imported tasks start processing within ten minutes; do not treat this as a completion-time estimate.

Use GrabzIt’s no-code batch tools

Quick Batch Capture

  1. Open the bulk capture page and sign in or create an account.
  2. For a specific list or sitemap under the page’s stated 5,000-URL recommendation, open its Quick Batch/Create Multiple Tasks flow.
  3. Select screenshot-to-image as the task type and configure the output and capture settings in the current interface.
  4. Review the task list and submit it. The tool supports other conversions too, including URL to PDF and URL to MP4; choose image output when the goal is screenshots.
  5. Check the resulting tasks and retrieve the completed outputs through the interface.

CSV import for a larger batch

  1. Create one task with the options you need, such as output format and dimensions. Treat this as the template row.
  2. Export that task, then open the exported file in a spreadsheet application.
  3. Duplicate the template row once for each target page and replace the URL column in every row.
  4. Save as CSV, XLS, or XLSX, as the current import page permits.
  5. Before uploading, confirm that replacing scheduled tasks and deleting archived screenshots is acceptable for the account.
  6. Upload the file. The page says processing starts within ten minutes. Monitor task status and collect results as they complete.

The template export is the source of truth for the required columns and option values. Do not hand-build a CSV schema based on guesses. The import action carries a destructive account-level warning: it overwrites all current scheduled tasks and deletes archived screenshots. Export or otherwise preserve anything needed before proceeding, and avoid using this importer as an incremental update to an existing task collection.

Automate a URL list with the Node.js SDK

The SDK documents url_to_image(url, options), asynchronous save(callbackUrl, oncomplete), and get_result(id). This example submits each URL as a separate task and uses the callback’s identifier to retrieve bytes. Install the GrabzIt Node package according to its current Node.js documentation, configure credentials as environment variables according to the package’s setup instructions, and make a public callback endpoint for your own deployment before running it.

const fs = require('node:fs/promises');
const path = require('node:path');
const GrabzIt = require('grabzit');

const urls = [
  'https://example.com/',
  'https://example.org/',
];

const grabzIt = new GrabzIt(
  process.env.GRABZIT_APPLICATION_KEY,
  process.env.GRABZIT_APPLICATION_SECRET
);
const callbackUrl = process.env.GRABZIT_CALLBACK_URL;

if (!process.env.GRABZIT_APPLICATION_KEY ||
    !process.env.GRABZIT_APPLICATION_SECRET ||
    !callbackUrl) {
  throw new Error('Set GrabzIt credentials and a reachable callback URL');
}

function capture(url) {
  return new Promise((resolve, reject) => {
    grabzIt.url_to_image(url, { format: 'png', browserWidth: 1366, browserHeight: -1 });
    grabzIt.save(callbackUrl, (error, id) => {
      if (error) return reject(new Error(`Capture submission failed for ${url}: ${error}`));
      if (!id) return reject(new Error(`No capture ID returned for ${url}`));

      let attempts = 0;
      const poll = setInterval(async () => {
        attempts += 1;
        try {
          const bytes = await grabzIt.get_result(id);
          if (bytes && bytes.length) {
            clearInterval(poll);
            const filename = `${new URL(url).hostname.replace(/[^a-z0-9.-]/gi, '_')}.png`;
            await fs.writeFile(path.join('screenshots', filename), bytes);
            resolve(filename);
          } else if (attempts >= 60) {
            clearInterval(poll);
            reject(new Error(`No result became available for ${url} (capture ${id})`));
          }
        } catch (err) {
          clearInterval(poll);
          reject(err);
        }
      }, 2000);
    });
  });
}

(async () => {
  await fs.mkdir('screenshots', { recursive: true });
  for (const url of urls) {
    try {
      console.log(url, '=>', await capture(url));
    } catch (error) {
      console.error(error.message);
    }
  }
})();

This illustrates the documented callback-and-result pattern. Adapt the package import and client initialization to the version you install, and verify exact callback behavior against the current SDK documentation. Sequential submission is deliberate for a small example; for a large workload, add bounded concurrency and durable job tracking rather than launching an unbounded number of browser captures.

Call the GrabzIt REST API with cURL

The REST demo guide shows direct HTTP capture without an SDK. The following shell loop makes one request for each URL and writes each response to a separate file. Keep your key out of source control and provide it through an environment variable.

export GRABZIT_APPLICATION_KEY='YOUR_APPLICATION_KEY'
export GRABZIT_APPLICATION_SECRET='YOUR_APPLICATION_SECRET'

mkdir -p screenshots
while IFS= read -r url; do
  [ -z "$url" ] && continue
  name=$(printf '%s' "$url" | sed 's#https\?://##; s#[^A-Za-z0-9._-]#_#g')
  curl --fail --show-error --silent --get \
    'https://api.grabz.it/services/convert' \
    --data-urlencode "key=$GRABZIT_APPLICATION_KEY" \
    --data-urlencode "url=$url" \
    --data-urlencode 'format=png' \
    --output "screenshots/$name.png" \
    || printf 'Capture failed: %s\n' "$url" >&2
done < urls.txt

Save one fully qualified URL per line in urls.txt. Confirm current endpoint parameters and account requirements in the GrabzIt REST API demo guide before relying on this in production. The API overview currently lists formats including JPEG, WEBP, BMP, TIFF, SVG, and PNG. Trial API captures may carry a watermark; the overview advertises a seven-day trial, so verify current trial terms before planning around them.

Run the same pattern in Python

Use the REST API once per URL. The example below streams each response to disk, uses a timeout, and continues when one URL fails. Check GrabzIt’s current API guide for the exact endpoint and parameter names enabled for your account.

import os
from pathlib import Path
from urllib.parse import urlparse
import requests

API_URL = "https://api.grabz.it/services/convert"
APP_KEY = os.environ["GRABZIT_APPLICATION_KEY"]
APP_SECRET = os.environ["GRABZIT_APPLICATION_SECRET"]
URLS = ["https://example.com/", "https://example.org/"]
OUT = Path("screenshots")
OUT.mkdir(exist_ok=True)

session = requests.Session()
for url in URLS:
    host = urlparse(url).hostname or "capture"
    safe_name = "".join(c if c.isalnum() or c in ".-_" else "_" for c in host)
    try:
        response = session.get(
            API_URL,
            params={"key": APP_KEY, "url": url, "format": "png"},
            timeout=(10, 120),
        )
        response.raise_for_status()
        content_type = response.headers.get("content-type", "")
        if not content_type.startswith("image/"):
            raise RuntimeError(f"Expected image bytes, received {content_type or 'unknown content type'}")
        (OUT / f"{safe_name}.png").write_bytes(response.content)
        print(f"Saved {url}")
    except (requests.RequestException, RuntimeError) as exc:
        print(f"Failed {url}: {exc}")

The documented REST example uses a GET request that returns a JPG response. If your account or endpoint returns an asynchronous job identifier instead of image bytes, follow the documented completion and retrieval flow instead of writing the response body as a PNG.

Run the same pattern in Node.js with fetch

This direct REST example works in Node.js versions with built-in fetch. It checks HTTP status and content type before saving each image.

import { mkdir, writeFile } from 'node:fs/promises';

const appKey = process.env.GRABZIT_APPLICATION_KEY;
if (!appKey) throw new Error('Set GRABZIT_APPLICATION_KEY');

const urls = ['https://example.com/', 'https://example.org/'];
const outDir = 'screenshots';
await mkdir(outDir, { recursive: true });

for (const target of urls) {
  const query = new URLSearchParams({ key: appKey, url: target, format: 'png' });
  const response = await fetch(`https://api.grabz.it/services/convert?${query}`, {
    signal: AbortSignal.timeout(120_000),
  });
  if (!response.ok) {
    console.error(`Failed ${target}: HTTP ${response.status}`);
    continue;
  }
  const type = response.headers.get('content-type') || '';
  if (!type.startsWith('image/')) {
    console.error(`Failed ${target}: expected image, received ${type || 'unknown type'}`);
    continue;
  }
  const host = new URL(target).hostname.replace(/[^a-z0-9.-]/gi, '_');
  await writeFile(`${outDir}/${host}.png`, Buffer.from(await response.arrayBuffer()));
  console.log(`Saved ${target}`);
}

Use the API guide as the authority for authentication and endpoint details; never expose application secrets in a browser, public repository, or client-side script.

Configure each capture consistently

GrabzIt’s Node.js image options document the following capture controls. Exact availability can depend on account or package limits, so check current documentation before depending on a particular setting.

Need Documented option Notes
Browser viewport browserWidth, browserHeight Defaults are 1366 by 1170; documented maximum is 10,000 pixels. Set browser height to -1 for full-page capture.
Output dimensions width, height Set dimensions for resizing; -1 retains full output width or height as documented.
File type format Documented choices include BMP variants, JPG, PNG, SVG, TIFF, and WEBP. The API overview and SDK docs may present different subsets; check the interface or endpoint being used.
Wait before capture delay Milliseconds, default 0, documented maximum 30,000. Prefer a deliberate wait over assuming client-side content is ready immediately.
Click, hover, or scroll clickElement, hoverElement, scrollElement CSS selector based. The docs say only one of these actions can be specified; a delay may be needed afterward.
Capture a single element targetElement CSS selector. If several elements match, the first is selected.
Hide elements hideElement One or more CSS selectors, separated by commas.
Wait for a visible element waitForElement Capture waits for the specified selector to become visible.
Execute page JavaScript jsCode Runs before capture; use only code you control and need.
Choose a user agent requestAs 0 standard browser, 1 mobile browser, 2 search engine according to the docs.
Reduce visual clutter noAds, noCookieNotifications Documented flags for hiding adverts and common cookie notifications.
Image quality quality, hd, transparent Quality applies to JPG and WEBP and is documented from -1 through 100. HD doubles dimensions. Transparency is documented for PNG and TIFF.
Capture geography country Docs list SG, UK, and US, with fastest location as default. Verify current availability and plan support for location-specific captures.
Network and request behavior proxy, post, address Proxy configures HTTP proxy details; POST data forces an HTTP POST; address can provide a base URL for relative resources in supplied HTML.

For PDF, DOCX, or MP4, select the matching conversion operation instead of treating those outputs as screenshot image files. The API and bulk tool cover broader conversion workflows.

Make a large batch reliable

  1. Keep an input manifest. Preserve each original URL and assign a stable local job key so repeated or redirected URLs do not overwrite one another.
  2. Validate URLs first. Require a scheme, reject malformed rows, and decide how to handle duplicate URLs and fragments.
  3. Limit concurrency. Start with a small worker pool and increase only while errors and service limits remain acceptable. No fixed rate limit or guaranteed throughput is established by the cited material.
  4. Persist progress. Record submitted, pending, completed, and failed states so a process restart does not silently lose the batch.
  5. Retry selectively. Retry transient transport or service errors with bounded exponential backoff and jitter. Do not endlessly retry invalid URLs, access-denied pages, or deterministic selector failures.
  6. Validate results. Check HTTP status, response type, non-empty bytes, and expected output format before marking a URL complete.
  7. Keep credentials private. Use environment variables or a secret manager and restrict access to result files.
  8. Review content permissions. Only capture pages you are authorized to access, and consider whether stored screenshots contain personal or confidential data.

Performance, reliability, and cost

Full-page images and high-definition captures can produce larger files and take longer to process than a viewport capture. Delay, interaction, and waiting for client-rendered elements also add work. Set only the browser dimensions and image quality required for the use case, and process a large list with bounded concurrency.

For reliable scheduling, the CSV importer is described as asynchronous, while the SDK documents callback-based saving and retrieval using an identifier. That avoids holding a client request open for the entire batch, but your integration still needs persistent status and recovery handling. The source material does not establish a completion-time guarantee, rate limit, success rate, or plan price; check current account terms before estimating budget or duration. GrabzIt’s API overview currently advertises a seven-day trial and says trial API images carry a watermark. Those terms can change.

Or skip the browser setup

For a single URL, ScreenshotNeo turns one GET request into a screenshot. Put the request inside your own loop or job queue for a URL list. See the ScreenshotNeo API documentation for the complete parameter set.

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

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 screenshots. Only clean shots are billed, and response headers report the page verdict and billing status. Learn about ScreenshotNeo or sign up free for 1,000 screenshots a month, no card required.

Troubleshooting

Symptom Likely cause Fix
Existing scheduled tasks disappeared after upload The CSV import replaces all current scheduled tasks and deletes archived screenshots, as the importer warns. Preserve needed task data before importing. Use Quick Batch for a separate prepared list when appropriate.
CSV upload rejects the file or creates unexpected tasks The rows do not match the exported template, or option values/columns changed. Create and export a current template task, duplicate its row, and replace only the URL cells.
Tasks do not start when expected A task start date is set in the past. Leave the start date unset for immediate scheduling or set a future date, as the bulk page advises.
API call returns an error instead of an image Authentication, URL, account, or request parameters are invalid, or the response represents a failed capture. Check credentials and current endpoint requirements; log status and response details without logging secrets.
Saved file is empty or not an image An asynchronous result is not ready, or an error payload was written under an image extension. Check response status and content type. For SDK async captures, use the returned identifier and retrieve the result after completion.
Screenshot misses content loaded by JavaScript The capture happens before the page has rendered the target content. Set an appropriate delay or wait for a visible selector; use a selector that is unique and present on every target page.
Selector-based capture fails on some URLs Pages use different layouts or the selector is absent. Normalize the input set, use per-site options, and record failures individually instead of aborting the full batch.
Capture looks different from a visitor’s view Viewport, user agent, or capture location differs; location features may have plan constraints. Set consistent dimensions and request type. Verify current country options and plan support when geography matters.
Trial image has a watermark The API overview says trial captures carry a watermark. Check the current trial and plan terms before using captures in production.

FAQ

Can I pass multiple URLs in one GrabzIt API call?

The documented Node.js URL-to-image method takes one URL. Submit one capture request per URL, or use the bulk import tools for a prepared batch.

Should I use the CSV importer for every batch?

No. The current bulk page recommends Quick Batch Capture below 5,000 URLs and CSV import above that scale. Choose based on the live tool flow and remember the CSV import replaces scheduled tasks.

Can I capture a sitemap?

The bulk page identifies sitemap imports as a use case for Quick Batch Capture. Verify the current Create Multiple Tasks controls and review the resulting URL list before submission.

Can I create PDFs instead of image screenshots?

Yes. The bulk tools list PDF conversion, and the SDK documents URL-to-PDF methods. Select the PDF workflow when the intended output is a document.

Can I capture the same page from a particular country?

The SDK docs list SG, UK, and US country options. Check current availability and plan support before depending on geographic capture.

Sources: GrabzIt bulk import and batch capture, Node.js SDK documentation, REST API demo guide, Screenshot API overview, and location-specific capture notes.