# Website Screenshot API — Full-Page, Mobile & PDF Captures (`reverend_sal/website-screenshot`) Actor

Take full-page website screenshots in bulk with real Chrome: desktop, laptop, tablet and mobile in one run, PNG/JPEG/PDF, element screenshots, dark mode, auto-hidden cookie banners and lazy-load scrolling. Public image URLs, pay per screenshot.

- **URL**: https://apify.com/reverend_sal/website-screenshot.md
- **Developed by:** [North Eng](https://apify.com/reverend_sal) (community)
- **Categories:** Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 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.

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, Mobile & PDF Captures

**Website Screenshot API** captures websites in bulk with real Chrome: full-page or viewport, desktop, laptop, tablet and mobile in one run, PNG, JPEG or PDF. Cookie banners are hidden, lazy-loaded images are scrolled into view and animations are frozen, so the screenshots look like what a visitor sees. Every image gets a public URL you can open, download or embed.

### What it does

- **Bulk URLs × devices**: each URL is captured once per selected device: desktop (1920×1080), laptop (1366×768), tablet (iPad, 2× pixels) and mobile (iPhone, 2× pixels, mobile user agent).
- **Full-page screenshots**: the whole scrollable page, with a configurable maximum height for endless pages.
- **PNG, JPEG or PDF**: lossless PNG, smaller JPEG with adjustable quality, or a printable A4 PDF of the page.
- **Element screenshots**: capture just `#pricing`, `.hero` or any other CSS selector.
- **Clean captures**: hides common consent pop-ups (OneTrust, Cookiebot, Quantcast, Didomi, Usercentrics and more) plus any selectors you list, such as chat widgets.
- **Timing control**: wait for `load`, `domcontentloaded` or `networkidle`, wait for a selector, and add an extra delay.
- **Dark mode and custom viewports**: emulate `prefers-color-scheme: dark`, set any width/height and pixel density up to 3×.
- **Safe by design**: localhost, private-network and cloud-metadata addresses are refused.

### Why use it

- **Visual monitoring and archiving**: snapshot landing pages, competitors' pricing pages or your own releases on a schedule.
- **Responsive design QA**: see the same page on desktop, tablet and mobile in one run.
- **Thumbnails and previews**: generate link previews, portfolio images or social cards.
- **AI agents**: give a vision model a screenshot of any page through the Apify API or MCP.

### How to use

1. Add one or more **Website URLs**.
2. Pick **Devices** and a **Format** (PNG by default).
3. Click **Start**. Open the **Screenshots** tab of the output to see the images, or download them from the key-value store.

### Input

| Field | Default | What it does |
|---|---|---|
| `urls` | `https://apify.com` | Pages to capture. |
| `devices` | `["desktop"]` | Any of `desktop`, `laptop`, `tablet`, `mobile`. One screenshot per URL per device. |
| `fullPage` | `true` | Capture the whole scrollable page instead of the first screen. |
| `format` | `png` | `png`, `jpeg` or `pdf`. |
| `jpegQuality` | 80 | 1–100, JPEG only. |
| `selector` | none | CSS selector: capture only the first matching element. |
| `viewportWidth` / `viewportHeight` | device default | Custom viewport in CSS pixels (up to 3840×2160). |
| `deviceScaleFactor` | device default | Pixel density 1–3. |
| `maxHeight` | 10000 | Full-page captures are cut at this height (CSS px, max 20000). |
| `darkMode` | `false` | Emulate dark mode. |
| `hideCookieBanners` | `true` | Hide common consent banners. |
| `hideSelectors` | none | Extra CSS selectors to hide, e.g. `#intercom-container`. |
| `scrollPage` | `true` | Scroll through the page first so lazy images load. |
| `waitUntil` | `load` | `load`, `domcontentloaded` or `networkidle`. |
| `waitForSelector` | none | Wait until this selector is visible before capturing. |
| `delaySecs` | 1 | Extra wait before capturing (0–30 s). |
| `timeoutSecs` | 60 | Page-load timeout per screenshot (10–180 s). |
| `maxConcurrency` | 3 | Screenshots taken in parallel (1–10). |

Example: a landing page on three devices as JPEG, without the chat widget:

```json
{
  "urls": [{ "url": "https://www.bbc.com/news" }, { "url": "https://github.com" }],
  "devices": ["desktop", "tablet", "mobile"],
  "format": "jpeg",
  "jpegQuality": 70,
  "hideSelectors": ["#intercom-container"]
}
```

Element screenshot:

```json
{ "urls": [{ "url": "https://example.com" }], "selector": "p", "fullPage": false }
```

### Output

Each screenshot is saved to the run's key-value store and listed in the dataset. Real example from the default input (Oct 22 2026):

```json
{
  "url": "https://apify.com/",
  "finalUrl": "https://apify.com/",
  "title": "Apify: Marketplace of ready-to-run tools for AI",
  "device": "desktop",
  "format": "png",
  "fullPage": true,
  "width": 1920,
  "height": 9932,
  "truncatedAtMaxHeight": false,
  "fileSizeBytes": 1025335,
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/apify.com-b2cbecea-desktop.png",
  "keyValueStoreKey": "apify.com-b2cbecea-desktop.png",
  "statusCode": 200,
  "durationMs": 4975,
  "error": null,
  "errorCode": null,
  "takenAt": "2026-10-23T00:07:43.086Z"
}
```

`width` and `height` are in image pixels (CSS pixels × pixel density); they are `null` for PDFs. `truncatedAtMaxHeight` is `true` when the page was taller than `maxHeight` (or than the 120-megapixel safety limit for very tall high-density captures) and the image was cut.

Failed screenshots are listed with `error` and `errorCode` and are **not charged**:

```json
{
  "url": "https://httpbin.org/status/404",
  "device": "desktop",
  "format": "png",
  "error": "HTTP 404",
  "errorCode": "HTTP_404"
}
```

Error codes: `HTTP_404` (and other statuses ≥ 400), `TIMEOUT`, `NETWORK_ERROR` (DNS, connection or TLS failure), `SELECTOR_NOT_FOUND`, `BLOCKED_URL` (the URL or a redirect resolves to a private, local or cloud-metadata address), `NAVIGATION_FAILED`. A run summary is saved to the key-value store as `OUTPUT`.

### Pricing

Pay per screenshot. Only successful captures are charged:

| Event | Price |
|---|---|
| Screenshot taken (one URL on one device, any format) | **$0.002** ($2 per 1,000) |
| Actor start | Apify default ($0.00005) |

Examples: 100 URLs on desktop cost **$0.20**; 100 URLs on desktop + mobile cost **$0.40**. Failed pages cost nothing. Set a max cost per run and the Actor stops when the budget is reached, keeping the screenshots it already took.

### FAQ

**Can it screenshot pages behind a login?**
No. It captures public pages only.

**Why is my mobile screenshot wider than 780 px?**
Pages without a mobile `viewport` meta tag are laid out at 980 px wide by mobile browsers. The Actor shows what a real phone shows.

**Does it remove every pop-up?**
It hides the common consent managers automatically. For other pop-ups, add their selectors to **Hide elements**.

**Can I get a screenshot at a fixed size, like 1200×630?**
Yes: set `viewportWidth` 1200, `viewportHeight` 630 and turn off `fullPage`.

### Limitations

- Very long pages are cut at `maxHeight` (default 10,000 px).
- Some sites block automated browsers or show a CAPTCHA; the screenshot then shows that page.
- Private, local and cloud-metadata addresses are refused after DNS resolution, for the page, every redirect, sub-resource and WebSocket. Sites on a private network (intranets, staging behind a VPN) cannot be captured.

# Actor input Schema

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

Pages to capture. Each URL is screenshotted once per selected device.

## `devices` (type: `array`):

Capture each URL on these devices (each device is a separate screenshot). Desktop 1920×1080, laptop 1366×768, tablet 820×1180 (iPad, 2× pixels), mobile 390×844 (iPhone, 2× pixels).

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

Capture the whole scrollable page, not just the visible viewport.

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

PNG (lossless), JPEG (smaller files) or PDF (printable A4 document of the page).

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

1–100. Only used for JPEG.

## `selector` (type: `string`):

Optional CSS selector: capture only this element (e.g. `#pricing`, `.hero`). The first match is used.

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

Override the device width. 0 = device default.

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

Override the device height. 0 = device default.

## `deviceScaleFactor` (type: `integer`):

1 = normal, 2 = retina, 3 = high-DPI phones. 0 = device default.

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

Very long pages are cut at this height (CSS pixels) to keep images usable.

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

Emulate prefers-color-scheme: dark.

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

Hide common consent pop-ups (OneTrust, Cookiebot, Quantcast, Didomi, Usercentrics and more).

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

CSS selectors to hide before capturing (chat widgets, popups, ads), e.g. `#intercom-container`.

## `scrollPage` (type: `boolean`):

Scroll through the page before a full-page capture so lazy-loaded images appear.

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

When navigation counts as finished.

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

Optional CSS selector to wait for before capturing (e.g. a chart that renders late).

## `delaySecs` (type: `integer`):

Wait this long after loading, before capturing.

## `timeoutSecs` (type: `integer`):

Give up on a page after this long.

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

Screenshots taken at the same time. More needs more memory (about 500 MB per page).

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://apify.com"
    }
  ],
  "devices": [
    "desktop"
  ],
  "fullPage": true,
  "format": "png",
  "jpegQuality": 80,
  "selector": "",
  "viewportWidth": 0,
  "viewportHeight": 0,
  "deviceScaleFactor": 0,
  "maxHeight": 10000,
  "darkMode": false,
  "hideCookieBanners": true,
  "hideSelectors": [],
  "scrollPage": true,
  "waitUntil": "load",
  "waitForSelector": "",
  "delaySecs": 1,
  "timeoutSecs": 60,
  "maxConcurrency": 3
}
```

# Actor output Schema

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

One row per URL and device with the image link, size and page title.

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

All screenshot files in the key-value store.

# 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": [
        {
            "url": "https://apify.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("reverend_sal/website-screenshot").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": [{ "url": "https://apify.com" }] }

# Run the Actor and wait for it to finish
run = client.actor("reverend_sal/website-screenshot").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": [
    {
      "url": "https://apify.com"
    }
  ]
}' |
apify call reverend_sal/website-screenshot --silent --output-dataset

```

## MCP server setup

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

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/uvlAWjATvt9TPkaeD/builds/Akx548oROtGtmQLnS/openapi.json
