# Website Screenshot (`jcwilk/website-screenshot`) Actor

Website screenshot API: bulk full-page or viewport screenshots of any URL as PNG/JPEG, or URL to PDF. Desktop, tablet, mobile; cookie banners removed, lazy images loaded. $2.50 per 1,000 screenshots; failed pages are free.

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

## Pricing

from $6.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: Bulk Full-Page Screenshots, Mobile, URL to PDF

**URL in, screenshot out.** Capture full-page or viewport screenshots of any list of URLs as PNG/JPEG, or convert URL to PDF, with cookie banners removed. **$6 per 1,000 screenshots**; failed pages are free.

Take **screenshots of any website** in bulk. Paste a list of URLs and get back **full-page or viewport screenshots** as PNG or JPEG, or a **URL-to-PDF** render, on **desktop, laptop, tablet, iPhone, or Android** viewports. Each result has a **direct image URL** you can download, embed, or pass to an AI agent.

It is built for clean captures. **Cookie banners and consent popups are dismissed automatically** (OneTrust, Cookiebot, Didomi, Usercentrics, Sourcepoint, Quantcast, TrustArc and generic ones). **Lazy-loaded images are scrolled into view** so the full page renders. **You pay only for successful screenshots**: failed pages, DNS errors and HTTP 4xx/5xx pages are free.

### What it does

- **Full-page screenshots** of the whole scrollable page, or the visible viewport only.
- **Device presets**: desktop 1920×1080, laptop 1366×768, tablet (iPad, retina), mobile (iPhone 390×844 @3x), Android (Pixel), or any custom width, height and pixel density.
- **PNG, JPEG, or PDF**: get several formats in one run, e.g. a PNG plus a PDF of the same page.
- **Cookie banner and popup removal**: clicks "Accept" in 15+ languages, hides known consent platforms, and unlocks pages that a consent wall stops from scrolling.
- **Lazy-load handling**: scrolls the page before capturing so images and sections below the fold load.
- **Element screenshots**: capture a single CSS selector, e.g. a chart, pricing table, or tweet.
- **Hide elements**: remove chat widgets, ads, or sticky headers with CSS selectors.
- **Wait controls**: `waitUntil` (load / networkidle), `waitForSelector`, and an extra delay for animations.
- **Dark mode** (`prefers-color-scheme: dark`) and a custom user agent.
- **Bulk and fast**: one shared headless Chromium captures several pages in parallel. Typical pages take 2–10 s.
- **Robust**: a timeout still captures whatever had loaded (with a warning), one bad URL never fails the run, and each error is readable ("Domain does not resolve", "Page returned HTTP 404"…).

### Use cases

- **SEO and agency reporting**: screenshot client homepages and landing pages every week.
- **Visual QA and regression checks** on desktop and mobile.
- **Archiving and compliance**: keep dated PDF or PNG captures of web pages, ads, and terms pages.
- **Competitor monitoring**: track pricing pages and homepages over time (pair it with an Apify schedule).
- **AI agents and LLM vision**: give a model "what this page looks like" via the image URL.
- **Link previews and thumbnails** for directories and dashboards.

### Input

| Field | Default | Description |
|---|---|---|
| `urls` | – | List of URLs: `[{ "url": "https://…" }]` |
| `device` | `desktop` | `desktop`, `laptop`, `tablet`, `mobile`, `android`, `custom` |
| `fullPage` | `true` | Whole page vs. visible viewport |
| `formats` | `["png"]` | Any of `png`, `jpeg`, `pdf` |
| `dismissCookieBanners` | `true` | Accept or hide cookie/consent popups |
| `scrollToBottom` | `true` | Scroll to trigger lazy-loaded content |
| `delayMs` | `1000` | Extra wait after load |
| `waitUntil` | `load` | `load`, `domcontentloaded`, `networkidle` |
| `waitForSelector` | – | Wait for a CSS selector before capturing |
| `selector` | – | Capture only this element |
| `hideSelectors` | – | CSS selectors to hide |
| `width` / `height` / `deviceScaleFactor` | preset | Custom viewport |
| `blockTrackers` | `true` | Block analytics, ads, chat widgets and video (faster, cleaner) |
| `darkMode` | `false` | Emulate dark color scheme |
| `jpegQuality` | `80` | JPEG quality 1–100 |
| `pdfFormat` | `A4` | `A4`, `Letter`, `Legal`, `A3`, `Tabloid` |
| `maxFullPageHeight` | `16384` | Very long pages are cut at this height (px) |
| `timeoutSecs` | `45` | Navigation timeout per page |
| `maxRetries` | `1` | Retries after a browser error |
| `maxConcurrency` | `4` | Pages in parallel (auto-capped at 1 per GB of memory) |
| `includeHtml` | `false` | Also return the rendered HTML |

#### Example input

```json
{
  "urls": [{ "url": "https://apify.com" }, { "url": "https://www.bbc.co.uk" }],
  "device": "mobile",
  "fullPage": true,
  "formats": ["png", "pdf"]
}
```

### Output

Images and PDFs are stored in the run's key-value store. The dataset has one item per URL:

```json
{
  "url": "https://apify.com",
  "status": "success",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/apify.com-mobile-1a2b3c4d5e.png",
  "pdfUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/apify.com-mobile-1a2b3c4d5e.pdf",
  "files": [{ "format": "png", "key": "apify.com-mobile-1a2b3c4d5e.png", "url": "…", "bytes": 912345 }],
  "title": "Apify: Full-stack web scraping and data extraction platform",
  "finalUrl": "https://apify.com/",
  "httpStatus": 200,
  "device": "mobile",
  "viewport": "390x844",
  "fullPage": true,
  "pageWidth": 390,
  "pageHeight": 12771,
  "warnings": [],
  "elapsedSec": 7.4,
  "error": null
}
```

