# Website Screenshot & PDF API (Full Page, Mobile) (`saiberops/website-screenshot-api`) Actor

Screenshot any list of public URLs: viewport, full page, element or mobile as PNG, JPEG or WebP, or a PDF. Hides cookie banners without clicking, adds an optional thumbnail for AI agents, and never charges for failed or blocked pages.

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

## Pricing

from $2.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.
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 & PDF API: Full Page, Mobile, Element, Thumbnail

Give it a list of public URLs and get back a **screenshot or PDF of each page**, saved in the run's storage, plus one clean dataset row per URL with the file link, final URL, HTTP status, image size and timings. Cookie and consent banners are **hidden without clicking anything**, so captures show the page, not the pop-up. Failed, blocked and timed-out URLs are **never charged**.

**Built for:**

- **AI agents and LLM pipelines** that need to "see" a page. Turn on the free **thumbnail** for a small JPEG sized for vision models instead of downloading a large PNG.
- **SEO and marketing agencies** making audit decks: desktop and mobile homepage captures for a whole client list in one run.
- **QA and release teams** archiving how pages looked, before and after a deploy or migration.
- **Monitoring and brand-safety tools** that keep a visual record of landing pages and link targets.
- **No-code users** on Make, Zapier or n8n who want an image link for every URL in a sheet.

### What it does

- **Four capture types:** viewport (what a visitor sees first), **full page** (the whole scrolling page, with lazy images loaded), **element** (one CSS selector, e.g. a pricing table) and **PDF** (A4, Letter and more, screen or print styling).
- **Devices:** desktop 1280×800, desktop HD 1920×1080, tablet 768×1024 at 2x, **mobile 390×844 at 3x** with a real mobile layout, or any custom size.
- **Formats:** PNG, JPEG or WebP with adjustable quality.
- **Cookie banners hidden, never clicked:** about 30 common consent managers plus a heuristic for home-grown banners. Nothing is accepted or rejected on anyone's behalf. Each row says whether a banner was hidden and flags full-screen consent walls it left in place.
- **No idling:** "network idle" waits are capped at 8 seconds and every URL has a hard time budget, so one chatty page can't stall or inflate a run.
- **Honest rows:** every URL gets a row. Failures carry a clear error code and are free.
- **Run summary:** a `SUMMARY` record with counts, an error histogram, average timings and total bytes, so automations can branch on one record.

### Quick start

1. Paste URLs into **URLs to capture**, one per line.
2. Pick a **Capture type** and **Device** (defaults: viewport, desktop).
3. Click **Start**. Open the **Gallery** view of the results to see every capture; the files are in the run's key-value store.

### Sample output

One dataset row for `https://example.com` with **thumbnail** on:

```json
{
  "index": 0,
  "url": "https://example.com/",
  "finalUrl": "https://example.com/",
  "redirected": false,
  "ok": true,
  "statusCode": 200,
  "error": null,
  "errorDetail": null,
  "titlePresent": true,
  "captureType": "viewport",
  "device": "desktop",
  "viewport": { "width": 1280, "height": 800, "deviceScaleFactor": 1, "isMobile": false },
  "format": "png",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/shot-0000-example-com-viewport.png",
  "imageUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/shot-0000-example-com-viewport.png",
  "thumbnailUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/thumb-0000-example-com-viewport.jpg",
  "image": { "key": "shot-0000-example-com-viewport.png", "url": "https://api.apify.com/v2/key-value-stores/<storeId>/records/shot-0000-example-com-viewport.png", "format": "png", "contentType": "image/png", "bytes": 21544, "width": 1280, "height": 800 },
  "thumbnail": { "key": "thumb-0000-example-com-viewport.jpg", "url": "https://api.apify.com/v2/key-value-stores/<storeId>/records/thumb-0000-example-com-viewport.jpg", "bytes": 9120, "width": 512, "height": 320 },
  "pdf": null,
  "pageHeight": 800,
  "truncated": false,
  "loadComplete": true,
  "consentHidden": false,
  "consentWall": false,
  "blockedPrivateRequests": 0,
  "timings": { "navigationMs": 640, "settleMs": 530, "captureMs": 180, "totalMs": 1420 },
  "chargedEvent": "screenshot",
  "capturedAt": "2026-10-24T12:00:00+00:00"
}
```

