# Website Screenshot & PDF Capture (`kiwitools/website-screenshot-pdf`) Actor

Capture bulk website screenshots (PNG, JPEG, WebP) or PDFs. Full page, single element, mobile and tablet presets, dark mode, cookie-banner hiding and lazy-image scrolling. Pay only for successful captures.

- **URL**: https://apify.com/kiwitools/website-screenshot-pdf.md
- **Developed by:** [Kiwi Tools](https://apify.com/kiwitools) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 screenshot captureds

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 & PDF Capture

Turn a list of web pages into **PNG, JPEG or WebP screenshots or PDF files** in one run. Paste up to 1,000 URLs, pick a device and a format, and download one file per page. Full-page captures, single-element captures, mobile and tablet views, dark mode, and automatic hiding of cookie pop-ups are built in.

**You pay only for files that were actually produced.** Failed, blocked or skipped URLs are free.

### What you can use it for

- Visual archives and "before/after" records of your own sites or landing pages
- Thumbnails and previews for directories, link lists and dashboards
- Checking how pages look on phones, tablets and desktops
- Turning articles, docs or reports into PDFs
- Feeding page images to AI or vision workflows (works well as a tool for AI agents)

### Features

- **Bulk:** up to 1,000 URLs per run, captured in parallel
- **Formats:** PNG (lossless), JPEG and WebP (adjustable quality), or PDF (A4, Letter, Legal, A3, Tabloid; portrait or landscape)
- **Devices:** Desktop 1920×1080, Laptop 1366×768, Tablet 768×1024 @2x, iPhone 390×844 @3x, Android 412×915 @2.625x, or any custom width × height × pixel ratio
- **Full page** or **just one element** (CSS selector), e.g. a chart, a pricing table or a product card
- **Lazy images:** optional scroll-through before capturing so lazy-loaded images appear
- **Cookie pop-ups hidden** with a built-in ruleset for common consent tools (the overlay is hidden; no consent choice is made)
- **Hide anything else** (chat bubbles, promo bars) with your own CSS selectors
- **Dark mode** for sites that support it
- **Wait options:** `load`, `domcontentloaded` or `networkidle`, plus an extra delay
- **Safe by design:** only public `http(s)` addresses are captured; local, private-network and `file:` addresses are refused, including after redirects
- Optional **ZIP** with all files, and an optional proxy

### How to use it

1. Paste your URLs into **URLs** (one per line).
2. Choose an **Output format** and a **Device**.
3. Optionally switch on **Full page**, set an **Element selector**, or tweak the loading options.
4. Click **Start**. Each file appears in the run's key-value store, and the dataset lists every URL with a direct link to its file.

#### Example: full-page mobile screenshots

```json
{
  "urls": ["https://example.com", "https://example.org/pricing"],
  "format": "jpeg",
  "quality": 80,
  "device": "iphone",
  "fullPage": true,
  "scrollToBottom": true
}
```

#### Example: capture one element

```json
{
  "urls": ["https://example.com/dashboard"],
  "format": "png",
  "selector": "#sales-chart",
  "device": "laptop"
}
```

#### Example: save pages as PDFs

```json
{
  "urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"],
  "format": "pdf",
  "pdfFormat": "A4",
  "pdfPrintBackground": true
}
```

#### Example: dark mode WebP at a custom size

```json
{
  "urls": ["https://example.com"],
  "format": "webp",
  "device": "custom",
  "width": 1440,
  "height": 900,
  "deviceScaleFactor": 2,
  "darkMode": true,
  "hideSelectors": [".chat-widget", "#promo-bar"]
}
```

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array of strings | (required) | Public URLs to capture, max 1,000. `https://` is added if missing. |
| `format` | `png` · `jpeg` · `webp` · `pdf` | `png` | Output format. |
| `quality` | integer 1–100 | `85` | JPEG/WebP quality. |
| `device` | `desktop` · `laptop` · `tablet` · `iphone` · `android` · `custom` | `desktop` | Viewport preset. |
| `width`, `height` | integer | `1280`, `800` | Custom viewport in CSS px (only with `custom`). |
| `deviceScaleFactor` | number 0.5–3 | `1` | Custom pixel ratio (only with `custom`). |
| `fullPage` | boolean | `false` | Capture the whole page height (capped, see Limits). |
| `selector` | string | – | Capture only the first element matching this CSS selector (images only). |
| `waitUntil` | `load` · `domcontentloaded` · `networkidle` | `load` | When the page counts as loaded. |
| `delayMs` | integer 0–30000 | `0` | Extra wait before capturing. |
| `scrollToBottom` | boolean | `false` | Scroll through the page first to trigger lazy images. |
| `hideCookieBanners` | boolean | `true` | Hide common cookie/consent pop-ups. |
| `hideSelectors` | array of strings | – | Extra CSS selectors to hide. |
| `darkMode` | boolean | `false` | Request the site's dark theme (`prefers-color-scheme: dark`). |
| `pdfFormat` | `A4` · `Letter` · `Legal` · `A3` · `Tabloid` | `A4` | PDF paper size. |
| `pdfLandscape` | boolean | `false` | Landscape PDF. |
| `pdfPrintBackground` | boolean | `true` | Include background colours and images in PDFs. |
| `pdfUseScreenMedia` | boolean | `false` | Use on-screen styles instead of the print stylesheet. |
| `timeoutSecs` | integer 5–180 | `60` | Time limit per URL. |
| `maxConcurrency` | integer 1–10 | `3` | Pages captured in parallel (about 1 GB memory per 2 pages). |
| `skipHttpErrors` | boolean | `true` | Don't capture or charge for pages answering HTTP 4xx/5xx. |
| `zipOutput` | boolean | `false` | Also store `ALL_CAPTURES.zip` with every file (free). |
| `proxy` | proxy settings | off | Optional proxy. Proxy traffic is billed by the platform separately. |

### Output

**Files** go to the run's default key-value store, named like `shot-0001-example-com.png` or `pdf-0002-example-org-pricing.pdf`.

**Dataset:** one row per URL (`index` is the position in your input list):

```json
{
  "index": 0,
  "url": "https://example.com",
  "finalUrl": "https://example.com/",
  "httpStatus": 200,
  "title": "Example Domain",
  "fileKey": "shot-0001-example-com.png",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/shot-0001-example-com.png",
  "format": "png",
  "width": 1920,
  "height": 1080,
  "pages": null,
  "bytes": 80185,
  "truncated": false,
  "captureMethod": "viewport",
  "capturedAt": "2026-10-18T09:51:45.619Z",
  "charged": true,
  "blockedRequests": 0,
  "durationMs": 1240,
  "ok": true,
  "error": null
}
```

Failed rows have `"ok": false`, `"charged": false` and a plain-English `error` such as `Timed out after 60 s`, `Page returned HTTP 404` or `Rejected URL: Address 10.0.0.1 is not a public IP`.

### Pricing

This Actor uses **pay-per-event** pricing. You are charged only after a file has been saved:

| Event | Price | When it is charged |
|---|---|---|
| `screenshot` | **US$0.0025** per image (US$2.50 per 1,000) | A PNG, JPEG or WebP file was saved |
| `pdf` | **US$0.003** per PDF (US$3.00 per 1,000) | A PDF file was saved |
| Actor start | small platform start fee | Once per run |

- **Not charged:** URLs that fail, time out, are refused by the safety check, return an HTTP error (with `skipHttpErrors` on), or are skipped.
- **Spending limit:** set **Maximum cost per run** in the run options. The Actor checks it before every capture and stops cleanly when the limit is reached; the remaining URLs are listed as skipped (not charged).
- The price already includes the compute the capture needs. Optional proxy traffic is billed separately by the platform.
- You can try it on the Apify Free plan; charges are taken from your plan's monthly usage credit.

### Limits and good to know

- **Page height** is capped at 20,000 CSS px (and 32,000 device pixels; 16,383 px for WebP, a format limit). Taller pages are cut at the cap and marked `"truncated": true`.
- On a few very heavy pages the browser's one-shot full-page capture stalls; the Actor then reloads the page and stitches viewport tiles together (`"captureMethod": "stitched"`). Fixed headers appear once at the top.
- **No logins:** the Actor does not accept cookies, sessions or passwords, so pages behind a login can't be captured.
- Cookie pop-ups are **hidden, not answered**. Some sites redirect to a separate consent page instead of showing a pop-up; that page is what gets captured.
- Some sites block automated browsers or show a CAPTCHA. The Actor does not try to bypass such protections.
- PDFs use the browser's print engine, so sites with a print stylesheet look different from the screen; switch on `pdfUseScreenMedia` to use screen styles.

### Responsible use

Only submit URLs you have the right to capture, and respect the terms of the websites you capture and any copyright in their content. This Actor visits each URL once, like a normal browser; it does not crawl links. It is an independent tool and is not affiliated with or endorsed by any website it captures.

### FAQ

**Can I call it from my own code or an AI agent?**
Yes. Like any Actor it can be started through the Apify API, the API clients, integrations, or the Apify MCP server, and its input schema describes every option.

**Why did a URL fail with "Rejected URL"?**
The safety check only allows public internet addresses. `localhost`, private networks (10.x, 192.168.x, etc.), cloud metadata addresses and `file:` links are always refused.

**Are the files public?**
Files live in your run's key-value store and follow your storage settings and retention, like any other run output.

# Actor input Schema

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

Public http(s) URLs to capture, one per line (max 1,000 per run). Only submit pages you have the right to capture. Local, private-network and file: addresses are rejected.

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

png, jpeg and webp are images (event `screenshot`). pdf prints the page to a PDF document (event `pdf`).

## `quality` (type: `integer`):

Compression quality from 1 to 100. Ignored for PNG and PDF.

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

Viewport preset. Choose 'custom' to set your own width, height and pixel ratio below.

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

Viewport width in CSS pixels. Used only when Device is 'custom'.

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

Viewport height in CSS pixels. Used only when Device is 'custom'.

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

Device pixel ratio from 0.5 to 3, for example 1, 1.5 or 2. Used only when Device is 'custom'.

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

Capture the whole scrollable page instead of just the viewport. Height is capped at 20,000 CSS px (lower for WebP, which has a 16,383 px format limit).

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

Optional CSS selector. If set, only the first matching element is captured (images only). The capture fails if no element matches.

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

When the page counts as loaded. 'networkidle' waits for network quiet (slower, best for heavy pages).

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

Wait this long after loading, before capturing. Useful for animations.

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

Scroll through the page before capturing so lazy-loaded images appear.

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

Hide common cookie and consent pop-ups with a built-in CSS/JS ruleset. It hides the overlay; it does not accept or reject consent.

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

CSS selectors to hide before capturing, for example chat widgets or ads.

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

Ask the page for its dark theme (prefers-color-scheme: dark). Only works on sites that support it.

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

Paper size for PDF output.

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

Landscape orientation for PDF output.

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

Include background colours and images in PDF output.

## `pdfUseScreenMedia` (type: `boolean`):

Render the PDF with the on-screen styles instead of the site's print stylesheet.

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

Maximum time for loading and capturing one URL.

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

How many pages to capture in parallel. Higher needs more memory (about 1 GB per 2 pages).

## `skipHttpErrors` (type: `boolean`):

Don't capture (or charge for) pages that answer with HTTP 4xx/5xx, such as 404 Not Found. Turn off if you want proof of an error page.

## `zipOutput` (type: `boolean`):

Store an extra record ALL_CAPTURES.zip in the key-value store. Free of charge.

## `proxy` (type: `object`):

Optional. Route traffic through Apify Proxy (proxy usage is billed by Apify to your account separately). Off by default.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "https://en.wikipedia.org/wiki/Screenshot"
  ],
  "format": "png",
  "quality": 85,
  "device": "desktop",
  "width": 1280,
  "height": 800,
  "deviceScaleFactor": 1,
  "fullPage": false,
  "waitUntil": "load",
  "delayMs": 0,
  "scrollToBottom": false,
  "hideCookieBanners": true,
  "darkMode": false,
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfPrintBackground": true,
  "pdfUseScreenMedia": false,
  "timeoutSecs": 60,
  "maxConcurrency": 3,
  "skipHttpErrors": true,
  "zipOutput": false,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per URL with ok/error status, final URL, HTTP status, title, file key, signed fileUrl, dimensions or page count, size and whether it was charged.

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

PNG, JPEG and WebP captures in the default key-value store (keys starting with shot-).

## `pdfs` (type: `string`):

PDF captures in the default key-value store (keys starting with pdf-).

## `zip` (type: `string`):

ALL_CAPTURES.zip with every capture in the run. Only present when zipOutput is enabled.

# 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://example.com",
        "https://en.wikipedia.org/wiki/Screenshot"
    ],
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kiwitools/website-screenshot-pdf").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://example.com",
        "https://en.wikipedia.org/wiki/Screenshot",
    ],
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("kiwitools/website-screenshot-pdf").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://example.com",
    "https://en.wikipedia.org/wiki/Screenshot"
  ],
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call kiwitools/website-screenshot-pdf --silent --output-dataset

```

## MCP server setup

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

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/sohM7jeKWMuSLEFbV/builds/DYqI7vumGp4rmufJc/openapi.json
