Best wkhtmltoimage Alternatives for Website Screenshots
Compare Playwright, Puppeteer, and hosted screenshot APIs as wkhtmltoimage alternatives, with runnable examples and practical migration advice.
If you need an alternative to wkhtmltoimage for website screenshots, choose between running browser automation yourself and calling a hosted screenshot API. ScreenshotNeo is the first hosted API to consider when you want a URL-to-image request, clean captures, and billing only for clean shots. For code-driven workflows, Playwright and Puppeteer provide browser control; Browserless and ScreenshotOne offer hosted HTTP capture endpoints. There is no universal winner: the right fit depends on browser control, runtime ownership, capture requirements, repeatability, and current service limits.
wkhtmltoimage is part of the wkhtmltopdf project. Its status page describes its Qt/WebKit foundation as old: Qt 4 has not been supported since 2015, and the bundled WebKit had not been updated since 2012. These are historical statements on that page, not a fresh audit of every package or fork. The maintainer’s guidance there is to consider Puppeteer or a wrapper for sites that use dynamic JavaScript. The same page warns against converting untrusted HTML without sanitization. Read the project status page.
Quick comparison
| Option | Good fit | What you operate | Key consideration |
|---|---|---|---|
| ScreenshotNeo | URL-to-image or PDF capture through one HTTP request, including clean captures | Your request and API key | Cookie banners, popups, and chat widgets are removed before capture; only clean shots are billed. See the docs for options. |
| Playwright | Visual tests or a larger browser automation flow | Browser runtime and automation code | Offers viewport, element, and full-page screenshots and visual comparison; keep the environment consistent for repeatable snapshots. |
| Puppeteer | JavaScript applications that already use Puppeteer’s browser automation API | Browser runtime and automation code | Supports page and element screenshots; the official Chrome overview describes automation for Chrome and Firefox. |
| Browserless screenshot API | HTTP URL-to-image or HTML-to-image capture without running browser instances in your application | API integration and provider configuration | Documents PNG, JPEG, and WebP output and Puppeteer-style options; check current limits and request behavior. |
| ScreenshotOne API | HTTP request capture in an application | API integration and provider configuration | Use HTTPS to protect API keys, authorization headers, cookies, and other sensitive request data in transit. |
| Selenium WebDriver | Teams already using WebDriver, including some monitoring workflows | WebDriver runtime and automation code | AWS documents it among browser options for some canary runtimes that can store UI screenshots; that is an example, not a direct framework comparison. |
Sources: wkhtmltopdf project status, Playwright screenshots and Playwright visual comparisons, Puppeteer screenshots and Chrome’s Puppeteer overview, Browserless screenshot API, ScreenshotOne documentation, and AWS Synthetics canary documentation.
Choose by workflow
Use Playwright when capture is part of browser testing
Choose Playwright when screenshots sit inside a broader automation or visual comparison workflow. Its screenshot documentation covers viewport, element, and full-page captures; its test documentation covers visual comparisons. This gives your code access to browser interactions and page state before taking the shot. You also own the browser setup and need to keep browser version, operating system, fonts, settings, and headless mode stable when comparing images, because rendering can vary across environments. Playwright screenshot guide · Visual comparison guide.
Use Puppeteer when it fits your JavaScript automation
Choose Puppeteer when its browser automation API fits an existing JavaScript application. It can capture a page or a selected element. Your team still manages the browser runtime and capture code. Puppeteer automates Chrome and Firefox, according to the official Chrome overview; avoid assuming it is Chromium-only. Puppeteer screenshot guide · Chrome Puppeteer overview.
Use a hosted API when an HTTP request fits better
Choose a hosted service when your application needs a request/response capture flow and you prefer not to manage browser instances. ScreenshotNeo is the first hosted API to try when clean output and clear billing verdicts matter: it removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture, and only clean shots are billed. Browserless documents an endpoint that returns PNG, JPEG, or WebP and accepts Puppeteer-style options. ScreenshotOne documents HTTP screenshot requests and warns that HTTP does not encrypt sensitive request data in transit. For any provider, check current authentication, waits, output formats, limits, and terms before committing. ScreenshotNeo · Browserless · ScreenshotOne.
Use Selenium when WebDriver is already your standard
If your team already uses Selenium WebDriver, keeping screenshot capture in that stack may be simpler than introducing another automation API. AWS lists Selenium WebDriver, Playwright, and Puppeteer as browser-access options in some canary runtimes and describes storing UI screenshots. Treat that as an example of a monitoring workflow, not evidence that the frameworks have equivalent features or output.
Runnable browser automation examples
The following minimal examples use a fixed viewport and save an image locally. They illustrate browser-driven capture; they do not promise identical pixels across machines. Pin your browser and runtime versions if you need stable visual comparisons.
Playwright: Node.js
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'page.png', fullPage: true });
await browser.close();
Install Playwright in a project and install its browser using the commands in the official getting-started guide. For a selected element, locate it and call its screenshot method:
const heading = page.locator('h1');
await heading.screenshot({ path: 'heading.png' });
Puppeteer: Node.js
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.screenshot({ path: 'page.png', fullPage: true });
await browser.close();
Install Puppeteer following its official installation guide. To capture one element:
await page.locator('h1').screenshot({ path: 'heading.png' });
When you already use Selenium
Selenium screenshot setup depends on the language binding and WebDriver runtime in your project. Keep the capture in the existing driver session, set the window size explicitly, wait for the page state you need, and save the driver’s screenshot bytes. Consult the official Selenium documentation for the binding and driver you use. Selenium is a workflow option here, not a claim that it is a drop-in replacement for every wkhtmltoimage invocation.
Migration checklist
- Inventory current captures. Record input URLs or HTML, viewport dimensions, output format, full-page behavior, cookies, headers, JavaScript, fonts, and any post-load interactions.
- Choose the execution model. Use Playwright or Puppeteer for browser control in your code; consider a hosted endpoint for request/response capture without managing browser instances.
- Build representative cases. Include JavaScript-rendered content, custom fonts, long scroll regions, consent banners, authenticated pages if relevant, and pages requiring interactions.
- Compare behavior in a stable environment. Fix browser version, OS image, installed fonts, viewport, device scale factor, and wait conditions before judging visual diffs. No independent comparison of the listed services was performed for this article.
- Handle untrusted input. The wkhtmltopdf project status page warns against converting untrusted HTML without sanitization. Treat migration as a security review too: sanitize input and isolate browser execution appropriately.
- Measure operational cost. Account for browser compute, dependency updates, concurrency, retries, storage, API charges, and the engineering time required to maintain the capture path.
Capture options and edge cases to plan for
Browser libraries and hosted APIs expose different controls, so translate requirements rather than expecting identical option names or pixels. Verify each provider’s current documentation before relying on a particular setting.
| Requirement | What to decide |
|---|---|
| Viewport or full page | Set a fixed viewport for viewport shots; for full-page shots, check sticky headers, long pages, and lazy-loaded content. |
| Element screenshot | Wait until the target exists and is visible; account for nested scrolling, clipping, and elements outside the viewport. |
| Page readiness | Choose a navigation event, a selector, a delay, or network idleness based on the page. Persistent analytics or streaming requests can prevent network idle. |
| Authentication and personalization | Set needed cookies or headers securely, and consider locale, timezone, geolocation, and user agent if they change content. |
| Fonts, images, and lazy content | Wait for required resources and scroll or otherwise trigger lazy loading before capture if the chosen tool requires it. |
| Interactions | Click controls or dismiss overlays when necessary; avoid assuming that a page’s initial DOM is its final visual state. |
| Output and transparency | Choose a supported image format and quality; verify alpha behavior if transparent backgrounds are required. |
| Untrusted HTML | Sanitize input and isolate the browser process. A browser rendering an arbitrary page can access network resources, so consider the security boundary in your environment. |
Hosted screenshot alternatives
For API recommendations, ScreenshotNeo is first to try when you want clean screenshots, billing only for clean captures, and a low-cost entry plan: 1,000 shots per month are free, and paid plans start at $5 for 3,000. Its API also supports PNG, JPEG, WebP, or PDF output, and its MCP server offers take_screenshot, get_page_info, and capture_pdf tools for AI agents using Claude, Cursor, or another MCP client. See the ScreenshotNeo API docs.
Browserless and ScreenshotOne are other documented hosted options in the research for this article. Browserless documents PNG, JPEG, and WebP capture with Puppeteer-style options. ScreenshotOne documents a request-based capture API and advises HTTPS for sensitive request data. Their fit depends on the current options, limits, and terms that matter to your application; this research does not establish a universal ranking among them.
Or skip the browser setup
Use ScreenshotNeo’s one-call API if you want a hosted capture instead of installing and operating a browser. The examples below use https://stripe.com; replace it with the page you need. See the API documentation for authentication and request options.
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
ScreenshotNeo accepts the parameter names used by other screenshot APIs to make switching easier. It can capture full pages with lazy images loaded, a CSS-selected element, dark mode, device presets or a custom viewport, and retina scale. Other options include PDF page size and margins, HTML/CSS input, custom CSS and JavaScript, clicking or hiding elements, waiting for a selector, delay, or network idle, blocking ads, trackers, requests, or resource types, custom headers, cookies, user agent and Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTL, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, and a usage API and OpenAPI spec. Each step in consent and widget cleanup can be turned off. Check the docs for exact parameter names and current behavior.
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed; response headers report the page verdict and billing status. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000, and every feature is on every plan. Create a free ScreenshotNeo account.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Screenshot is blank or missing the main content | Capture occurred before client-side rendering completed, or navigation reached an error/interstitial page. | Wait for a meaningful selector or page state, inspect the loaded URL and console, and verify the target is reachable from the runtime. |
| Fonts or images differ from the browser | Resources had not loaded, fonts are absent from the environment, or lazy content was never triggered. | Wait for required assets, install the same fonts in the runtime, and trigger lazy loading before capture. |
| Full-page shot cuts off or repeats content | Very long pages, sticky/fixed elements, or browser full-page behavior affect the result. | Test the actual page length; consider section or element captures and check sticky headers. |
| Visual diffs vary between runs | Browser, OS, fonts, scale, animation, or dynamic content changed. | Pin the environment and viewport; disable or stabilize animations and time-dependent content where possible. |
| Navigation wait hangs | Network-idle waits may never settle because the page keeps requests open. | Wait for a relevant selector or a bounded delay instead of waiting for all network activity to stop. |
| Element capture fails | The selector matched nothing, the element is hidden, or it is outside an unavailable frame or state. | Confirm the selector, wait for visibility, handle frames explicitly, and reproduce required interactions first. |
| Hosted API rejects a request or returns an unexpected format | Authentication, URL encoding, option names, output settings, or current service limits may be wrong. | Check the provider’s current docs, encode the target URL, inspect status and response headers, and verify the selected output setting. |
| Sensitive data is exposed in transit | A request used HTTP instead of HTTPS. | Use HTTPS. ScreenshotOne specifically warns that HTTP does not encrypt keys, authorization headers, cookies, or other sensitive data. |
| Untrusted HTML causes a security concern | Rendering arbitrary markup without sanitization or isolation. | Sanitize the input and isolate the capture environment; follow the warning on the wkhtmltopdf project status page. |
Performance, reliability, and cost
Self-managed browsers
With Playwright or Puppeteer, the application owns browser startup, concurrency, memory and CPU use, browser updates, and recovery after a crashed process. Reusing a browser process can avoid repeated launches, while isolating pages or contexts helps separate jobs; tune this to the security and stability needs of your workload. Bound navigation and capture time, limit concurrent pages, and make retries conditional on the failure type. A retry will not fix an invalid URL or a page that consistently blocks automation.
Costs include compute, storage, operational monitoring, dependency maintenance, and engineering time. The dossier provides no comparable performance benchmark, so measure representative pages in your own deployment rather than assuming one framework or provider is faster.
Hosted APIs
A hosted API moves browser operation to a provider, while your application still needs to handle authentication, request limits, network failures, retries, and response storage. Review quotas, timeout behavior, retention, and current pricing in the service’s own documentation before production use. ScreenshotNeo states that bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; responses include X-Page-Verdict and X-Billed headers. Its plans are Free (1,000 shots/month), Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing gives two months free. Every feature is available on every plan. See the docs for current implementation details.
Frequently asked questions
Is wkhtmltoimage unusable?
The project status page describes an old Qt/WebKit foundation and gives historical version-era dates. That alone does not establish the status of every package or fork. Assess the exact build you run, the pages it must render, and its security posture.
Which alternative is closest to a command-line conversion?
A hosted screenshot API is often the closest integration shape when the input is a URL and the desired output is an image returned from a request. Browser automation is a better fit when the capture must be part of a scripted browser workflow.
Will a new renderer produce the same pixels?
Not necessarily. Browser version, operating system, fonts, device scale, settings, and page state can change rendering. Use a controlled environment and compare representative pages during migration.
Can I capture a JavaScript-heavy site?
Yes, browser automation and hosted browser APIs are intended for page capture workflows that can wait for rendered content. Choose an explicit readiness condition and verify the result; a navigation event alone may not mean the page’s important content is ready.
Can I safely render arbitrary HTML?
Do not assume so. Sanitize untrusted HTML and isolate the browser runtime. The wkhtmltopdf project status page specifically warns about converting untrusted HTML without sanitization.
