Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To capture several URLs with CasperJS, put the addresses in an array, open them as ordered thenOpen() steps, and call capture() after each page loads. Give every image a filename based on its index or URL so one page cannot overwrite another.

This is primarily a maintenance guide for existing CasperJS projects. The CasperJS project is no longer actively maintained, and its maintainers describe continued use as appropriate only for certain legacy PhantomJS 1.9 production tests. Treat the script below as an example to adapt and verify against the exact CasperJS version and runtime you maintain.

The basic multi-URL pattern

CasperJS scenarios are sequences of navigation and action steps. The documented iteration API can add one thenOpen() step for each item in an array. Capture inside that navigation callback, where the current page has finished its navigation step.

var casper = require('casper').create();
var urls = ['https://example.com/', 'https://example.org/'];

casper.start().each(urls, function (self, url, index) {
    self.thenOpen(url, function () {
        this.capture('screenshot-' + index + '.png');
    });
});

casper.run();

The filenames in this example are screenshot-0.png, screenshot-1.png, and so on. Using the loop index is the simplest way to guarantee distinct names, even when two URLs have similar paths. If you replace the index with a hostname or slug, sanitize characters such as /, ?, &, and : before using the value as a filesystem name.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The code combines documented iteration and capture methods into one example; it is illustrative rather than a tested script. Verify the callback arguments and runtime behavior with the CasperJS version in your legacy environment before relying on it in production.

Run the script in the environment you already maintain

Save the example as a JavaScript file and invoke it with the CasperJS executable installed in your existing PhantomJS or SlimerJS setup. Keep the runtime, CasperJS version, and output directory under version control or in deployment documentation so a later runtime change does not silently alter the images.

Choosing the right capture method

Capture the page or a rectangle

capture(targetFilepath, [clipRect, imgOptions]) writes an image file. Omit clipRect for the normal page capture, or provide a rectangle when only a defined region is needed. Image options include an output format and quality; the API documents quality values from 1 through 100.

this.capture(
    'reports/home.jpg',
    { top: 0, left: 0, width: 1280, height: 800 },
    { format: 'jpg', quality: 85 }
);

Use a format and quality that match the downstream job. A lossless format is useful for text inspection; JPEG can reduce file size when small compression artifacts are acceptable. Keep the extension, format option, and any later processing consistent.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture one element

captureSelector(targetFile, selector [, imgOptions]) captures the area occupied by a selected element rather than the whole page. This is useful for a chart, invoice panel, or component whose boundaries are already represented in the DOM.

this.captureSelector(
    'reports/summary.png',
    '#summary-panel',
    { format: 'png' }
);

The selector must match the page at capture time. If the element is inserted after navigation, wait for it before calling captureSelector().

Return Base64 instead of writing a file

captureBase64(format[, area]) returns an encoded image string. The documented formats include BMP, JPG/JPEG, PNG, PPM, TIFF, XBM, and XPM. This is appropriate when the next step sends the image to another API or embeds it in a report; it is not a direct file-save operation, so your script must handle the returned string.

Waiting for pages that render after navigation

A navigation callback alone is not a guarantee that asynchronous content is visible. Prefer a condition-based wait when you can identify a readiness signal, such as a result element, rather than sleeping for an arbitrary number of milliseconds.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
var casper = require('casper').create();
var urls = [
    'https://example.com/dashboard',
    'https://example.org/report'
];

casper.start().each(urls, function (self, url, index) {
    self.thenOpen(url, function () {
        var output = 'dashboard-' + index + '.png';

        this.waitFor(
            function check() {
                return this.exists('#report-ready');
            },
            function then() {
                this.capture(output);
            },
            function timeout() {
                this.echo('Readiness condition timed out for ' + url);
            }
        );
    });
});

casper.run();

The API’s waitFor() example uses a default timeout of 5,000 milliseconds. Set a timeout appropriate to the page and your CasperJS version when the API permits it, and provide a failure callback so a timeout is visible instead of producing an apparently valid but incomplete image. A condition-based wait is preferable when readiness can be detected, but this old browser stack should not be expected to handle every modern site.

Building a safer URL list workflow

Keep order and identity explicit

Use an array of objects when the output needs a human-readable name as well as a URL. The index still provides a collision-proof fallback.

var pages = [
    { name: 'home', url: 'https://example.com/' },
    { name: 'pricing', url: 'https://example.com/pricing' }
];

casper.start().each(pages, function (self, page, index) {
    self.thenOpen(page.url, function () {
        this.capture('shots/' + index + '-' + page.name + '.png');
    });
});

casper.run();

Create the destination directory before running, or point the paths at a directory your deployment creates. Keep the URL-to-file mapping in a log if another process will consume the images.

Handle duplicate or unstable addresses

  • Assign the index before navigation so duplicate URLs still produce separate records.
  • Normalize trailing slashes only if your application considers those addresses equivalent; otherwise preserve the supplied URL.
  • Do not use a raw query string as a filename. Convert it to a safe slug or use the index.
  • Record the final URL or a page label in your own log when redirects matter to the report.

