Website Screenshot API avatar

Website Screenshot API

Pricing

from $5.00 / 1,000 screenshots

Go to Apify Store
Website Screenshot API

Website Screenshot API

Screenshot any list of URLs as PNG, JPEG or WebP: desktop, mobile, tablet or custom size, full page or a single element. Hides cookie banners and ads. Pay only for successful screenshots.

Pricing

from $5.00 / 1,000 screenshots

Rating

0.0

(0)

Developer

Djaber Guassmi

Djaber Guassmi

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Take screenshots of any list of web pages: desktop, mobile, tablet or a custom size; the visible viewport, the full scrolling page, or a single element. Get PNG, JPEG or WebP images with cookie banners and ads removed. You pay only for screenshots that succeed. Failed URLs cost nothing.

What it does

For each URL in urls, the Actor:

  1. Opens the page in a real Chromium browser that emulates the device you picked.
  2. Waits until the page is loaded (waitUntil), without hanging on sites that never stop loading.
  3. Removes cookie and consent popups and ads, and loads lazy images if you want the full page.
  4. Saves the image to the run's key-value store.
  5. Adds one dataset item per URL with the image link, page title, image size and an ok/failed status plus an error message when something went wrong.

One bad URL never stops the run. Temporary errors (timeouts, network errors, HTTP 429/5xx) are retried with exponential backoff, and every attempt has a hard time limit.

Why use this instead of other screenshot Actors

It was built to fix the most common complaints in reviews and issues of other screenshot tools:

Common complaintWhat this Actor does
"It still used up my credits" when the result didn't workPay per successful screenshot. Failed, skipped and 404 URLs are never charged.
Slow and expensive (minutes and cents per screenshot)A fixed, low price per screenshot. One shared browser, pages captured in parallel, ads blocked.
Full page only shows the part above the fold; missing lazy-loaded imagesReal full-page capture in one pass, after scrolling through the page so lazy images load.
Endless scrolling makes runs take foreverLazy-load scrolling is limited to 10 seconds and maxFullPageHeight.
Cookie banners cover the contentBanners are dismissed or hidden by default, and the scroll lock they leave behind is removed.
Sticky headers repeated across the imageNo stitching, so a sticky header appears once, at the top. Hide it completely with hideSelectors.
Can't set the image height / can't capture only the top or one partcustom device with any width/height, or selector to capture one element.
Runs time out or hang foreverPer-attempt timeout, and a "soft" wait: if a page never reaches networkidle, it's captured anyway.
Out-of-memory crashes and frozen runsParallelism is automatically limited to what the run's memory and CPU can handle.
Emoji and Asian text render as boxesEmoji and CJK fonts are installed.
Certificate errors (ERR_CERT_AUTHORITY_INVALID)Pages with invalid or self-signed certificates are still captured.
No dataset output, hard to use from the APIEvery URL produces one dataset item, so run-sync-get-dataset-items returns everything you need.

Input example

Only urls is required. Everything else is optional.

{
"urls": ["https://apify.com", "news.ycombinator.com", "https://example.com/missing-page"],
"device": "mobile",
"fullPage": true,
"format": "webp",
"quality": 80,
"waitUntil": "load",
"extraDelayMs": 500,
"hideCookieBanners": true,
"blockAds": true,
"darkMode": false,
"concurrency": 5
}

All input fields

FieldTypeDefaultDescription
urlsstring[](required)Pages to capture. https:// is added if missing.
devicedesktop | mobile | tablet | customdesktopDesktop 1920x1080; mobile 390x844 @2x; tablet 820x1180 @2x; custom uses width/height/deviceScaleFactor.
width, heightinteger1920, 1080Viewport size for custom.
deviceScaleFactornumber1Pixel ratio for custom (1 to 3).
fullPagebooleanfalseCapture the whole page instead of the viewport.
selectorstring(none)CSS selector: capture only this element.
formatpng | jpeg | webppngImage format.
qualityinteger801 to 100, for jpeg and webp.
waitUntilload | domcontentloaded | networkidleloadLoad event to wait for (soft: never fails the URL).
extraDelayMsinteger0Extra wait before the capture.
hideCookieBannersbooleantrueDismiss or hide cookie and consent popups.
blockAdsbooleantrueBlock ad and tracker requests and hide empty ad slots.
darkModebooleanfalseEmulate prefers-color-scheme: dark.
concurrencyinteger5Max pages in parallel; capped at 2 per CPU core (2 GB = 1 page, 8 GB = 4).
timeoutSecsinteger60Time limit for one attempt at one URL.
maxRetriesinteger1Retries for temporary errors, with 1 s, 2 s, 4 s... backoff.
hideSelectorsstring[][]Extra CSS selectors to hide (chat widgets, sticky headers...).
scrollToLoadLazyContentbooleantrueWith fullPage, scroll first so lazy content loads.
maxFullPageHeightinteger15000Height cap in CSS pixels for full-page and element shots.
proxyConfigurationobject(none)Optional proxy for sites that block data centers.