`status` is `success`, `httpError` (the page answered 4xx/5xx; it is captured but not charged) or `error` (nothing captured, with a readable `error`).

### Use it from code or an AI agent

```bash
curl -X POST "https://api.apify.com/v2/acts/jcwilk~website-screenshot/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"urls":[{"url":"https://example.com"}],"device":"desktop","formats":["png"]}'
```

The response has the `screenshotUrl` for each page. The Actor also works as a tool through the Apify MCP server: the input is a flat JSON object with a URL list, so it is easy for LLM agents to call.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Actor start (charged by Apify, per GB of run memory) | $0.003 per GB |
| Screenshot (PNG or JPEG) | **$0.006** ($6 per 1,000) |
| Page saved as PDF | $0.015 |

Example: 1,000 full-page screenshots in one 4 GB run cost about $6.012. Failed pages, unreachable domains and HTTP error pages are free. Platform compute and browser time are included.

### FAQ

**Is this a ScreenshotOne / Urlbox / screenshotapi.net alternative?** For bulk, pay-as-you-go captures without a monthly plan, yes: you call it through the Apify API, MCP server, Make, Zapier or n8n and pay per successful screenshot.

**Does it remove cookie banners?** Yes, by default. It clicks the "Accept" button when it can find one (English, German, French, Spanish, Italian, Portuguese, Dutch, Nordic, Polish, Czech, Turkish, Japanese…), hides the major consent platforms, and re-enables scrolling. Turn it off with `dismissCookieBanners: false` if you need the banner in the image.

**Why is my screenshot cut off at the bottom?** Pages taller than `maxFullPageHeight` (16,384 px by default) are cut to keep files usable. You can raise it up to 32,000 px.

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

**Does it work on sites with bot protection?** Most public sites work without a proxy. Sites behind aggressive anti-bot walls (e.g. some Cloudflare challenges) may show the challenge page instead.

**How long are image URLs valid?** They live in your run's key-value store, so they follow your Apify data retention (unnamed storages are kept for 7 days on the free plan). Download them, or copy them to a named store, to keep them longer.

**Can I schedule it?** Yes. Use Apify Schedules to capture the same URLs daily or weekly for monitoring and archiving.

### Limitations

- Public pages only, with no login or cookies input.
- Videos, canvas animations, and some WebGL content may look different from a real browser.
- A few consent walls that need a choice before any content loads (rare) may still appear.
- Maximum height of 32,000 px per full-page image. PDF output uses print layout, with background graphics on.

### Run locally (no Apify account needed)

```bash
pip install -r requirements-dev.txt && playwright install chromium
python -m pytest -q             # unit + end-to-end tests against a local test site
python scripts/smoke.py mobile  # real-site smoke test, images in smoke-results/
```

# Actor input Schema

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

Web pages to capture (full http:// or https:// URLs).

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

Viewport preset. desktop 1920×1080, laptop 1366×768, tablet (iPad 820×1180 @2x), mobile (iPhone 390×844 @3x), android (Pixel 412×915), or custom (set width/height).

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

Capture the whole scrollable page. Off = only the visible viewport.

## `formats` (type: `array`):

One or more of png, jpeg, pdf. Each format is a separate charged event.

## `dismissCookieBanners` (type: `boolean`):

Auto-accept or hide cookie/consent banners (OneTrust, Cookiebot, Didomi, Usercentrics, Quantcast and generic ones).

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

Scroll through the page before capturing so lazy-loaded images and sections render.

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

Wait this long after the page loads, e.g. for animations.

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

Navigation event to wait for. networkidle is slower but safer for single-page apps.

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

Optional CSS selector that must appear before capturing.

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

Optional CSS selector: capture only this element instead of the page.

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

CSS selectors to hide before capturing (chat widgets, ads, sticky headers).

## `width` (type: `integer`):

Viewport width; used with device=custom or to override a preset.

## `height` (type: `integer`):

Viewport height; used with device=custom or to override a preset.

## `deviceScaleFactor` (type: `number`):

Pixel density (1 = normal, 2 = retina). Defaults to the preset value.

## `blockTrackers` (type: `boolean`):

Block analytics, ad and chat-widget scripts and video downloads. Faster and cleaner screenshots.

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

Emulate prefers-color-scheme: dark.

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

1–100, JPEG only.

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

Paper size for PDF output.

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

Very long pages are cut at this height.

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

Navigation timeout per page. On timeout, whatever has loaded is captured.

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

Retries per page after a browser error.

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

Max pages in parallel. Automatically capped at 1 page per GB of run memory.

## `userAgent` (type: `string`):

Optional custom User-Agent string.

## `includeHtml` (type: `boolean`):

Also return the rendered HTML in the dataset item.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://apify.com"
    },
    {
      "url": "https://www.wikipedia.org"
    }
  ],
  "device": "desktop",
  "fullPage": true,
  "formats": [
    "png"
  ],
  "dismissCookieBanners": true,
  "scrollToBottom": true,
  "delayMs": 1000,
  "waitUntil": "load",
  "blockTrackers": true,
  "darkMode": false,
  "jpegQuality": 80,
  "pdfFormat": "A4",
  "maxFullPageHeight": 16384,
  "timeoutSecs": 45,
  "maxRetries": 1,
  "maxConcurrency": 4,
  "includeHtml": false
}
```

# Actor output Schema

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

One row per URL with links to the stored image/PDF.

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

PNG, JPEG and PDF files in the default 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"
        },
        {
            "url": "https://www.wikipedia.org"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jcwilk/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" },
        { "url": "https://www.wikipedia.org" },
    ] }

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

```

## MCP server setup

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