# Website Screenshot & URL to PDF (`hpbt/website-screenshot-pdf`) Actor

Full-page screenshots (PNG, JPEG, WebP) or PDFs of any list of URLs. Desktop, laptop, tablet and mobile views. Cookie banners and chat widgets hidden, lazy images loaded. Failed URLs and blank pages are free.

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

## Pricing

from $2.00 / 1,000 screenshot or pdfs

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 & URL to PDF

Take **full-page screenshots** of any list of URLs as **PNG, JPEG or WebP**, or turn web pages into **PDF** documents. Capture desktop, laptop, tablet and mobile views in one run. Cookie banners and chat widgets are hidden, lazy-loaded images are loaded, and **you only pay for real captures**: failed URLs and blank pages are free.

### What it does

- 📸 **Full-page or first-screen screenshots**, up to 16,384 px tall and exactly as wide as the screen you chose. Long pages come out right instead of repeating the top of the page halfway down.
- 🖼️ **PNG, JPEG, WebP or PDF**. PDFs in A4, Letter, Legal, A3 or Tabloid, portrait or landscape, with selectable text.
- 📱 **Desktop (1920×1080), laptop (1440×900), tablet (820×1180), mobile (393×852)** or any custom size, with real mobile emulation and an optional retina (2×, 3×) pixel density.
- 🍪 **Clean captures**: cookie and consent banners (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, CookieYes, Sourcepoint and many more) and chat widgets (Intercom, Drift, HubSpot, Zendesk, Crisp, Tidio, LiveChat…) are hidden. Nothing is clicked and no consent is given.
- 🐢 **Lazy-loaded images appear**: the page is scrolled through before the capture.
- 🧩 **Web apps captured whole**: dashboards and apps that scroll a panel instead of the page come out complete, not just their first screen.
- ⏳ **No loading screens**: a page that is still blank or shows only a spinner after loading gets up to 10 more seconds to show its content.
- 🎯 **Element screenshots**: capture only `#pricing`, `main` or any CSS selector.
- 🌙 **Dark mode**, browser language, custom CSS, hide any element, wait for an element, extra delay.
- ⏱️ **Slow pages still work**: a page that has not finished loading after 15 seconds is captured anyway.
- ✅ **Clear results**: one row per file with a preview, a download link, the HTTP status and a plain-English error for every failed URL.

### How to use it

1. Paste your URLs (one per line; `example.com` works too).
2. Choose the format and the devices.
3. Click **Start**. Each file shows up in the **Output** tab with a preview, and `fileUrl` is its download link.

### Pricing

**$2.00 per 1,000 files** ($0.002 per screenshot or PDF), with pay-per-event pricing.

- You pay only for files that were saved, plus a tiny start fee of about $0.0001 per run. URLs that time out, fail to load or show a bot check cost nothing.
- A page that stays blank or keeps showing only a loading message is free too. You still get its file, marked `"status": "blank"`.
- Each URL × device is one file. 500 pages on desktop and mobile = 1,000 files = $2.00.
- Set a **maximum cost per run** in the run options, and the Actor stops when it reaches it.

### Input example

```json
{
    "urls": ["https://apify.com", "example.com/pricing"],
    "format": "png",
    "devices": ["desktop", "mobile"],
    "fullPage": true,
    "hideCookieBanners": true
}
```

All other settings are optional. See the **Input** tab for the full list.

### Output example

One dataset row per file:

```json
{
    "url": "https://apify.com/",
    "finalUrl": "https://apify.com/",
    "device": "desktop",
    "format": "png",
    "status": "success",
    "fileUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/apify-com-desktop-1a2b3c4d.png",
    "previewUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/apify-com-desktop-1a2b3c4d.png",
    "key": "apify-com-desktop-1a2b3c4d.png",
    "httpStatus": 200,
    "pageTitle": "Apify: Full-stack web scraping and data extraction platform",
    "width": 1920,
    "height": 8412,
    "pixelRatio": 1,
    "bytes": 2317412,
    "truncated": false,
    "durationSecs": 4.1,
    "capturedAt": "2026-10-27T06:38:42.862Z",
    "error": null
}
```

A failed URL has `"status": "failed"`, no file, and a reason such as `"The domain does not exist (DNS lookup failed)."` A page that stayed blank has `"status": "blank"` and its file, and costs nothing.

The files themselves are in the run's key-value store, named after the page and device.

### Use it from your code or an AI agent