Output example

One dataset item per input URL, in the same form for successes and failures:

[
{
"url": "https://apify.com",
"finalUrl": "https://apify.com/",
"screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/screenshot_0001_apify-com.webp",
"screenshotKey": "screenshot_0001_apify-com.webp",
"title": "Apify: Full-stack web scraping and data extraction platform",
"width": 780,
"height": 14230,
"format": "webp",
"device": "mobile",
"status": "ok",
"error": null,
"httpStatus": 200,
"truncated": false,
"attempts": 1,
"takenAt": "2026-10-17T19:30:12.511Z",
"billedAs": "full-page-screenshot"
},
{
"url": "https://example.com/missing-page",
"finalUrl": null,
"screenshotUrl": null,
"screenshotKey": null,
"title": null,
"width": null,
"height": null,
"format": "webp",
"device": "mobile",
"status": "failed",
"error": "HTTP 404 Not Found",
"httpStatus": 404,
"truncated": false,
"attempts": 1,
"takenAt": "2026-10-17T19:30:09.004Z",
"billedAs": null
}
]
  • screenshotUrl opens the image directly. The same file is in the run's default key-value store under screenshotKey (screenshot_<position in input>_<host>.<format>).
  • width and height are the real pixel dimensions of the image (viewport x pixel ratio).
  • status is ok only when the image was stored. Only ok items are charged; billedAs says whether as screenshot or full-page-screenshot (null = not charged).
  • truncated: true means the page was taller than maxFullPageHeight and was cut off.

Pricing

This Actor uses pay-per-event pricing:

EventPriceWhen it's charged
screenshot$0.005 per screenshot ($5 per 1,000)Viewport or element (selector) screenshot, once the image is stored
full-page-screenshot$0.01 per full-page screenshot ($10 per 1,000)fullPage: true screenshot, once the image is stored

Full-page screenshots cost more because the whole page is rendered and encoded (often 10-15x the pixels of one screen). If you only need what's visible, leave fullPage off. Each dataset item's billedAs field shows which event it was charged as.

  • Failed URLs (timeouts, 404s, unknown domains, missing selector, invalid URL) are never charged.
  • Retries are free: a URL that succeeds on its third attempt is charged once.
  • Platform compute is included in the price, so slow sites don't cost you more.
  • If you set a maximum cost per run, the Actor stops starting new screenshots when the limit is reached and reports the rest as failed with the reason "maximum cost per run was reached" (not charged).

Use from the API, code or AI agents

For AI agents (Apify MCP server): call this Actor with {"urls": [...]} and read the dataset items. Every input URL gets exactly one item. Check status: when it's "ok", use screenshotUrl. When it's "failed", error says why in plain language and nothing was charged. Use device: "mobile" for a phone view, selector for one element, and fullPage: true only when the whole page is needed (it costs $0.01 instead of $0.005).

One HTTP call, results back directly (waits until the run finishes):

curl -X POST "https://api.apify.com/v2/acts/<username>~website-screenshot-api/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"urls": ["https://apify.com"], "fullPage": true, "format": "jpeg"}'

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<username>/website-screenshot-api').call({ urls: ['https://apify.com'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => (i.status === 'ok' ? i.screenshotUrl : `${i.url}: ${i.error}`)));

Monitoring pages: schedule the Actor (for example daily) to keep a visual history of a page.

Tips and limitations

  • SPAs and dashboards: use waitUntil: "networkidle" and maybe extraDelayMs: 1000.
  • Element not found: the URL fails with No element matches selector "..." and isn't charged. Check the selector in your browser's DevTools.
  • Bot protection: some sites (Cloudflare challenges, login walls) answer with 403 or a challenge page. A 403 is reported as failed; try proxyConfiguration with residential proxies.
  • HTTP errors: pages that answer with HTTP 4xx/5xx are reported as failed and not charged, even if they show an error page.
  • Very long pages are cut at maxFullPageHeight (default 15,000 px). WebP images can't be taller than 16,383 pixels, so WebP full-page shots are limited to that height.
  • Cookie banner removal covers the common consent platforms and thousands of site-specific banners, but not every one in existence. Add stubborn ones to hideSelectors.