Choose sequential execution deliberately

The each() pattern adds navigation steps in list order. That makes output ordering predictable, but a long list can take as long as the sum of its page loads and waits. Keep the list bounded, use the shortest reliable readiness condition, and avoid an unconditional long delay on every page.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Compatibility limits you must account for

CasperJS is a navigation scripting and testing utility for PhantomJS and SlimerJS; the project marks SlimerJS support as experimental. The maintainers also note that versions through 1.1-beta3 do not support PhantomJS 2.0 and newer. Attach that warning to the exact version and runtime you are preserving rather than treating it as a complete compatibility matrix.

Because the project is no longer actively maintained, test representative pages before expanding a capture job. Authentication flows, newer JavaScript syntax, strict security policies, client-side rendering, and anti-automation checks can all expose limits in an old browser engine. A successful navigation callback does not prove that every visual asset or application state is present.

Troubleshooting common failures

Only the last image exists

Cause: every iteration used the same target path.

Fix: include the index or a sanitized page name in the path, as in screenshot- plus index in the first example.

The image is blank or missing dynamic content

Cause: capture happened before the page’s asynchronous content was ready, or the legacy engine could not execute the page’s code.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix: wait for a selector or other observable condition, then test the page in the maintained runtime. If the condition never appears, log the timeout and inspect the page’s compatibility requirements instead of increasing the delay indefinitely.

A selector capture produces no useful image

Cause: the selector did not match, the element was not inserted yet, or its layout had not settled.

Fix: wait for the selector, verify the exact selector spelling, and capture the full page once to determine whether the problem is selection or rendering.

Navigation hangs on one URL

Cause: a page may be waiting on a resource, redirect, script, or interaction that the old runtime cannot complete.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix: isolate the URL, add a bounded readiness or timeout path, and continue or fail explicitly according to your reporting requirements. Do not let an unbounded wait stall the entire list.

The script fails after a runtime upgrade

Cause: CasperJS and its supported PhantomJS versions have narrow historical compatibility. In particular, versions through 1.1-beta3 are documented as not supporting PhantomJS 2.0 and newer.

Fix: restore the previously documented runtime or validate a compatible combination in a separate environment before changing production capture jobs.

Base64 output consumes too much memory

Cause: captureBase64() keeps encoded image data in memory until your code sends or releases it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix: write files with capture() when direct file output is sufficient, and process Base64 results one URL at a time.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Operational, performance, and cost considerations

CasperJS runs on infrastructure you operate, so the direct service charge is determined by your own machine, storage, and maintenance arrangements rather than a CasperJS API bill. The trade-off is engineering time: you must preserve an old browser/runtime pairing, handle page failures, and keep output storage under control.

  • Throughput: the documented multi-URL pattern is sequential. Total duration grows with navigation and wait time for each URL.
  • Reliability: condition-based waits and explicit timeout handlers make failures visible. They cannot make an unsupported page render correctly.
  • Storage: select format and quality deliberately, and use deterministic names so reruns replace the intended artifact rather than create ambiguity.
  • Reproducibility: record the URL list, runtime versions, capture options, and date with each batch.

Or skip the browser setup

If you need screenshots from a URL list without maintaining a PhantomJS-era browser, ScreenshotNeo provides a website screenshot API and MCP server. A GET request returns PNG, JPEG, WebP, or PDF. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled.

Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. For an AI workflow, its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One-call examples

See the ScreenshotNeo documentation for request details. The following calls capture one URL; put them in your own loop for a list.

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}`);

Options useful for a URL batch

ScreenshotNeo supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or any viewport, retina scale, image resizing, transparent backgrounds, custom CSS and JavaScript, click-before-capture actions, selector hiding, waits for a selector, delay or network idle, and blocking for ads, trackers, requests, or resource types. You can supply headers, cookies, a user agent, an Authorization value, timezone, and geolocation; choose PDF paper size, margins, landscape mode, and page ranges; use caching with a chosen TTL; create signed links for public <img> tags; submit asynchronous jobs with signed webhooks; capture up to 100 URLs per bulk call; query usage; and use the OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.

Plans

Plan Included shots Price
Free 1,000 per month $0, no card
Starter 3,000 $5
Growth 15,000 $15
Pro 60,000 $39
Scale 250,000 $99
Business 1,000,000 $249

Yearly billing gives two months free, and every feature is available on every plan. You can start with 1,000 free screenshots a month with no card.

Frequently Asked Questions

Can one CasperJS batch mix full-page and element captures?

Yes. Keep the same ordered URL loop and choose capture() or captureSelector() inside each page’s navigation callback according to that page’s output requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should I preserve when handing a CasperJS job to another developer?

Preserve the URL list, filename convention, CasperJS and runtime versions, wait conditions, image options, and the expected output directory. Those details determine whether a rerun is comparable.

Is a successful file write proof that the page rendered correctly?

No. A file can be created even when asynchronous content was not ready or the legacy engine could not execute part of the page. Use a readiness condition and inspect representative images.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.