Run the Actor and get the results in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/Hpbt~website-screenshot-pdf/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com"], "devices": ["mobile"]}'
```

- **JavaScript or Python**: use the Apify client (see the **API** tab for ready-made code).
- **AI agents**: the Actor works through the Apify MCP server, so agents can open a page and "see" it as an image.
- **No-code**: connect it to Zapier, Make, n8n, Google Drive or Slack through Apify integrations.

### Tips

- **Charts or animations missing?** Set **Wait until** to *Network idle*, add an **Extra delay**, or use **Wait for element**.
- **Sharper images:** set **Pixel density** to 2. A page that would then be taller than 16,384 px is captured at 1× so it still fits completely, and `pixelRatio` in the output says which one was used.
- **Only the top of the page:** turn off **Full page**, or set **Maximum page height** to the height you want.
- **Very long or endless pages:** lower **Maximum page height**. `truncated: true` tells you when a page was cut.
- **PDF looks like a phone layout?** PDFs are laid out for the paper width, as when printing in Chrome. Try **Landscape** or a bigger paper size.

### Limitations

- No logging in, no CAPTCHA solving and no bot-protection bypass. Pages that answer with a bot check are reported as failed, and they are free.
- Local and private network addresses (localhost, 10.x, 192.168.x…) are blocked.
- Images are at most 16,384 px tall (WebP 16,383 px). Browsers can't render taller images correctly.
- Residential and SERP proxies are not supported. Automatic and datacenter Apify Proxy, and your own proxies, are.

### FAQ

**Is taking screenshots of websites legal?**
Capturing public pages for testing, monitoring, archiving or research is common practice. If you publish screenshots of someone else's site, respect their copyright and terms. This is not legal advice.

**Can it capture social networks?**
Usually not in a useful way. Social networks show a login wall or a bot check to visitors who are not logged in, and this Actor does not log in. Bot checks are free. A login wall is captured as it appears.

**How long are the files kept?**
As long as your Apify plan keeps run storage. Download them or send them on with an integration if you need them longer.

**Something looks wrong?**
Open an issue in the **Issues** tab with the URL and the input you used.

# Changelog

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

# Actor input Schema

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

Pages to capture, one per line. "example.com" works too, https:// is added for you.

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

PNG is lossless. JPEG and WebP files are several times smaller. PDF gives a printable document with selectable text.

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

Each URL is captured once per device. Custom uses the viewport width and height under Image options.

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

Capture the whole page from top to bottom. Turn off to capture only the first screen.

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

Hide cookie and consent pop-ups (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, CookieYes and many more), including the dark overlay behind them.

## `hideChatWidgets` (type: `boolean`):

Hide floating chat buttons and windows (Intercom, Drift, HubSpot, Zendesk, Crisp, Tidio, LiveChat and others).

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

CSS selectors of anything else to hide, for example .newsletter-popup or #promo-bar.

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

CSS added to every page before it is captured.

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

When the page counts as ready. A page that has not finished loading after 15 seconds is captured anyway.

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

Optional CSS selector to wait for before capturing, for example #chart canvas.

## `delaySecs` (type: `number`):

Seconds to wait after the page is ready, for animations or slow widgets.

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

Scroll through the page before a full-page, element or PDF capture, so images that load on scroll appear.

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

Dark works on sites that support dark mode.

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

Locale code such as en-US, de-DE or vi-VN. Sites that adapt to the visitor's language use it.

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

Maximum time for one URL, from opening it to saving the file.

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

Optional CSS selector, for example #pricing or main. The first visible match is captured. Images only.

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

Viewport width for the Custom device.

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

Viewport height for the Custom device. It also sets the image height when Full page is off.

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

1 is standard, 2 is retina: sharper, with files about 4 times larger. Leave empty for 1 on desktop and laptop, 2 on tablet and mobile.

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

Full-page captures are cut at this height, so endless pages don't make huge files. Images are at most 16,384 pixels tall (above that, browsers repeat the top of the page). A long page that would be taller at 2× pixel density is captured at 1× so the whole height still fits.

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

For JPEG and WebP. Higher is sharper and larger.

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

Pages are laid out for this paper width, as when printing in Chrome.

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

Wide pages instead of tall ones.

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

Keep background colors and images, as on screen.

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

"As on screen" keeps the normal look. "Print" uses the site's print style, if it has one.

## `pdfMarginMm` (type: `integer`):

Blank border on every side of each PDF page.

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

How many pages to capture in parallel. The Actor slows down by itself when memory runs low.

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

How many more times to try a URL after a server error. A URL that runs out of time is tried once more at most.

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

Optional, for sites that show different content per country. Automatic and datacenter Apify Proxy or your own proxy URLs work. Residential and SERP proxies are not supported.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Screenshot"
  ],
  "format": "png",
  "devices": [
    "desktop"
  ],
  "fullPage": true,
  "hideCookieBanners": true,
  "hideChatWidgets": true,
  "waitUntil": "load",
  "delaySecs": 0,
  "scrollPage": true,
  "colorScheme": "light",
  "locale": "en-US",
  "timeoutSecs": 60,
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "maxHeight": 15000,
  "quality": 80,
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfPrintBackground": true,
  "pdfMedia": "screen",
  "pdfMarginMm": 0,
  "maxConcurrency": 5,
  "maxRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Results table. fileUrl is the download link of each file. status is success, blank (the page stayed blank, free) or failed (no file, free).

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

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

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

```

## MCP server setup

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