# Website Screenshot: Bulk Full-Page & Mobile (`ctriolab/website-screenshot`) Actor

Capture full-page or viewport screenshots of many URLs as PNG or JPEG, in desktop, laptop, tablet and mobile sizes in one run. Hides cookie banners, captures single elements, and returns a shareable image link per screenshot.

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

## Pricing

from $0.70 / 1,000 screenshots

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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: Bulk Full-Page & Mobile

Capture screenshots of many web pages in one run. Paste a list of URLs, pick one or more device sizes (desktop, laptop, tablet, mobile or your own), and get a PNG or JPEG for every URL and size, with a link you can open or share.

**$1 per 1,000 screenshots plus the run's platform usage, like Apify's own screenshot Actor. Failed URLs are not charged the screenshot fee.**

### What it does

- **Bulk URLs**: hundreds of pages per run, several at a time. Duplicates are removed and `example.org` is read as `https://example.org`.
- **Several device sizes in one run**: Desktop 1920x1080, Laptop 1366x768, Tablet 820x1180 (iPad, 2x), Mobile 390x844 (iPhone, 2x) or a custom width and height. Each URL is captured once per size you pick.
- **Full page or above the fold**: full-page captures scroll first so lazy-loaded images appear.
- **Hide cookie banners**: common consent pop-ups (OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast, CookieYes, Google consent and more) are hidden with CSS. Nothing is clicked or accepted.
- **Hide anything else**: add your own CSS selectors, e.g. `.newsletter-popup`, `#chat-widget`.
- **Capture one element**: e.g. `#pricing`, `main` or `.chart` instead of the whole page.
- **Wait for content**: extra delay, "wait for element", and three load modes (load, DOM ready, network idle) for single-page apps.
- **Dark mode**: asks the page for its dark color scheme.
- **Shareable image links**: every result has a `screenshotUrl` that opens the image directly (turn off "Shareable screenshot links" to keep images private to your account).
- **Clear results**: final URL after redirects, HTTP status, page title, image size in pixels and bytes, and a preview column in the dataset table.

### How to use it

1. Paste your URLs into **URLs**.
2. Pick **Viewports** (desktop is the default), PNG or JPEG, and full page or not.
3. Start the run. Open the **Screenshots** tab of the dataset to see previews, or download the dataset as JSON, CSV or Excel. All image files are also in the run's key-value store.

Example input:

```json
{
  "urls": ["https://apify.com", "https://www.wikipedia.org", "https://news.ycombinator.com"],
  "viewports": ["desktop", "mobile"],
  "fullPage": true,
  "format": "jpeg",
  "jpegQuality": 80,
  "hideCookieBanners": true,
  "hideSelectors": [".newsletter-popup"]
}
```

### Output

One item per screenshot:

```json
{
  "url": "https://www.wikipedia.org/",
  "finalUrl": "https://www.wikipedia.org/",
  "viewport": "mobile",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/0002-wikipedia.org-mobile.jpg",
  "key": "0002-wikipedia.org-mobile.jpg",
  "format": "jpeg",
  "width": 780,
  "height": 3410,
  "fullPage": true,
  "truncated": false,
  "selector": null,
  "statusCode": 200,
  "title": "Wikipedia",
  "sizeBytes": 214733,
  "capturedAt": "2026-10-26T04:30:00.000Z"
}
```

File names start with the URL's position in your list, so they sort in your order: `0001-apify.com-desktop.png`, `0001-apify.com-mobile.png`, `0002-...`.

URLs that fail (domain does not exist, timeout, element not found) are **not** added to the dataset and are **not charged**. They are listed with the error in the key-value store record `SUMMARY`.

### Input

| Field | What it does |
|---|---|
| `urls` | Pages to capture. Required. |
| `viewports` | `desktop`, `laptop`, `tablet`, `mobile`, `custom`. One screenshot per URL per viewport. Default `desktop`. |
| `fullPage` | Whole page (default) or only the visible viewport. |
| `format`, `jpegQuality` | `png` (default) or `jpeg` with quality 1 to 100 (default 80). |
| `hideCookieBanners` | Hide common consent pop-ups with CSS. Default on. |
| `hideSelectors` | Extra CSS selectors to hide. |
| `selector` | Capture only the first element matching this CSS selector. |
| `waitForSelector` | Wait until this element is visible. |
| `delayMs` | Extra wait after load. Default 1000 ms. |
| `waitUntil` | `load` (default), `domcontentloaded`, `networkidle`. |
| `scrollToBottom` | Scroll first to load lazy images (full page only). Default on. |
| `darkMode` | Prefer the dark color scheme. |
| `customWidth`, `customHeight` | Size of the `custom` viewport. |
| `maxFullPageHeight` | Long pages are cut here (default 10,000 px, `truncated: true`). 2x viewports are cut at 8,000 px of page height. |
| `pageTimeoutSecs` | Time limit for loading one page (default 60 s). A page that does not load in time is retried once. |
| `maxConcurrency` | Pages at a time (default 4, also limited by memory: 2 per 2 GB). |
| `publicLinks` | Make `screenshotUrl` open without an API token (default on). |

### Pricing

