# Website Screenshot & PDF Generator (`webthreenity/website-screenshot-pdf`) Actor

Screenshot any website or HTML as PNG, JPEG, WebP or PDF. Full page with lazy-loaded images, 13 devices in one run, cookie banners refused and hidden, ads blocked, real PDF layout options. Batch, sitemap or a live HTTP API.

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

## Pricing

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

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 Generator

Capture any web page, or your own HTML, as a **PNG, JPEG, WebP or PDF**. Full page with lazy-loaded images, desktop and phone sizes in the same run, cookie banners refused and hidden, ads blocked. Run it as a batch, point it at a sitemap, or call it as a live HTTP API that returns the file in one request.

- **Clean pages.** Refuses cookie pop-ups by clicking "Reject" or "Necessary only" on 300+ consent tools, then hides whatever is left. Blocks ads and trackers with EasyList, EasyPrivacy and uBlock Origin filters.
- **Whole pages.** Lazy images are loaded before the capture, so long pages don't come out with grey boxes. Pages up to 60,000 px tall.
- **13 devices in one run.** Desktop, laptop, MacBook Retina, 2K, iPad, iPhone 16, iPhone 16 Pro Max, iPhone SE, Pixel 9, Galaxy S24, a 1200×630 social card, a square, or your own size. Every URL is captured on every device you pick.
- **Real PDFs.** A4, Letter, Legal, A3, A5 or Tabloid. Portrait or landscape, margins, page numbers, print or screen look, or the whole site as **one long page with no page breaks**.
- **Your own HTML.** Turn an invoice, a report or a social card template into a PDF or an image.
- **Built for code and AI agents.** A single-URL run returns the file itself, Standby mode answers an HTTP request with the file, and you can put the file in the dataset as base64.
- **You don't pay for failures or bot walls.** Failed pages are free, and so are pages a site answers with an error such as `403 Access Denied`.

### What you can do with it

- Archive client websites or competitors every week (schedule the Actor).
- Make responsive previews: one URL, desktop + tablet + phone, in one run.
- Generate PDFs of reports, documentation or invoices from HTML.
- Add website thumbnails to a directory, a CRM or a portfolio.
- Capture an entire site from its sitemap for an audit or a redesign brief.
- Give an AI agent eyes: it gets a clean image of the page, without the cookie wall.

### Quick start

1. Paste one or more URLs.
2. Pick the devices and the format.
3. Click **Start**. Files appear in the **Output** tab with a preview, and in the run's key-value store.

That's all most people need. Every other option has a sensible default.

### Input examples

**One page, full length, PNG**

```json
{ "urls": ["https://apify.com"] }
```

**Responsive set: desktop, iPad and iPhone**

```json
{
    "urls": ["https://stripe.com", "https://www.lemonde.fr"],
    "devices": ["desktop", "ipad", "iphone"],
    "format": "webp"
}
```

**PDF with page numbers**

```json
{
    "urls": ["https://en.wikipedia.org/wiki/Morocco"],
    "format": "pdf",
    "pdfFormat": "A4",
    "pdfMargin": "15mm",
    "pdfPageNumbers": true
}
```

**The whole website as one long PDF page**

```json
{ "urls": ["https://apify.com"], "format": "pdf", "pdfSinglePage": true }
```

**An invoice from your own HTML**

```json
{
    "html": "<html><body><h1>Invoice INV-0042</h1><p>Total: 1,250.00 EUR</p></body></html>",
    "format": "pdf",
    "pdfFormat": "A4"
}
```

**Every page of a sitemap, as phone screenshots**

```json
{ "sitemapUrl": "https://example.com/sitemap.xml", "maxSitemapPages": 200, "devices": ["iphone"] }
```

**One element only**

```json
{ "urls": ["https://en.wikipedia.org/wiki/Morocco"], "selector": ".infobox" }
```

**A page behind a login (session cookie)**

```json
{
    "urls": ["https://app.example.com/dashboard"],
    "cookies": [{ "name": "session", "value": "YOUR_SESSION", "domain": ".example.com" }]
}
```

### Output

Each file goes to the run's key-value store, and one row per file goes to the dataset:

```json
{
    "url": "https://www.spiegel.de/",
    "finalUrl": "https://www.spiegel.de/",
    "device": "iphone",
    "format": "png",
    "fileUrl": "https://api.apify.com/v2/key-value-stores/.../records/spiegel-de-iphone-3e331591.png",
    "width": 1179,
    "height": 24000,
    "sizeKb": 9861.3,
    "statusCode": 200,
    "title": "DER SPIEGEL | Online-Nachrichten",
    "cookieBanner": "refused (sourcepoint-top)",
    "truncated": true,
    "loadTimeMs": 8120,
    "capturedAt": "2026-10-20T18:42:11.204Z"
}
```

- `cookieBanner` says what happened: `refused (consent tool)`, `accepted (…)`, `2 elements hidden` or `none found`.
- `truncated` is `true` when the page was taller than **Max page height**.
- A page that fails gets a row with an `error` instead, and is **not charged**. The one exception is residential proxy traffic, which costs us the same whether the page works or not (see Pricing).
- With residential proxies, each row also has `residentialTrafficMb`: the traffic charged for that page.
- When you capture a single file, the run's `OUTPUT` record is the file itself. With several files, `OUTPUT` is a short summary with every file URL.

### Use it from your code

**One call, get the image back** (the run's `OUTPUT` is the file):

```bash
curl -X POST "https://api.apify.com/v2/acts/webthreenity~website-screenshot-pdf/run-sync?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "device": "iphone"}' \
  -o screenshot.png
```

**JavaScript**

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('webthreenity/website-screenshot-pdf').call({
    urls: ['https://example.com'],
    devices: ['desktop', 'iphone'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => i.fileUrl));
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("webthreenity/website-screenshot-pdf").call(run_input={"urls": ["https://example.com"], "format": "pdf"})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["fileUrl"])
```

### Live HTTP API (Standby)

Standby keeps a browser warm and answers each request with the file. Every input field works as a query parameter or as a JSON body field. Copy your Standby URL from the **Standby** tab.

```bash
## PNG of a phone screen
curl "https://webthreenity--website-screenshot-pdf.apify.actor/screenshot?url=https://example.com&device=iphone&fullPage=false&token=YOUR_TOKEN" -o phone.png

## PDF
curl "https://webthreenity--website-screenshot-pdf.apify.actor/pdf?url=https://example.com&pdfPageNumbers=true&token=YOUR_TOKEN" -o page.pdf

## Metadata and a stored file URL instead of the bytes
curl "https://webthreenity--website-screenshot-pdf.apify.actor/screenshot?url=https://example.com&response=json&token=YOUR_TOKEN"
```

Responses carry `X-Width`, `X-Height`, `X-Final-Url`, `X-Status-Code`, `X-Cookie-Banner` and `X-Charged` headers. Errors come back as JSON: `400` for invalid input, `422` when the page can't be captured as asked (private address, unknown domain, missing element), `502` when the site failed. None of them are charged. One URL and one device per request; use a normal run for batches.

### How cookie banners are handled

| Setting | What happens |
|---|---|
| **Refuse, then hide leftovers** (default) | Clicks "Reject all" or "Necessary only" using DuckDuckGo's open-source rules for 300+ consent tools (OneTrust, Didomi, Sourcepoint, Cookiebot, Usercentrics, Quantcast, TrustArc, Axeptio, tarteaucitron and many more). Anything still on screen is hidden with the EasyList Cookie and uBlock Origin cookie lists, and the page is unlocked if the banner had frozen scrolling. |
| **Accept all** | Clicks "Accept all". Use it when a site only shows its content after consent. |
| **Hide only** | Never clicks. Banners are hidden and their scripts blocked. |
| **Keep banners** | The page exactly as a first-time visitor sees it. |

Refusing is the default because it is what a privacy-minded visitor does, and refused pages usually load fewer trackers and ads.

### Pricing

Pay per event: you pay for each file delivered. Memory and run time are on us.

| Event | Price |
|---|---|
| Screenshot (PNG, JPEG or WebP) | $6.00 per 1,000 |
| PDF | $8.00 per 1,000 |
| Residential proxy traffic (only if you pick residential proxies) | $0.012 per MB |

Example: 500 pages on desktop and iPhone = 1,000 screenshots = **$6.00**.

**Not charged:**

- Pages that fail (timeout, unknown domain, missing element).
- Pages the site answers with an error: a `403` bot wall, a `404`, a `500`. You still get the file, and its row says `"charged": false`.
- Datacenter proxies.

**Residential proxies** are billed by the traffic they carry, because that is how they cost us: an ad-blocked page uses about 7 MB on average, so about $0.08. Ads and trackers are blocked before they reach the proxy. Each row shows its `residentialTrafficMb`. This traffic is charged even when the page then fails.

**Standby (live API):** Apify bills Standby's platform usage on top of the file price, because the browser stays warm between requests.

### Good to know

- **Very long pages** are cut at *Max page height* (20,000 px by default, up to 60,000). WebP files cannot exceed 16,383 px, so tall WebP captures are cut there. A one-page PDF can be up to 200 inches tall.
- **Sites that block robots** (Cloudflare challenges, CAPTCHAs) are not bypassed. Residential proxies and session cookies help on some of them.
- **Private addresses** (localhost, internal networks, cloud metadata) are refused.
- **Animations** are stopped before the capture, and the caret is hidden.
- **Timeouts:** a page that has shown content is still captured when its slowest requests never finish.
- **Files are kept** for as long as your Apify plan keeps the run's storage. Download them or copy them to your own storage if you need them longer.

### Coming from Apify's Website Screenshot Generator?

Your existing input works unchanged: `urls` as `{ "url": ... }` objects, `viewportWidth`, `scrollToBottom`, `delay`, `waitUntil` (including `networkidle2`) and `selectorsToHide` are all accepted. You get full-page captures, cookie banner handling, devices, WebP and PDF on top.

### FAQ

**Can I capture pages that need a login?** Yes. Pass session cookies in **Cookies**, or a token in **Extra HTTP headers**.

**How do I get a sharper image?** Pick **MacBook Retina** or a phone (2× to 3× pixel density), or set **Custom pixel density** to 2.

**Can it click something first, like a tab or a "Load more" button?** Yes, with **Custom JavaScript**: `document.querySelector('.tab-2').click()`. Add an **Extra delay** if the content takes a moment to appear.

**Is it legal to screenshot websites?** Capturing public pages is generally fine. Respect each site's terms of use and copyright when you publish or reuse what you capture.

### Open-source components

[DuckDuckGo autoconsent](https://github.com/duckduckgo/autoconsent) (MPL-2.0) for consent pop-ups, [Ghostery adblocker](https://github.com/ghostery/adblocker) (MPL-2.0) with EasyList, EasyPrivacy and uBlock Origin filter lists, and [Playwright](https://playwright.dev) (Apache-2.0) with Chromium.

### Support

Found a page that doesn't come out right? Open an issue on the **Issues** tab with the URL and your input. Made by WebThreenity.

# Changelog

This Actor's version history is a separate document: https://apify.com/webthreenity/website-screenshot-pdf/changelog.md

# Actor input Schema

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

Pages to capture. Plain URLs, one per line. `{ "url": "..." }` objects are accepted too, so input written for Apify's Website Screenshot Generator works unchanged.

## `html` (type: `string`):

Render this HTML instead of a URL. Handy for invoices, reports, social cards or email previews. Use absolute URLs for images and stylesheets.

## `sitemapUrl` (type: `string`):

Capture the pages listed in a sitemap.xml (sitemap indexes and .gz files work). Added to the URLs above.

## `maxSitemapPages` (type: `integer`):

Stop after this many sitemap pages.

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

PDF uses the PDF options below.

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

Every URL is captured once per device. Pick several to get desktop, tablet and phone versions in one run.

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

Used with the Custom size device.

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

Used with the Custom size device. With full page on, this is only the window height.

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

Used with the Custom size device. 2 gives a sharp Retina image.

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

Capture the whole scrollable page instead of the first screen. Social card and square devices always capture one screen.

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

Very long pages are cut at this height.

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

CSS selector. Only the first matching element is captured, for example `#pricing` or `.hero`.

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

Lower means smaller files. 85 looks the same as the original for most pages.

## `transparentBackground` (type: `boolean`):

PNG and WebP only. Pages with their own background colour stay opaque.

## `colorScheme` (type: `string`):

Dark asks the site for its dark mode, if it has one.

## `cookieBanners` (type: `string`):

Refuse clicks the 'Reject' or 'Necessary only' button of 300+ consent tools, then hides anything left. Accept clicks 'Accept all'. Hide only hides banners without clicking. Keep leaves them on the page.

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

Uses EasyList, EasyPrivacy and uBlock Origin filters. Faster pages and cleaner images.

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

CSS selectors to remove before the capture, for example a chat widget or a promo bar.

## `customCss` (type: `string`):

Added to the page before the capture.

## `customJs` (type: `string`):

Runs in the page before the capture. Can be async. Example: `document.querySelector('.tab-2').click()`

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

When the page counts as loaded. Network idle is slower but safer for heavy JavaScript sites.

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

Wait until this CSS selector is visible before capturing.

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

Wait this long after the page is ready, for late animations or charts.

## `scrollToLoad` (type: `boolean`):

Scrolls through the page first so lazy-loaded images and sections appear in full-page captures and PDFs.

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

Give up on a page after this long. Failed pages are not charged.

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

Paper size of each PDF page.

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

Turn the paper sideways. Good for wide tables and dashboards.

## `pdfMargin` (type: `string`):

One value for all sides (`10mm`) or four values top, right, bottom, left (`20mm 15mm 20mm 15mm`). Units: mm, cm, in, px.

## `pdfMedia` (type: `string`):

Screen keeps the website's normal look. Print uses the site's print stylesheet, if it has one.

## `pdfSinglePage` (type: `boolean`):

The whole website on one tall page, with no page breaks (up to 200 inches). Paper size and margins are ignored.

## `pdfPageNumbers` (type: `boolean`):

Adds 'Page 1 of 3' at the bottom of every page.

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

Keep background colours and images, like on screen.

## `pdfScale` (type: `number`):

Zoom the content, from 0.1 to 2.

## `cookies` (type: `array`):

Log in by passing session cookies: `[{ "name": "session", "value": "...", "domain": ".example.com" }]`.

## `headers` (type: `object`):

Sent with every request, for example `{ "Authorization": "Bearer ..." }`.

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

Overrides the device's user agent.

## `locale` (type: `string`):

Browser language, for example `fr-FR` or `en-US`.

## `timezone` (type: `string`):

IANA time zone, for example `Europe/Paris`.

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

Off by default. Datacenter proxies cost nothing extra. Residential proxies are charged per MB of traffic they carry, shown in each result as residentialTrafficMb (see Pricing).

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

0 picks the right number for the run's memory: 2 pages per CPU core (Apify gives 1 core per 4 GB). Higher values are capped at 4 per core.

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

How many more times to try a page that failed to load.

## `includeBase64` (type: `boolean`):

Adds the file as a base64 data URL to each dataset row. Handy for AI agents and no-code tools. Files over 5 MB are skipped.

## `url` (type: `string`):

Same as one entry in URLs.

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

Same as one entry in Devices.

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

Accepted for compatibility. Same as the Custom size device with this width.

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

Accepted for compatibility.

## `delay` (type: `integer`):

Accepted for compatibility. Same as Extra delay.

## `selectorsToHide` (type: `string`):

Accepted for compatibility. Comma-separated.

## Actor input object example

```json
{
  "urls": [
    "https://www.apify.com"
  ],
  "maxSitemapPages": 100,
  "format": "png",
  "devices": [
    "desktop"
  ],
  "width": 1280,
  "height": 800,
  "deviceScaleFactor": 1,
  "fullPage": true,
  "maxHeight": 20000,
  "quality": 85,
  "transparentBackground": false,
  "colorScheme": "light",
  "cookieBanners": "reject",
  "blockAds": true,
  "waitUntil": "load",
  "delayMs": 0,
  "scrollToLoad": true,
  "timeoutSecs": 45,
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfMargin": "10mm",
  "pdfMedia": "screen",
  "pdfSinglePage": false,
  "pdfPageNumbers": false,
  "pdfPrintBackground": true,
  "pdfScale": 1,
  "locale": "en-US",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxConcurrency": 0,
  "maxRetries": 1,
  "includeBase64": false
}
```

# Actor output Schema

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

One row per file: fileUrl (direct link to the image or PDF), url, finalUrl, device, format, width, height, pages, sizeKb, statusCode, title, cookieBanner, truncated, charged, loadTimeMs. Failed pages have an error field instead and are not charged.

## `storedFiles` (type: `string`):

Keys of the PNG, JPEG, WebP and PDF files in the run's key-value store, named <site>-<device>-<hash>.<extension>.

## `output` (type: `string`):

The file itself when the run captured exactly one file (what run-sync returns). Otherwise a JSON summary: captured, failed and the URL of every file.

# 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://www.apify.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("webthreenity/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://www.apify.com"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("webthreenity/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://www.apify.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call webthreenity/website-screenshot-pdf --silent --output-dataset

```

## MCP server setup

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