A failed URL (free; abbreviated):

```json
{ "url": "https://blocked.example/", "ok": false, "error": "http_4xx_blocked", "errorDetail": "HTTP 403: the site refused the request; not retried.", "chargedEvent": null }
```

Error codes: `invalid_url`, `blocked_host`, `private_network`, `robots_disallowed`, `http_4xx_blocked` (401/403/429), `challenge_page`, `timeout`, `navigation_failed`, `selector_not_found`, `browser_crash`, `capture_failed`.

### Inputs

| Field | Default | What it does |
|---|---|---|
| URLs to capture | (required) | Public http(s) URLs, up to 500 per run. |
| Capture type | viewport | `viewport`, `fullPage`, `element` or `pdf`. One type per run. |
| Device | desktop | `desktop`, `desktopHd`, `tablet`, `mobile` or `custom` (width, height and pixel ratio fields). |
| Image format / quality | png / 80 | `png`, `jpeg` or `webp`; quality applies to JPEG and WebP. |
| Element selector | — | CSS selector for the `element` type, e.g. `#pricing`. |
| Also make a thumbnail | off | Free JPEG preview, longest edge 128–1024 px (default 512). |
| Hide cookie banners | on | Hides consent pop-ups with CSS. |
| Also hide these elements | — | Your own selectors, e.g. chat widgets or sticky bars. |
| Dark mode | off | Requests the page's dark color scheme. |
| Wait until | load | `load`, `domcontentloaded` or `networkidle` (max 8 s extra). |
| Extra delay | 500 ms | Settle time after load. |
| Wait for element | — | Waits for a selector; captures anyway if it never appears (`loadComplete: false`). |
| Scroll to load lazy images | on | Full page and PDF only. |
| Max height for full page | 16,384 px | In CSS px, up to 30,000. Taller pages are cut and flagged `truncated: true`; the image limits under Limits can cut sooner. |
| PDF options | A4, portrait, backgrounds, screen | Paper size, landscape, backgrounds, screen or print styling. |
| Timeout per URL | 30 s | Budget for loading, waiting and capturing one URL. |
| Pages in parallel | auto | One page per GB of memory (2 at 2 GB), up to 8. |
| Skip video and audio | on | Faster captures; images, fonts and CSS always load. |

### Pricing

Pay per event: you pay for captures that were saved, plus a small start fee per run (below).

| Event | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|
| Screenshot (viewport, element or mobile; any format) | $0.003 | $0.0025 | $0.00225 | $0.002 |
| Full-page screenshot | $0.0045 | $0.004 | $0.0036 | $0.0034 |
| PDF | $0.0055 | $0.005 | $0.0045 | $0.004 |

That's $2.00–$3.00 per 1,000 screenshots.

**Start fee:** each run also pays a start fee of $0.001 per GB of run memory, on every plan, because starting a browser costs more than one screenshot. At the default memory that is **$0.001 for a run of 1–2 URLs (1 GB) and $0.002 for 3 or more URLs (2 GB)**; if you raise the memory yourself, it is $0.001 per GB (for example $0.004 at 4 GB). Batch URLs into one run to pay it once.

**Thumbnails are free. Not charged:** invalid or refused URLs, robots.txt blocks, 401/403/429 responses, bot-protection challenges, timeouts, missing selectors, browser crashes and anything that couldn't be saved. A rendered 404 or 500 page is captured and charged, because you get the image. Set **Max cost per run** to cap a run: it stops cleanly at the limit and the `SUMMARY` record lists the URLs it skipped.

### Integrations and API

