# Website Screenshot API: Full-Page PNG, JPEG & PDF (`phaux/website-screenshots`) Actor

Screenshot web pages from a list of URLs as PNG, JPEG or PDF. Use when you need an image of a public page. Input: urls, optional format, fullPage, device, darkMode. Output per URL: screenshot file URL, final URL, HTTP status, title, size. Cookie banners hidden; failed pages not charged.

- **URL**: https://apify.com/phaux/website-screenshots.md
- **Developed by:** [Phaux](https://apify.com/phaux) (community)
- **Categories:** Developer tools, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.61 / 1,000 screenshots

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Website Screenshot API: Full-Page PNG, JPEG & PDF

A **website screenshot** tool you can call from the Console, the API or a schedule: capture **full page screenshots**, viewport shots or **URL to PDF** files for a list of public web pages, and get a direct download link for each one.

Take screenshots of any public web page as **PNG, JPEG or PDF**: the full scrolling page, only the viewport, one element or a pixel region, on desktop or an emulated phone or tablet, in light or dark mode. Cookie banners are hidden and lazy-loaded images are loaded before capture. Each file is saved to the run's key-value store, and its **direct download URL** is returned in the dataset.

**You pay only for successful screenshots.** Errors, timeouts, blocked pages and error pages (HTTP 4xx/5xx) are never charged.

### Quick start for AI agents and API users

- **What it does:** Screenshot web pages from a list of URLs as PNG, JPEG or PDF.
- **When to use it:** When you need an image of a public page.
- **Example input** (the form's prefill; it finishes in under a minute):

```json
{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ]
}
```

- **Example output:** one real dataset row from run `luDLGZY6UnHexrjFC` (2026-10-23), with long fields shortened and empty fields left out. Fields not shown are listed in the output table below.

```json
{
  "url": "https://en.wikipedia.org/wiki/Web_scraping",
  "finalUrl": "https://en.wikipedia.org/wiki/Web_scraping",
  "httpStatus": 200,
  "title": "Web scraping - Wikipedia",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/bn8uBJ9vFUhDiGolu/records/0001-en-wikipedia-org-wiki-Web-scraping-0dbff88b.png?signature=3m9Hvb...",
  "key": "0001-en-wikipedia-org-wiki-Web-scraping-0dbff88b.png",
  "format": "png",
  "contentType": "image/png",
  "bytes": 2362697,
  "fullPage": true,
  "viewport": {
    "width": 1280,
    "height": 800
  },
  "pageHeight": 10474,
  "darkMode": false,
  "cookieBannersHidden": 0,
  "stickyElementsAdjusted": 4,
  "adRequestsBlocked": 0
}
```

- **Cost (pay per event, Free-plan prices):** Screenshot $0.00089. Failed items are not charged.
- **From an AI agent:** with the Apify MCP server, call `call-actor` with actor `phaux/website-screenshots`, or load it as its own tool with `https://mcp.apify.com?tools=phaux/website-screenshots`. Read results with `get-dataset-items`.

### What does Website Screenshots do?

- Opens each URL in headless Chromium, waits for the page to settle, and captures it.
- Formats: `png`, `jpeg` (with `jpegQuality`) and `pdf` (paper size, landscape and background options).
- Full page or viewport, an element (`elementSelector`) or a region (`clip`). Full-page captures are cropped at **10,000 px** by default (`maxHeight`; raise it up to the 20,000 px hard cap). `truncatedAtPx` in the output tells you when a page was cut.
- Device emulation: iPhone 15 Pro / Pro Max, iPhone 14, Pixel 7 / 5, Galaxy S9+, iPad Pro 11, iPad Mini, Galaxy Tab S9, and desktop Chrome or Safari profiles. Each sets the viewport, pixel ratio, touch support and user agent.
- Dark mode, retina output (`deviceScaleFactor` 2 or 3), hiding your own selectors, and ad-network blocking.

### Why use it?

| Common screenshot problem | What this Actor does |
|---|---|
| Failed captures still cost credits | You're charged only when an image or PDF is actually saved. Failures go to `FAILED_URLS` with a reason, free of charge. |
| Half-loaded pages and spinners | It waits for `load`, then up to 3 s for network idle, then web fonts, then your `waitForSelector`, then `delayMs`. |
| Missing lazy-loaded images | It scrolls the page quickly (capped by `maxScrollMs`) and scrolls back to the top before capturing. |
| Sticky headers repeated or covering content | Fixed and sticky elements are shown once at the top by default, or hidden (`stickyElements`). |
| Need only part of the page | Use `maxHeight`, `fullPage: false`, `elementSelector` or `clip`. |
| Emoji render as boxes | Colour-emoji and CJK fonts are installed. |
| Hung runs | Each page has a 60 s limit by default. If a full-page capture takes over 30 s, the top 4,000 px are captured instead (`captureFallback: true`), so you still get a result. The browser restarts after a crash. |

Use cases: website monitoring and visual archives, SEO and ad audits, link previews and thumbnails, compliance evidence, and saving pages as PDF.

### How to use it

1. Add one or more page URLs to `urls`.
2. Pick a `format` and, if needed, `fullPage`, `device` or `darkMode`.
3. Run the Actor. Open the **Overview** dataset view to see each `screenshotUrl`, or download all files from the key-value store.
4. To call it from code, use the Apify API or the MCP server (see below).
5. To detect visual changes between runs, turn on `visualDiff` (see the tutorial below).

### Tutorial: visual regression testing and change monitoring with visual diff

Turn on **Visual diff** (`visualDiff: true`) and each new screenshot is compared with a baseline. The result tells you **what percentage of the page changed**, **where** it changed (a bounding box), and gives a **diff image** with the changed pixels in red. It's off by default, and **turning it on costs nothing extra**: you still pay per screenshot as usual.

**Where the baseline comes from:**

- **Previous run (default):** the last screenshot of the same URL with the same settings is kept in a named key-value store in *your* Apify account (`baselineStoreName`, default `website-screenshots-baselines`). The first run only saves baselines (`status: "no_baseline"`), and every later run compares with the one before.
- **An image you choose:** put public PNG/JPEG links in `baselineImageUrls`, in the same order as `urls`. For example, use a `screenshotUrl` from an earlier run, or an approved design export. Set `updateBaseline: false` to keep comparing with a fixed reference.

**Worked example: watching a fast-changing page (real runs, 22 Oct 2026).** We used the Hacker News front page, viewport only:

```json
{
  "urls": ["https://news.ycombinator.com/"],
  "fullPage": false,
  "visualDiff": true,
  "baselineStoreName": "vd-tutorial-hn"
}
```

1. **11:03 PM ET, first run** (`nmogl5moJDVeY3NjN`): `"status": "no_baseline"`, `"baselineUpdated": true`. The screenshot is saved as the baseline and there is nothing to compare yet.
2. **11:13 PM ET, same input** (`X5YAcj9MRublfZZ2t`, 6 s): compared with the 11:03 PM baseline:

```json
"visualDiff": {
  "status": "compared",
  "baselineSource": "store",
  "baselineTakenAt": "2026-10-23T03:03:56.232176Z",
  "percentChanged": 3.57,
  "changedPixels": 36553,
  "totalPixels": 1024000,
  "changedArea": {"x": 135, "y": 47, "width": 535, "height": 738},
  "newSize": {"width": 1280, "height": 800},
  "baselineSize": {"width": 1280, "height": 800},
  "sizeChanged": false,
  "pixelTolerance": 32,
  "diffImageUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/0000-news-ycombinator-com-f1f3bd09-diff.png",
  "baselineUpdated": true
}
```

In ten minutes, 3.57 % of the pixels changed. The diff image showed exactly what you would expect: several stories swapped places, and points, comment counts and "hours ago" values were updated. The header, the layout and the stories that stayed put came out faded grey with no red.

3. **Same page, fixed baseline** (`0PSff9Z2k8reaU8R0`): we passed the first run's `screenshotUrl` in `baselineImageUrls` with `updateBaseline: false`. It returned `"baselineSource": "url"` and `"percentChanged": 3.268`. This is how you compare against an approved reference image.

**Turn it into a monitor.** Create a Schedule (for example hourly or daily) with `visualDiff: true`. Then filter the dataset on `visualDiff.percentChanged`, or open the **Visual diff** dataset view to see the screenshot and the diff image side by side. You can also use an Apify integration (webhook, Slack, email, Zapier/Make) to act when `percentChanged` is above your threshold, for example 1 % for a landing page or 0.1 % for a pricing table.

**Tips for fewer false alarms:**

- Hide parts that always change (ads, carousels, clocks, "x minutes ago", cookie bars) with `hideSelectors`, or capture just one part with `elementSelector` or `clip`.
- Keep the same `device`, viewport, `fullPage` and `darkMode` between runs. The stored baseline is per URL *and* per settings, so a change in settings starts a new baseline instead of giving a false 100 %.
- `diffPixelTolerance` (default 32 of 255) ignores anti-aliasing and JPEG noise. Use 0 to count every pixel.
- If the page got taller or shorter, the extra area counts as changed and `sizeChanged` is `true`.
- PDF output can't be diffed. Use `png` or `jpeg`.
- A diff problem (for example an unreachable baseline URL) is reported in `visualDiff.error`. It never fails the screenshot itself.
- Baseline images are stored in your own account (one image per URL and settings), so they count toward your Apify storage like any named store.

### Input

```json
{
  "urls": ["https://apify.com", "https://en.wikipedia.org/wiki/Web_scraping"],
  "format": "png",
  "fullPage": true,
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "device": "",
  "darkMode": false,
  "hideCookieBanners": true,
  "waitForSelector": "",
  "delayMs": 500
}
```

Only `urls` is required; every other option has a working default.

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array | required | Public web pages to capture (http/https). Duplicates are removed. No login or credentials are supported. |
| `format` | string | `"png"` | Image (PNG/JPEG) or a PDF of the page. Allowed: `png`, `jpeg`, `pdf`. |
| `fullPage` | boolean | `true` | Capture the whole scrollable page. Off = only the visible viewport. |
| `maxHeight` | integer | `10000` | Crop full-page captures to this height from the top, in pixels. Default 10,000 px; raise it up to 20,000 px (hard cap). 0 = up to the 20,000 px cap. When a page is cut, truncatedAtPx in the output says where. |
| `viewportWidth` | integer | `1280` | Browser window width in CSS pixels (ignored when a device is chosen). |
| `viewportHeight` | integer | `800` | Browser window height; this is the image height when Full page is off. |
| `deviceScaleFactor` | string | `"1"` | Device pixel ratio (ignored when a device is chosen). Allowed: `1`, `2`, `3`. |
| `device` | string | `""` | Emulate a phone/tablet: its viewport, pixel ratio, touch and user agent. Allowed: `iPhone 15 Pro`, `iPhone 15 Pro Max`, `iPhone 14`, `Pixel 7`, `Pixel 5`, `Galaxy S9+`, `iPad Pro 11`, `iPad Mini`, `Galaxy Tab S9`, `Desktop Chrome`, `Desktop Chrome HiDPI`, `Desktop Safari`. |
| `darkMode` | boolean | `false` | Ask the page for its dark colour scheme (prefers-color-scheme: dark). |
| `hideCookieBanners` | boolean | `true` | Hide common consent pop-ups (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, Sourcepoint and generic cookie bars). Nothing is clicked or accepted. |
| `blockAds` | boolean | `true` | Block requests to common ad/tracking networks for cleaner, faster captures. |
| `waitUntil` | string | `"load"` | Navigation event to wait for before the steps below. Allowed: `load`, `domcontentloaded`, `networkidle`. |
| `waitForNetworkIdleMs` | integer | `3000` | Wait up to this long for network requests to settle (prevents half-loaded screenshots). It never fails the capture. |
| `waitForSelector` | string | — | CSS selector that must be visible before capturing, e.g. '#main' or '.product-grid'. |
| `waitForSelectorTimeoutSecs` | integer | `10` | Fail (not charged) if the selector does not appear in time. |
| `delayMs` | integer | `500` | Fixed delay right before the capture, for animations. |
| `scrollToLoadLazyContent` | boolean | `true` | For full-page captures, scroll down quickly (time-capped) to trigger lazy loading, then scroll back to the top. |
| `maxScrollMs` | integer | `4000` | Hard cap on scrolling time so long/infinite pages stay fast. |
| `stickyElements` | string | `"unstick"` | How fixed/sticky headers and widgets are handled in full-page captures. Allowed: `unstick`, `hide`, `keep`. |
| `hideSelectors` | array | — | CSS selectors to hide before capturing (chat widgets, banners, ...). |
| `elementSelector` | string | — | CSS selector; capture only the first matching element. |
| `clip` | object | — | Region in page pixels: {"x":0,"y":0,"width":1280,"height":600}. |
| `jpegQuality` | integer | `80` | JPEG compression quality from 1 (smallest file) to 100 (best quality). Used only when format is jpeg. |
| `pdfFormat` | string | `"A4"` | Paper size of the PDF. Used only when format is pdf. Allowed: `A4`, `Letter`, `Legal`, `A3`, `Tabloid`. |
| `pdfLandscape` | boolean | `false` | Print the PDF in landscape orientation. Used only when format is pdf. |
| `pdfPrintBackground` | boolean | `true` | Include background colours/images in PDFs. |
| `visualDiff` | boolean | `false` | Compare each PNG/JPEG screenshot with a baseline and add `visualDiff` (percentChanged, changedArea, diffImageUrl). Off by default; no extra charge. |
| `baselineStoreName` | string | `"website-screenshots-baselines"` | Named key-value store in your account that keeps the last screenshot per URL and settings. |
| `baselineImageUrls` | array | — | Optional baseline image links, in the same order as `urls`; they take priority over the stored baseline. |
| `updateBaseline` | boolean | `true` | Save each new screenshot as the next baseline. Off = keep a fixed reference. |
| `diffPixelTolerance` | integer | `32` | Per-channel colour difference (0–255) above which a pixel counts as changed. |
| `failOnHttpError` | boolean | `true` | On: error pages are not captured and not charged. Off: capture them anyway. |
| `navigationTimeoutSecs` | integer | `30` | How long to wait for the page to load on each attempt, in seconds. Slow pages that exceed it are retried (maxRetries) and then reported in FAILED_URLS, not charged. |
| `maxRetries` | integer | `1` | Retries for timeouts and browser errors. |
| `maxConcurrency` | integer | — | Pages captured at the same time. Default: 2 per GB of memory (2 at the default 1 GB), up to 8. |
| `proxyConfiguration` | object | `{"useApifyProxy": false}` | Usually not needed. Use a proxy only for geo-specific views. Datacenter Apify Proxy or your own proxy URLs only; residential proxy isn't supported. |
| `captureTimeoutSecs` | integer | — | Time allowed for rendering/encoding the image itself. Default 30 s. If a full-page capture times out, it is retried once cropped to the top 4,000 px (captureFallback: true in the output). |
| `maxLoadWaitMs` | integer | `10000` | With waitUntil=load: after the DOM is ready, wait at most this long for the load event (slow third-party assets no longer stall the run). |
| `detectBlockedPages` | boolean | `true` | Treat bot walls ('Access denied', 'Just a moment...', 'Unknown Error') and blank renders as failures, so you are not charged for them. |
| `pageTimeoutSecs` | integer | — | Total time budget per URL attempt, default 60 s; pages that keep the browser busy longer are abandoned (not charged). |

### Output example

One dataset item per successful screenshot:

```json
{
  "url": "https://apify.com",
  "finalUrl": "https://apify.com/",
  "httpStatus": 200,
  "title": "Apify: Full-stack web scraping and data extraction platform",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/0000-apify-com-7c0cb881.png",
  "key": "0000-apify-com-7c0cb881.png",
  "format": "png",
  "contentType": "image/png",
  "bytes": 977392,
  "fullPage": true,
  "device": null,
  "viewport": {"width": 1280, "height": 800},
  "pageHeight": 10036,
  "darkMode": false,
  "cookieBannersHidden": 5,
  "stickyElementsAdjusted": 17,
  "adRequestsBlocked": 17,
  "loginFormDetected": false,
  "durationMs": 11934,
  "timingsMs": {"domReady": 800, "load": 700, "networkIdle": 1300, "capture": 650},
  "truncatedAtPx": null,
  "takenAt": "2026-10-20T11:20:00Z"
}
```

### Output fields

| Field | Type | Description |
|---|---|---|
| `url` | string | The URL you submitted. |
| `finalUrl` | string or null | URL after redirects. |
| `httpStatus` | number or null | HTTP status of the page. |
| `title` | string or null | Page title. |
| `screenshotUrl` | string | Direct URL of the saved screenshot in the run's key-value store. |
| `key` | string | Key-value store record key of the screenshot. |
| `format` | string | png, jpeg or pdf. |
| `contentType` | string | MIME type of the file. |
| `bytes` | number | File size in bytes. |
| `fullPage` | boolean or null | Whether the full page was captured. |
| `device` | string or null | Emulated device name, if any. |
| `viewport` | object or null | Viewport width and height in px. |
| `pageHeight` | number or null | Rendered page height in px. |
| `darkMode` | boolean or null | Whether dark mode was emulated. |
| `cookieBannersHidden` | number or null | Number of cookie banners hidden. |
| `stickyElementsAdjusted` | number or null | Sticky headers/footers adjusted for full-page capture. |
| `adRequestsBlocked` | number or null | Ad/tracker requests blocked. |
| `loginFormDetected` | boolean or null | Whether the page showed a login form. |
| `truncatedAtPx` | number or null | Height in px at which the capture was cut (maxHeight, the 20,000 px cap, or the 4,000 px fallback); null if the whole page was captured. |
| `captureFallback` | boolean | True if the full capture timed out and the top 4,000 px were captured instead. |
| `lazyScrollMs` | number or null | Time spent scrolling to load lazy images, ms. |
| `durationMs` | number | Total capture time, ms. |
| `timingsMs` | object or null | Per-step timings, ms. |
| `takenAt` | string | Capture time (ISO 8601). |

The run also saves `FAILED_URLS` (each failed URL with its error code and reason) and `RUN_STATS` in the key-value store.

### How much does it cost?

Pay per event: you pay only the **per-screenshot price** below, plus a start fee of $0.00001 per run per GB of memory ($0.00001 at the default 1 GB). **Apify platform usage (compute, storage, data transfer) is included**, so there is nothing else to pay. Failed captures are free.

| Apify plan | Price per screenshot | Per 1,000 screenshots |
|---|---|---|
| Free | $0.00089 | $0.89 |
| Bronze (Starter) | $0.00083 | $0.83 |
| Silver (Scale) | $0.00074 | $0.74 |
| Gold, Platinum, Diamond (Business+) | $0.00061 | $0.61 |

Example: 1,000 screenshots on the Free plan cost $0.89 in total. Memory can be set from 1 GB (default) to 4 GB; more memory captures more pages in parallel (2 pages per GB) but doesn't change the price.

### Use with AI agents, MCP and the API

- **MCP:** add `https://mcp.apify.com?tools=phaux/website-screenshots` to any MCP client and call it as a tool. The dataset returns a direct `screenshotUrl` for each capture.
- **API:** `POST https://api.apify.com/v2/acts/phaux~website-screenshots/run-sync-get-dataset-items` with `{"urls": [...]}` returns the items, including file URLs.
- Each input field's type, default and allowed values are documented in the input schema.

### Data and compliance

- Only public pages are captured, exactly as any visitor sees them. Logins, cookies, custom headers and custom scripts aren't supported, on purpose.
- URLs with embedded credentials and addresses on private or internal networks are refused.
- You are responsible for having the right to capture the pages you submit.

### Limits

- Each URL has a hard time limit (`pageTimeoutSecs`, default 60 s). Pages that keep the browser busy past it are abandoned and not charged.
- Full-page captures stop at 10,000 px by default and at most 20,000 px (`maxHeight`). Very long pages are cropped from the top.
- Each GB of memory captures 2 pages in parallel (all modes).
- Some sites block automated browsers (HTTP 403). They're reported as failures and aren't charged; some work through the optional proxy.
- For very long pages, JPEG is 5–10× smaller than PNG.

### FAQ

- **Do I pay for compute or storage on top?** No. You pay only the per-screenshot price (and the tiny start fee); Apify platform usage is included.
- **Why is my long page cut off?** Full-page captures are cropped at 10,000 px by default to keep them fast. Set `maxHeight` up to 20,000 px for longer pages. If `captureFallback` is true, the full capture timed out and the top 4,000 px were saved instead.
- **Where are the files?** In the run's default key-value store. `screenshotUrl` in each dataset item is a direct link.
- **Can I capture a page behind a login?** No. Only public pages are supported.
- **Can it tell me when a page changes?** Yes. Turn on `visualDiff`: each screenshot is compared with the previous one (or with an image you provide), and you get the percent changed and a diff image, at no extra charge.
- **Something isn't working?** Open an issue on the Issues tab with the run ID.

# Actor input Schema

## `urls` (type: `array`):

Public web pages to capture (http/https). Duplicates are removed. No login or credentials are supported. Example: \["https://apify.com", "https://en.wikipedia.org/wiki/Web_scraping"].

## `format` (type: `string`):

Image (PNG/JPEG) or a PDF of the page. Default: "png".

## `fullPage` (type: `boolean`):

Capture the whole scrollable page. Off = only the visible viewport. Default: true.

## `maxHeight` (type: `integer`):

Crop full-page captures to this height from the top, in pixels. Default 10,000 px; raise it up to 20,000 px (hard cap). 0 = up to the 20,000 px cap. When a page is cut, truncatedAtPx in the output says where.

## `viewportWidth` (type: `integer`):

Browser window width in CSS pixels (ignored when a device is chosen). Default: 1280.

## `viewportHeight` (type: `integer`):

Browser window height; this is the image height when Full page is off. Default: 800.

## `deviceScaleFactor` (type: `string`):

Device pixel ratio (ignored when a device is chosen). Default: "1".

## `device` (type: `string`):

Emulate a phone/tablet: its viewport, pixel ratio, touch and user agent. Default: empty.

## `darkMode` (type: `boolean`):

Ask the page for its dark colour scheme (prefers-color-scheme: dark). Default: false.

## `hideCookieBanners` (type: `boolean`):

Hide common consent pop-ups (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, Sourcepoint and generic cookie bars). Nothing is clicked or accepted. Default: true.

## `blockAds` (type: `boolean`):

Block requests to common ad/tracking networks for cleaner, faster captures. Default: true.

## `waitUntil` (type: `string`):

Navigation event to wait for before the steps below. Default: "load".

## `waitForNetworkIdleMs` (type: `integer`):

Wait up to this long for network requests to settle (prevents half-loaded screenshots). It never fails the capture. Default: 3000.

## `waitForSelector` (type: `string`):

CSS selector that must be visible before capturing, e.g. '#main' or '.product-grid'.

## `waitForSelectorTimeoutSecs` (type: `integer`):

Fail (not charged) if the selector does not appear in time. Default: 10.

## `delayMs` (type: `integer`):

Fixed delay right before the capture, for animations. Default: 500.

## `scrollToLoadLazyContent` (type: `boolean`):

For full-page captures, scroll down quickly (time-capped) to trigger lazy loading, then scroll back to the top. Default: true.

## `maxScrollMs` (type: `integer`):

Hard cap on scrolling time so long/infinite pages stay fast. Default: 4000.

## `stickyElements` (type: `string`):

How fixed/sticky headers and widgets are handled in full-page captures. Default: "unstick".

## `hideSelectors` (type: `array`):

CSS selectors to hide before capturing (chat widgets, banners, ...). Example: \["#newsletter-popup", ".chat-widget"].

## `elementSelector` (type: `string`):

CSS selector; capture only the first matching element. Example: "main".

## `clip` (type: `object`):

Region in page pixels: {"x":0,"y":0,"width":1280,"height":600}.

## `jpegQuality` (type: `integer`):

JPEG compression quality from 1 (smallest file) to 100 (best quality). Used only when format is jpeg. Default: 80.

## `pdfFormat` (type: `string`):

Paper size of the PDF. Used only when format is pdf. Default: "A4".

## `pdfLandscape` (type: `boolean`):

Print the PDF in landscape orientation. Used only when format is pdf. Default: false.

## `pdfPrintBackground` (type: `boolean`):

Include background colours/images in PDFs. Default: true.

## `visualDiff` (type: `boolean`):

Off by default. When on, each new PNG/JPEG screenshot is compared with a baseline: the previous screenshot of the same URL and settings (kept in a key-value store in your account), or the image URL you give below. Each item gets 'visualDiff' with percentChanged, the changed area and a diff image link (changed pixels in red). No extra charge: you pay per screenshot as usual.

## `baselineStoreName` (type: `string`):

Named key-value store in YOUR Apify account that keeps the last screenshot per URL and settings. The first run with a new store has nothing to compare (status 'no_baseline') and only saves baselines. Use different names for separate monitors. Letters, digits and '-' only. Example: "website-screenshots-baselines". Default: "website-screenshots-baselines".

## `baselineImageUrls` (type: `array`):

Optional. Public PNG/JPEG URLs to compare against instead of the stored baseline, in the same order as the URLs above (line 1 is the baseline for URL 1, and so on; leave a line out to use the stored baseline). For example, a screenshotUrl from an earlier run.

## `updateBaseline` (type: `boolean`):

On (default): after comparing, each new screenshot replaces the stored baseline, so the next run shows changes since this run. Off: keep the old baseline (compare every run with a fixed reference).

## `diffPixelTolerance` (type: `integer`):

A pixel counts as changed when a colour channel differs by more than this. 32 ignores anti-aliasing and JPEG noise; 0 counts every tiny change. Default: 32.

## `failOnHttpError` (type: `boolean`):

On: error pages are not captured and not charged. Off: capture them anyway. Default: true.

## `navigationTimeoutSecs` (type: `integer`):

How long to wait for the page to load on each attempt, in seconds. Slow pages that exceed it are retried (maxRetries) and then reported in FAILED_URLS, not charged. Default: 30.

## `maxRetries` (type: `integer`):

Retries for timeouts and browser errors. Default: 1.

## `maxConcurrency` (type: `integer`):

Pages captured at the same time. Default: 2 per GB of memory (2 at the default 1 GB), up to 8.

## `proxyConfiguration` (type: `object`):

Usually not needed. Use a proxy only for geo-specific views. Datacenter Apify Proxy or your own proxy URLs only; residential proxy isn't supported.

## `captureTimeoutSecs` (type: `integer`):

Time allowed for rendering/encoding the image itself. Default 30 s. If a full-page capture times out, it is retried once cropped to the top 4,000 px (captureFallback: true in the output).

## `maxLoadWaitMs` (type: `integer`):

With waitUntil=load: after the DOM is ready, wait at most this long for the load event (slow third-party assets no longer stall the run). Default: 10000.

## `detectBlockedPages` (type: `boolean`):

Treat bot walls ('Access denied', 'Just a moment...', 'Unknown Error') and blank renders as failures, so you are not charged for them. Default: true.

## `pageTimeoutSecs` (type: `integer`):

Total time budget per URL attempt, default 60 s; pages that keep the browser busy longer are abandoned (not charged).

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ],
  "format": "png",
  "fullPage": true,
  "maxHeight": 10000,
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "deviceScaleFactor": "1",
  "device": "",
  "darkMode": false,
  "hideCookieBanners": true,
  "blockAds": true,
  "waitUntil": "load",
  "waitForNetworkIdleMs": 3000,
  "waitForSelectorTimeoutSecs": 10,
  "delayMs": 500,
  "scrollToLoadLazyContent": true,
  "maxScrollMs": 4000,
  "stickyElements": "unstick",
  "jpegQuality": 80,
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfPrintBackground": true,
  "visualDiff": false,
  "baselineStoreName": "website-screenshots-baselines",
  "updateBaseline": true,
  "diffPixelTolerance": 32,
  "failOnHttpError": true,
  "navigationTimeoutSecs": 30,
  "maxRetries": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxLoadWaitMs": 10000,
  "detectBlockedPages": true
}
```

# Actor output Schema

## `screenshots` (type: `string`):

All successful screenshots with direct file URLs (default dataset).

## `files` (type: `string`):

Screenshot files in the default key-value store.

## `failedUrls` (type: `string`):

URLs that failed (not charged), with error codes.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://apify.com",
        "https://en.wikipedia.org/wiki/Web_scraping"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("phaux/website-screenshots").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "urls": [
        "https://apify.com",
        "https://en.wikipedia.org/wiki/Web_scraping",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("phaux/website-screenshots").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ]
}' |
apify call phaux/website-screenshots --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,phaux/website-screenshots"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/VW8nkMW6BGnfRKUkZ/builds/jlvn8bl8K7fGSKhHY/openapi.json
