# Website Screenshot API & URL to PDF: Full-Page, Bulk (`mosaicked_sponge/website-screenshot-pdf`) Actor

Take full-page website screenshots and PDFs of any URL list or sitemap in bulk. Desktop, tablet and mobile sizes, cookie banners and ads removed, PNG, JPEG, WebP or PDF output, plus visual change detection for page monitoring.

- **URL**: https://apify.com/mosaicked_sponge/website-screenshot-pdf.md
- **Developed by:** [Jason](https://apify.com/mosaicked_sponge) (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 $1.60 / 1,000 screenshot or pdf delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 API & URL to PDF: Full-Page, Bulk

Take **screenshots or PDFs of many public web pages in one run**. You get clean output: cookie banners, ads, chat bubbles and "subscribe" pop-ups are removed before capture. Turn on **change-detection mode** to re-capture a list of pages on a schedule. You **pay the screenshot fee only for pages that changed**. Unchanged pages cost only a small comparison fee. Each changed page comes with a red-highlighted diff image.

![Website Screenshot API & URL to PDF: Full-Page, Bulk - sample output](https://api.apify.com/v2/key-value-stores/QpfrAb6wndEDB0tDd/records/output-ZQAJUp3K7M3QhgRfS.png)

Built on Playwright and headless Chromium.

### What it does

- **Batch URLs or sitemaps.** Paste up to 500 URLs per run, or give a `sitemap.xml` (sitemap indexes work too).
- **Viewport or full page.** Full-page mode scrolls the page first so lazy-loaded images appear. Height is capped by `maxHeight`, default 15,000 px.
- **Device presets:** `desktop` (1920×1080), `laptop` (1366×768), `tablet` (iPad), `mobile` (Pixel 7) and `iphone` (iPhone 14 at 3×). You can also set a `custom` viewport, scale factor and mobile emulation.
- **Formats:** PNG, JPEG, WebP or **PDF**. PDF paper size is A4, Letter, Legal, A3 or Tabloid, in portrait or landscape. PDFs use screen or print CSS.
- **Clean captures:**
  - Built-in rules for OneTrust, Cookiebot, Usercentrics, Quantcast, Didomi, TrustArc, Termly, Osano and other consent tools.
  - Blocks requests to common ad and tracker hosts.
  - A generic sweep hides fixed or sticky consent bars, full-screen modals and newsletter overlays.
  - Add your own CSS selectors with `hideSelectors`.
- **Element screenshots:** capture just one CSS selector, such as a pricing table or a chart.
- **Dark mode:** sets `prefers-color-scheme: dark`, `waitForSelector`, extra delay and a custom User-Agent.
- **Change detection (`mode: changeDetection`):**
  - Each page is compared with its baseline from the previous run of the same `monitorId`.
  - Pages that changed more than `diffThresholdPercent` are delivered with `diffPercent` and a diff image, then the baseline is updated.
  - Unchanged pages are **not delivered** and pay only the comparison fee ($1.00 / 1,000), never the screenshot fee.
  - For PDFs, the page text is compared instead of pixels.

### Output

Each delivered page is one dataset row:

```json
{
  "url": "https://www.python.org",
  "finalUrl": "https://www.python.org/",
  "status": "ok",
  "httpStatus": 200,
  "title": "Welcome to Python.org",
  "format": "png",
  "fileKey": "00001-www-python-org.png",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/00001-www-python-org.png",
  "bytes": 176212, "width": 1920, "height": 1080, "pdfPages": null,
  "device": "desktop", "viewport": "1920x1080@1x", "fullPage": false,
  "changeType": "changed", "diffPercent": 22.39,
  "diffImageUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/00001-www-python-org-diff.png",
  "loadMs": 2313, "captureMs": 2413, "capturedAt": "2026-10-20T01:40:23Z",
  "monitorId": "difftest"
}
```

- The files are stored in the run's **default key-value store** and linked from `fileUrl`.
- The `OUTPUT` record holds a run summary, with counts and the status of every URL.
- The `ERRORS` record lists every page that failed, was blocked or was skipped, with the reason.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Screenshot or PDF delivered (one dataset row) | **$2.00 / 1,000**, falling to $1.80 / $1.70 / $1.60 on Bronze / Silver / Gold+ Apify plans |
| Full-page screenshot or PDF (add-on) | +$2.50 / 1,000, so $4.50 / 1,000 in total |
| Page compared with the previous run (change-detection mode, from the second run on) | +$1.00 / 1,000 |

Examples:

- 1,000 viewport screenshots cost $2.00.
- 1,000 full-page screenshots or PDFs cost $4.50.
- A daily monitor of 1,000 pages in which 50 change costs $1.00 (comparisons) + $0.10 (50 changed screenshots) = **about $1.10 per day**.

You are **not charged** for:

- pages that failed, timed out, or returned HTTP errors;
- pages that were blocked or skipped (see acceptable use below);
- the screenshot of an **unchanged** page in change-detection mode. Only the comparison fee applies.

You can set a maximum total charge for the run. The Actor stops cleanly when it reaches that limit.

### Use cases

- **Bulk website screenshots:** capture thumbnails or previews for directories, link previews, portfolios and lead lists.
- **URL to PDF:** archive articles, invoices, documentation and legal pages as printable PDFs.
- **Full-page screenshots** for design reviews, client reports, SEO audits and competitor tracking.
- **Visual regression testing:** compare staging and production, or yesterday and today, with diff images.
- **Website change monitoring:** watch competitors' pricing pages, landing pages and terms of service, and get alerted only when something visibly changes.
- **Mobile screenshots:** see how pages render on an iPhone, an Android phone or a tablet.

### FAQ

**Can I use this as a screenshot API?**
Yes. Call the Actor through the Apify API, or run it synchronously with `run-sync-get-dataset-items`, and read `fileUrl` from the response. It also works from Make, Zapier, n8n and webhooks via Apify integrations.

**How do I get rid of cookie banners and pop-ups?**
They are removed by default (`blockCookieBanners`, `blockAds`, `removeOverlays`). Add any leftover selector to `hideSelectors`.

**Does it handle lazy-loaded images?**
Yes. Full-page mode scrolls the page before capturing so lazy images and infinite-scroll sections render.

**Can it log in to a site?**
No. It captures public pages only, by design. Login pages are skipped and not charged.

**Why did a site fail?**
Some sites block cloud servers (HTTP 403) or show a CAPTCHA. Those pages are reported in `ERRORS` and never charged.

### Acceptable use

This Actor is for capturing **public web pages that you are allowed to access and copy**. Typical uses are QA, visual regression, archiving, monitoring your own or competitors' public pages, and building previews. By default:

- **Login-walled pages are skipped.** These are pages that show a password field, `/login` and `/signin` URLs, and `accounts.` or `auth.` hosts. HTTP 401 pages are skipped too. `Cookie` and `Authorization` headers in `extraHeaders` are ignored. The Actor never captures behind a login.
- **Adult sites are refused**, using domain, TLD and keyword checks plus `rating` and `RTA` meta tags.
- **Private networks are refused.** That covers `localhost`, private, loopback and link-local IPs, internal hostnames, non-HTTP schemes and URLs with embedded credentials. This check applies to every redirect and sub-request.
- **CAPTCHA and bot-challenge pages fail** and are not charged. The Actor does not try to solve CAPTCHAs.

Do not use this Actor to capture illegal content or private, personal or copyrighted material you have no right to copy. Do not use it to break any site's terms of service. You are responsible for how you use the captures.

### Tips and limits

- Some sites block datacenter traffic, for example nytimes.com and ebay.com, which return HTTP 403. These pages are reported as failed and are not charged.
- A/B-tested pop-ups can occasionally slip through. Add their selector to `hideSelectors`.
- Very tall pages are cut at `maxHeight`. Images larger than about 8.5 MB are re-encoded or downscaled automatically.
- For monitoring, schedule the Actor (for example daily) with the same `monitorId` and the same device settings. Combine it with an Apify integration, such as email, Slack or a webhook, triggered when the dataset is not empty.
- Default memory is 2 GB, which handles about 4 parallel viewport pages or 1 full-page capture at a time. 1 GB works for viewport screenshots. Give the run 4 GB to work through large batches faster.

# Actor input Schema

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

Public web pages to capture, one per line. "example.com" is read as https://example.com. Localhost, private-network, login-walled and adult pages are refused.

## `sitemapUrls` (type: `array`):

Capture every page listed in these sitemap.xml files (sitemap indexes are followed one level). Capped by "Max URLs".

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

Viewport and device emulation preset. Choose "custom" to use the viewport fields below.

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

Capture the whole scrollable page (auto-scrolls first so lazy-loaded images appear). Full-page screenshots and PDFs are billed with an extra "full page or PDF" event.

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

Output file format. PDF renders the page as a printable document.

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

1-100. Ignored for PNG and PDF.

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

CSS selector of a single element to capture instead of the page, e.g. "#pricing".

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

Used when Device is "Custom".

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

Used when Device is "Custom".

## `deviceScaleFactor` (type: `integer`):

Used when Device is "Custom" (1-3).

## `isMobile` (type: `boolean`):

Used when Device is "Custom": mobile user agent, touch and meta-viewport.

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

Very long pages are cut at this height.

## `blockCookieBanners` (type: `boolean`):

Hides consent pop-ups from OneTrust, Cookiebot, Usercentrics, Quantcast, Didomi, TrustArc, Osano, CookieYes and similar, and unlocks page scrolling.

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

Blocks requests to common ad/tracking networks and hides ad slots.

## `removeOverlays` (type: `boolean`):

Also hides other fixed full-screen pop-ups (newsletter modals and similar).

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

Extra CSS selectors to hide, e.g. ".chat-widget".

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

Ask the site for its dark color scheme (prefers-color-scheme: dark).

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

Navigation event to wait for before capturing. "load" works for most sites; "networkidle" waits for background requests to settle.

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

Wait until this CSS selector appears before capturing.

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

Wait this long right before capturing (animations, fonts).

## `navigationTimeoutSecs` (type: `integer`):

Maximum time to wait for a page to load.

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

How many times to retry a page that failed to load. Failed pages are never charged.

## `mode` (type: `string`):

Change detection compares each page with the last delivered capture for the same Monitor ID. Unchanged pages are not delivered and pay only the small comparison fee, not the screenshot fee. The first run delivers everything as the baseline.

## `diffThresholdPercent` (type: `string`):

A page counts as changed when more than this percentage of pixels differ (PDFs: when the visible text changes). Example: 0.5

## `monitorId` (type: `string`):

Runs with the same Monitor ID share baselines. Use one per schedule.

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

Paper size for PDF output.

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

Render PDFs in landscape orientation.

## `pdfScreenMedia` (type: `boolean`):

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

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

Leave empty for a current Chrome user agent matching the device.

## `extraHeaders` (type: `object`):

E.g. {"Accept-Language": "de-DE"}. Cookie and Authorization headers are ignored: this Actor does not log in or reuse sessions.

## `captureErrorPages` (type: `boolean`):

By default pages answering 4xx/5xx are reported as failed and not charged.

## `skipLoginPages` (type: `boolean`):

Skip (and do not charge) pages that show a password field. Turn off only to capture your own public sign-in page.

## `maxUrls` (type: `integer`):

Safety cap on URLs per run (0 = no limit).

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

Default: 2 per GB of memory.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://www.wikipedia.org"
  ],
  "device": "desktop",
  "fullPage": false,
  "format": "png",
  "quality": 80,
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "deviceScaleFactor": 1,
  "isMobile": false,
  "maxHeight": 15000,
  "blockCookieBanners": true,
  "blockAds": true,
  "removeOverlays": true,
  "darkMode": false,
  "waitUntil": "load",
  "delayMs": 500,
  "navigationTimeoutSecs": 30,
  "maxRetries": 1,
  "mode": "capture",
  "diffThresholdPercent": "0.5",
  "monitorId": "default",
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfScreenMedia": true,
  "captureErrorPages": false,
  "skipLoginPages": true,
  "maxUrls": 500
}
```

# Actor output Schema

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

One row per delivered screenshot/PDF with a direct fileUrl.

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

The image/PDF files and change-highlight images.

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

OUTPUT record: per-URL status, unchanged/failed/blocked counts.

## `errors` (type: `string`):

ERRORS record: failed, blocked and skipped URLs with reasons (never 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"
    ]
};

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

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

```

## MCP server setup

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