Call it from the Apify API or a client library with a list of URLs and read the dataset: each row's `screenshotUrl` is a direct link to the file (`imageUrl` is the same link, but empty for PDFs). Schedule it as a saved task for weekly site galleries, or connect it to Make, Zapier, n8n, Google Sheets or Slack through Apify's integrations. AI agents can call it through Apify's MCP server and use `thumbnailUrl` to look at pages cheaply.

### Limits

- **Public pages only.** No logins, cookies, headers or session tokens are accepted, so pages behind a sign-in show whatever a signed-out visitor sees.
- **Refused hosts:** facebook.com, fb.com, instagram.com, linkedin.com, x.com, twitter.com, tiktok.com, accounts.google.com, login.microsoftonline.com, login.live.com and console.apify.com (login-walled or their terms forbid automated capture).
- **Private and internal addresses are refused**, including after redirects and DNS resolution: localhost, private networks, link-local and cloud metadata addresses. Requests a page makes to such addresses are blocked and counted in `blockedPrivateRequests`.
- **robots.txt is always respected** for the URL and for wherever it redirects (same host or another). A site that blocks bots in robots.txt gives a free `robots_disallowed` row.
- **Bot protection is not bypassed.** 401, 403 and 429 responses and challenge pages (for example "Just a moment...") are reported and not retried, and the run's remaining URLs on that host get free rows without being loaded. No proxies are used.
- **Cookie banners are hidden, not dismissed.** A full-screen consent wall is left in place and flagged `consentWall: true` (still charged, since the capture is delivered).
- Up to 500 URLs per run and 60 seconds per URL at most.
- **Image size limits** (full page and element captures are cut and flagged `truncated: true` at whichever comes first):
  - **Max height**, up to 30,000 CSS px.
  - **32,000 device pixels tall** (WebP: 16,383). In CSS px that is 32,000 at 1x, 16,000 at 2x and about 10,666 at 3x (mobile).
  - **40 million device pixels in all**, to keep memory bounded. For example about 31,250 CSS px tall at 1280 wide and 1x, 20,833 at 1920 and 1x, and 7,812 at 1280 and 2x.
  - Custom viewports wider than 1920 px use a pixel ratio of at most 2.
- Files live as long as your Apify storage retention for the run. Download or copy anything you need to keep.
- Video and audio don't play in captures; WebGL-heavy pages may render differently than in a desktop browser.

### FAQ

**Why is my screenshot missing the cookie banner?** That's the point: it is hidden with CSS so you see the page. Turn off **Hide cookie banners** to capture it.

**Why does a row say `consentWall: true`?** The page covers most of the screen with a consent or paywall overlay. Hiding it could hide the page itself, so it is left in place and flagged.

**The thumbnail of my full-page capture only shows the top of the page.** Very tall pages are cropped to the top (up to twice as tall as wide) before shrinking, so the thumbnail stays readable. The full image is untouched.

**My own site shows `challenge_page` or `http_4xx_blocked`.** Allowlist the `SaiberOpsBot` User-Agent token in your firewall or CDN and run it again.

**Can I get both a screenshot and a PDF?** Run it twice with different capture types, or save two tasks.

**Why is `loadComplete` false?** The page's load event or your **Wait for element** didn't happen within the timeout. The page was captured anyway.

### Responsible use and data sources

The only data sources are the URLs you submit and their robots.txt files. The browser identifies itself with Chrome's own User-Agent plus the token `SaiberOpsBot`, respects robots.txt (RFC 9309), never logs in, never solves CAPTCHAs and does not rotate IP addresses. Cookie banners are hidden on screen only: no consent is given, recorded or stored. Screenshots show whatever the page shows, which can include personal data on some pages; you are responsible for the URLs you submit and for how you use the captures. The consent selector list is our own.

### Support

Found a bug or need a feature? Open an issue on this Actor's **Issues** tab and we'll respond there.

*Maintained by Saiber Operations LLC.*

# Changelog

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

# Actor input Schema

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

Public http(s) pages, one per line (up to 500 per run). Each URL gives one dataset row and one file in the key-value store. Login-walled social networks and private or internal addresses are refused.