- **$1.00 per 1,000 screenshots** (one dataset item = one screenshot), with discounts on paid Apify plans (Bronze 10%, Silver 20%, Gold and higher 30%). Failed URLs are not charged.
- **Plus the run's platform usage** (compute), billed by Apify like for its own screenshot Actor. In our tests at the default 4 GB, a typical home page cost about $0.0005 to $0.001 of usage, and very long pages (10,000 px Wikipedia articles) about $0.002. Use JPEG, a smaller `maxFullPageHeight` or `fullPage: false` to make it cheaper.
- You can set a maximum cost per run in the run options; the Actor stops cleanly when it is reached.

### Use with the API, Make, Zapier or AI agents

Call it like any Apify Actor, for example `run-sync-get-dataset-items` with the input above, and read `screenshotUrl` from each item. Schedule it in Apify to keep daily visual snapshots of competitor pages, landing pages or your own site after each deploy. It also works through the Apify MCP server, so an AI agent can take screenshots on request.

### Limits

- Pages behind a login and pages protected by a CAPTCHA or bot wall are **not supported**. The Actor does not log in, solve CAPTCHAs or use proxies to get around blocks.
- Cookie banners are hidden, not accepted, so content that only loads after consent is not shown.
- Very long pages are cut at `maxFullPageHeight`.
- The default memory is 4 GB (one full CPU). In our tests it was cheaper per screenshot than 2 GB because pages render faster. Very heavy pages (huge articles with many images or maps) can take a minute each.
- Video, canvas animations and some web fonts may look different from your own browser.

### Feedback

Found a page that does not render well? Open an issue on the Actor's Issues tab with the URL and your input, and we will look at it.

# Actor input Schema

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

Web pages to capture, one per line. "example.com" is read as https://example.com. Duplicates are removed. Pages behind a login and pages that show a CAPTCHA are not supported.

## `viewports` (type: `array`):

Each URL is captured once per viewport (one result each). Desktop 1920x1080, Laptop 1366x768, Tablet 820x1180 (iPad, 2x), Mobile 390x844 (iPhone, 2x), Custom uses the width and height below.

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

Capture the whole scrollable page. Off = only the visible viewport (above the fold).

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

PNG is lossless. JPEG files are much smaller for long pages.

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

1 to 100. Used only for JPEG.

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

Hides common consent pop-ups (OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast, CookieYes, Google consent and more) with CSS. Nothing is clicked or accepted.

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

Extra elements to hide before the capture, e.g. ".newsletter-popup", "#chat-widget", "header.sticky".

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

Optional. Captures just the first element that matches, e.g. "main", "#pricing" or ".chart". If it is missing, that URL fails and is not charged.

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

Optional. Waits until this element is visible before the capture, e.g. a chart or a product grid.

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

Waits this long after the page has loaded, so animations and late content can settle.

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

load: all resources loaded (default). domcontentloaded: HTML parsed (fastest). networkidle: no network requests for 0.5 s (slowest, best for single-page apps).

## `scrollToBottom` (type: `boolean`):

For full-page captures, scrolls down step by step first so lazy-loaded images appear.

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

Asks the page for its dark color scheme (prefers-color-scheme: dark).

## `customWidth` (type: `integer`):

Used with the Custom viewport.

## `customHeight` (type: `integer`):

Used with the Custom viewport.

## `maxFullPageHeight` (type: `integer`):

Very long pages are cut at this height (the result has truncated = true). Mobile and tablet images are 2x, so they are cut at 8,000 px of page height.

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

Maximum time for one page. A page that times out is retried once, then reported as failed (not charged).

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

Upper limit. The Actor also limits it by memory (about 700 MB per page).

## `publicLinks` (type: `boolean`):

Makes this run's key-value store readable by anyone who has its link, so screenshotUrl opens without an API token. Turn off to keep the images private to your account.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://www.wikipedia.org",
    "https://news.ycombinator.com"
  ],
  "viewports": [
    "desktop"
  ],
  "fullPage": true,
  "format": "png",
  "jpegQuality": 80,
  "hideCookieBanners": true,
  "hideSelectors": [],
  "delayMs": 1000,
  "waitUntil": "load",
  "scrollToBottom": true,
  "darkMode": false,
  "customWidth": 1440,
  "customHeight": 900,
  "maxFullPageHeight": 10000,
  "pageTimeoutSecs": 60,
  "maxConcurrency": 4,
  "publicLinks": true
}
```

# Actor output Schema

## `results` (type: `string`):

One item per screenshot: URL, viewport, image link, size, HTTP status and page title.

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

All screenshot files in the run's key-value store.

## `summary` (type: `string`):

Counts and the list of URLs that failed (failed URLs are not charged).

# 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://www.wikipedia.org",
        "https://news.ycombinator.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ctriolab/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": [
        "https://apify.com",
        "https://www.wikipedia.org",
        "https://news.ycombinator.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ctriolab/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": [
    "https://apify.com",
    "https://www.wikipedia.org",
    "https://news.ycombinator.com"
  ]
}' |
apify call ctriolab/website-screenshot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ctriolab/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/4kpD3X4JgXGDZhW3T/builds/jMZ5xIkAm4qPgpz4q/openapi.json