## `captureType` (type: `string`):

viewport: what a visitor sees on load. fullPage: the whole scrolling page (up to Max height). element: one element matched by a CSS selector. pdf: a PDF of the page. One type per run; each has its own price.

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

desktop 1280×800, desktopHd 1920×1080, tablet 768×1024 at 2x, mobile 390×844 at 3x (mobile layout and touch). custom uses the three fields below.

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

Only used when Device is custom.

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

Only used when Device is custom.

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

Only used when Device is custom. 2 or 3 gives retina-sharp images (and bigger files). Limited to 2 when the viewport is wider than 1920 px.

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

png is lossless; jpeg and webp are much smaller. Ignored for PDF captures.

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

1 to 100. Ignored for PNG.

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

CSS selector for the element capture type, e.g. #pricing or main article. The first visible match is captured. If nothing matches, the row is an error and is not charged.

## `thumbnail` (type: `boolean`):

Adds a small JPEG next to each image, sized for vision models and previews. Not available for PDF.

## `thumbnailMaxPx` (type: `integer`):

Longest edge of the thumbnail, 128 to 1024. Very tall full-page images are cropped to the top of the page first.

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

Hides cookie and consent pop-ups with CSS. Nothing is clicked, accepted or rejected. Rows say whether a banner was hidden and whether a full-screen consent wall was left in place.

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

CSS selectors to hide before capturing, e.g. chat widgets or sticky promo bars (.intercom-launcher).

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

Asks the page for its dark color scheme (prefers-color-scheme: dark).

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

load (default) waits for the page's load event. domcontentloaded is faster. networkidle also waits for network quiet, capped at 8 seconds so busy pages can't stall the run.

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

Settle time after the page has loaded, for animations and late content.

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

Optional CSS selector to wait for before capturing. If it never appears, the page is captured anyway and the row says loadComplete: false.

## `scrollToLoadLazy` (type: `boolean`):

Full page and PDF only: scrolls through the page first so lazy-loaded images appear.

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

In CSS px. Taller pages are cut and flagged truncated: true. Images are also limited to 32,000 device px tall (about 10,666 CSS px at 3x; WebP 16,383) and 40 million device pixels in all (about 7,800 CSS px at 1280 wide and 2x).

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

Paper size for PDF captures.

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

PDF captures only.

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

Include background colors and images in the PDF.

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

screen renders what a visitor sees; print uses the site's print stylesheet.

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

Budget for loading, waiting and capturing one URL. Timed-out URLs are not charged.

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

0 = automatic: one page per GB of run memory (2 at the default 2 GB), up to 8. Raise memory for more parallelism.

## `blockMedia` (type: `boolean`):

Doesn't download video and audio files, which speeds up captures. Images, fonts and CSS always load.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "https://docs.apify.com"
  ],
  "captureType": "viewport",
  "device": "desktop",
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "deviceScaleFactor": 1,
  "format": "png",
  "quality": 80,
  "thumbnail": false,
  "thumbnailMaxPx": 512,
  "hideCookieBanners": true,
  "hideSelectors": [],
  "darkMode": false,
  "waitUntil": "load",
  "delayMs": 500,
  "scrollToLoadLazy": true,
  "maxHeight": 16384,
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfPrintBackground": true,
  "pdfMedia": "screen",
  "timeoutSecs": 30,
  "maxConcurrency": 0,
  "blockMedia": true
}
```

# Actor output Schema

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

No description

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

No description

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

No description

# 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://docs.apify.com"
    ],
    "hideSelectors": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("saiberops/website-screenshot-api").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://docs.apify.com",
    ],
    "hideSelectors": [],
}

# Run the Actor and wait for it to finish
run = client.actor("saiberops/website-screenshot-api").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://docs.apify.com"
  ],
  "hideSelectors": []
}' |
apify call saiberops/website-screenshot-api --silent --output-dataset

```

## MCP server setup

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

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/awapFSqyiTNb1LrEz/builds/91F4jqD8c55S6nfbl/openapi.json
