# Website Screenshot & PDF API: Full Page, Mobile, Cookie-Free (`tidewell/website-screenshot-pdf`) Actor

Full-page or viewport screenshots (PNG, JPEG, WebP) and PDFs of any URL on desktop, tablet or mobile. Rejects cookie banners, blocks ads, waits for selectors or network idle, dark mode, element capture, custom CSS/JS. Public file URLs. $2 per 1,000 screenshots; failures are free.

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

## Pricing

Pay per event

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 API: full page, mobile, no cookie banners

Capture **full-page or above-the-fold screenshots (PNG, JPEG, WebP) and PDFs** of any URL, on **desktop, laptop, tablet, iPhone or Android**, in real Chromium. Every file is saved to the run's key-value store, and you get a **public URL** for it in the dataset, along with the page title, final URL, HTTP status, pixel size, file size and timings.

- **Clean captures.** Cookie banners are **rejected** ("Reject all" / "Only necessary") on 25+ consent platforms and in 14 languages, or hidden when there is no reject button. "Accept all" is never clicked. Optional **ad and tracker blocking**, and a list of your own elements to hide.
- **Complete full-page shots.** The page is scrolled first so **lazy-loaded images** appear, animations are frozen at a sensible frame, and carousels don't come out blank.
- **Wait the way you need.** Load event, DOM ready or **network idle**, **wait for a CSS selector**, plus an extra delay.
- **Every device.** Desktop 1920x1080, laptop 1366x768, iPad, iPhone and Pixel presets (2x retina), or any custom viewport and pixel ratio. Capture each URL on several devices in one run.
- **PDFs that look like the page.** A4, Letter, Legal, A3, A5, Tabloid or **one long single page**, with margins, landscape, scale, background graphics and screen or print CSS.
- **More control.** Dark mode (`prefers-color-scheme: dark`), **element-only screenshots** by selector, custom CSS and JavaScript injection, custom user agent, **HTML to image / HTML to PDF**.
- **Pay per file.** **$2 per 1,000 screenshots, $3 per 1,000 PDFs**, plus $0.001 per run. Failed pages (DNS errors, timeouts, HTTP 404/500, missing selectors) are free, and so are retries.

### What can you use it for?

| Use case | How |
|---|---|
| **Website monitoring and visual change tracking** | Schedule a run with your URLs and compare today's PNGs with yesterday's |
| **SEO and client reports** | Capture landing pages on desktop and mobile in one run (`devices: ["desktop", "mobile"]`) |
| **Archiving and compliance evidence** | Full-page PDFs or PNGs saved to a named key-value store that keeps every capture |
| **Social cards, thumbnails and link previews** | Viewport JPEG or WebP at quality 70-80, or an element-only shot of a `#hero` section |
| **Invoices, receipts and certificates from HTML** | Pass `html` and render it to PDF or PNG |
| **QA and responsive design checks** | One URL x five devices, dark mode on and off |
| **AI agents and automations** | Call it from Make, Zapier, n8n, LangChain or Apify's MCP server and pass the `fileUrl` to a vision model |

### How to use it

1. Paste your **URLs**, one per line.
2. Choose one or more **devices**, and an **output format** (PNG, JPEG, WebP or PDF).
3. Leave **Full page** on to capture the whole page, or turn it off for the visible viewport only.
4. Click **Start**. Each capture appears in the **Output** tab with a preview, and the dataset can be downloaded as JSON, CSV or Excel, or read with the Apify API.

#### Input example: desktop and mobile full-page PNGs

```json
{
  "urls": ["https://apify.com", "https://en.wikipedia.org/wiki/Web_scraping"],
  "devices": ["desktop", "mobile"]
}
```

#### Input example: above-the-fold JPEG thumbnails with ads blocked

```json
{
  "urls": ["https://www.theguardian.com/uk", "https://www.lemonde.fr"],
  "fullPage": false,
  "format": "jpeg",
  "quality": 75,
  "blockAds": true,
  "waitUntil": "networkidle"
}
```

#### Input example: one element only, at 2x, in dark mode

```json
{
  "urls": ["https://en.wikipedia.org/wiki/Python_(programming_language)"],
  "selector": ".infobox",
  "deviceScaleFactor": 2,
  "darkMode": true,
  "hideSelectors": [".infobox-image"]
}
```

#### Input example: URL to PDF (A4, 10 mm margins) and one long single-page PDF

```json
{ "urls": ["https://apify.com/pricing"], "format": "pdf", "pdfFormat": "A4", "pdfMargin": "10mm" }
```

```json
{ "urls": ["https://apify.com"], "format": "pdf", "pdfFormat": "single-page" }
```

#### Input example: HTML to image

```json
{
  "html": "<html><body style='font-family:sans-serif;padding:60px'><h1>Invoice #1042</h1></body></html>",
  "devices": ["custom"],
  "viewportWidth": 800,
  "viewportHeight": 400,
  "fullPage": false
}
```

#### Input fields

| Field | What it does |
|---|---|
| `urls` | Pages to capture. `https://` is added when missing, and duplicates are removed |
| `devices` | Any of `desktop` (1920x1080), `laptop` (1366x768), `tablet` (iPad, 820x1180 @2x), `mobile` (iPhone, 393x852 @2x), `android` (Pixel, 412x915 @2x), `custom`. Each URL is captured once per device |
| `format` | `png` (default), `jpeg`, `webp` or `pdf` |
| `fullPage` | `true` (default) for the whole page, `false` for the viewport only |
| `quality` | 1-100 for JPEG and WebP (default 80) |
| `selector` | Capture only the first visible element matching this CSS selector |
| `html` | Raw HTML to render as an extra capture |
| `viewportWidth`, `viewportHeight` | Size of the `custom` device; on `desktop` and `laptop` they override the preset |
| `deviceScaleFactor` | Pixel ratio, 0.5-4 (2 = retina) |
| `maxHeight` | Full-page screenshots of endless pages are cut at this height in CSS pixels (default 20,000) |
| `waitUntil` | `load` (default), `networkidle` (capped at 20 s, then captured anyway) or `domcontentloaded` |
| `waitForSelector` | Wait until this element is visible. If it never appears, the capture fails and is free |
| `delayMs` | Extra wait just before the capture (default 500) |
| `scrollToLoadLazyContent` | Scroll through the page first so lazy images load (default on) |
| `navigationTimeoutSecs` | Page-load limit (default 45). A page that is usable when time runs out is still captured, with a warning |
| `dismissCookieBanners` | Reject or hide cookie banners (default on) |
| `hideSelectors` | Your own elements to remove, e.g. `#chat-widget` |
| `blockAds` | Block about 150 ad, analytics and tracking networks and collapse ad slots |
| `darkMode` | Emulate `prefers-color-scheme: dark` |
| `disableAnimations` | Freeze CSS animations and transitions (default on) |
| `customCss`, `customJs` | Injected before the capture. JavaScript runs as the body of an async function, so `await` works |
| `userAgent` | Override the device's user agent |
| `pdfFormat` | `A4` (default), `Letter`, `Legal`, `A3`, `A5`, `Tabloid`, `single-page` |
| `pdfLandscape`, `pdfMargin`, `pdfPrintBackground`, `pdfMediaType`, `pdfScale` | PDF layout. Margin takes a unit: `10mm`, `0.5in`, `1cm`, `20px` |
| `captureErrorPages` | Also capture (and pay for) pages that answer HTTP 4xx/5xx. Off by default |
| `maxConcurrency` | Pages captured in parallel (default 4, also limited by memory) |
| `maxRetries` | Retries for timeouts, connection errors and HTTP 429/5xx (default 1). Retries are free |
| `keyValueStoreName` | Save files to a named key-value store that outlives the run's data retention |
| `proxyConfiguration` | Optional Apify Proxy, for example to see a page as visitors in another country do |

### Output

One dataset row per URL and device:

```json
{
  "url": "https://apify.com",
  "source": "url",
  "status": "succeeded",
  "finalUrl": "https://apify.com/",
  "httpStatus": 200,
  "title": "Apify: Marketplace of ready-to-run tools for AI",
  "device": "desktop",
  "viewport": "1920x1080",
  "deviceScaleFactor": 1,
  "format": "png",
  "fullPage": true,
  "fileKey": "apify.com-desktop-2a0632.png",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/Dc5rB5jXL8A0nSMdK/records/apify.com-desktop-2a0632.png?signature=...",
  "contentType": "image/png",
  "width": 1920,
  "height": 10036,
  "bytes": 1199072,
  "pdfPages": null,
  "cookieBanner": {"action": "hidden", "platform": null, "hiddenElements": 3, "hidden": ["div#onetrust-consent-sdk", "div#onetrust-banner-sdk.otFloatingRoundedCorner.ot-bottom-left", "div.onetrust-pc-dark-filter.ot-hide"]},
  "blockedRequests": null,
  "timings": {"navigationMs": 1322, "waitMs": 0, "prepareMs": 3954, "captureMs": 810, "browserMs": 6172, "saveMs": 157, "totalMs": 6338},
  "totalMs": 6338,
  "attempts": 1,
  "warnings": [],
  "error": null,
  "errorCode": null,
  "charged": true,
  "capturedAt": "2026-11-08T03:54:02Z"
}
```

| Field | Notes |
|---|---|
| `fileUrl` | Public link to the file. It works without an API token, in a browser, `<img>` tag or vision model |
| `width`, `height` | Image size in pixels: CSS size x pixel ratio, so an iPhone capture is 786 px wide |
| `pdfPages` | Number of pages in a PDF |
| `cookieBanner.action` | `rejected` (a reject / necessary-only button was clicked), `hidden` (no reject button, so the banner was hidden) or `none` |
| `httpStatus` | Status of the main document. 4xx and 5xx count as failed unless `captureErrorPages` is on |
| `errorCode` | `dns_error`, `connection_error`, `navigation_timeout`, `http_404`, `http_403`, `wait_selector_not_found`, `selector_not_found`, `invalid_url`, ... |
| `warnings` | Things that didn't stop the capture: page cut at `maxHeight`, network never idle, a `customJs` error, ... |
| `charged` | Whether this row was billed. Failed rows are never billed |

The dataset has four views: **Overview** (with image previews), **Files & links**, **Details** (cookie banner, blocked requests, timings) and **Status & errors**. The files are in the run's key-value store, and the record `RUN_SUMMARY` lists every capture's status, including any skipped because the run hit its maximum cost.

### Pricing

Pay per event. You pay only for files that were saved:

| Event | Price | When |
|---|---|---|
| Screenshot | **$0.002** | Once per PNG, JPEG or WebP file saved |
| PDF | **$0.003** | Once per PDF saved |
| Run start | **$0.001** per run | $0.0005 per GB of memory, so $0.001 at the default 2 GB. It pays for starting the browser, so single-page API calls stay possible at this price |

Everything else is included: there's no charge for compute time, retries, failed pages, cookie handling, ad blocking or the dataset rows. Set **Max cost per run** in the run options to cap spending. The Actor reserves the price before each capture, so it stops cleanly before a page it couldn't pay for and lists the rest as skipped in `RUN_SUMMARY`.

Examples:

- 1 URL on desktop and mobile: **$0.005** ($0.001 start + 2 screenshots)
- 1,000 product pages in one run, one full-page PNG each: **$2.001**
- 50 client landing pages on 3 devices, one run a week for 4 weeks: 600 screenshots, **$1.204**
- One single-URL API call: **$0.003**

### Tips

- **Very long pages.** Full-page captures are cut at `maxHeight` (20,000 CSS px by default) so endless feeds don't produce giant files. Raise it up to 60,000 if you need more. WebP can't exceed 16,383 px per side, so taller WebP captures are scaled down to fit (with a warning). Use PNG or JPEG for full resolution.
- **Charts and single-page apps.** Use `waitUntil: "networkidle"` or `waitForSelector` for the element that appears last, plus a `delayMs` of 1000-2000.
- **Sticky headers in full-page shots.** Add `header { position: static !important; }` to `customCss`.
- **Clicking a tab before the capture.** Use `customJs`, e.g. `document.querySelector('#tab-pricing').click(); await new Promise(r => setTimeout(r, 500));`
- **Big batches.** Each page needs about 512 MB, so raise the run's memory (4-8 GB) and `maxConcurrency` for thousands of URLs. The price per file doesn't change.
- **Keep files longer.** Unnamed run storage follows your plan's data retention. Set `keyValueStoreName` (e.g. `site-archive`) to keep captures indefinitely.

### Other actors from Tidewell

- [ATS Jobs API](https://apify.com/tidewell/ats-jobs-api): jobs from Greenhouse, Lever, Ashby, Workable and 4 more ATSes
- [Document to Markdown for LLM & RAG](https://apify.com/tidewell/doc-to-markdown): PDF, Word, PowerPoint, Excel and scans to Markdown or RAG chunks
- [Google Trends API](https://apify.com/tidewell/google-trends-api): interest over time, rising queries and Trending Now
- [Google News API](https://apify.com/tidewell/google-news-api): Google News search with real article URLs and full text

### FAQ

**How does the cookie banner handling work?**
After the page loads, the Actor looks for a "Reject all" or "Only necessary" button from known consent platforms (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, Google Funding Choices, Sourcepoint, TrustArc, CookieYes, Complianz, Osano, iubenda, Klaro, Axeptio, consentmanager, Ketch, Borlabs, HubSpot, Termly, Shopify and others), including inside shadow DOM and consent iframes. It then looks for buttons with that wording in 14 languages. If there's no such button, the banner is hidden with CSS instead, and scrolling is unlocked. It never clicks "Accept all". It's best effort: an unusual banner can still slip through, and in that case you can add its selector to `hideSelectors`. The `cookieBanner` field in each row says what happened.

**Why did a page fail with `http_403`?**
Some sites block automated browsers or data-centre traffic. Those captures fail, and you are not charged. If you're allowed to capture the page, routing through Apify Proxy (`proxyConfiguration`) can help when a site blocks data-centre IP ranges. Turn on `captureErrorPages` if you want a picture of the error page itself.

**Is it legal to screenshot websites?**
Capturing a public page is generally fine for monitoring, QA, archiving and reporting. What you do with the image matters: copyright, trademarks and personal data shown on the page still belong to their owners, and some sites' terms restrict automated access. You're responsible for checking that your use complies with the law and the site's terms. This isn't legal advice.

**Can I capture pages behind a login?**
Not directly. You can pass session cookies by setting them with `customJs` (`document.cookie = ...`) and a reload, but only for accounts you own and are allowed to automate.

**Does dark mode work on every site?**
It sets `prefers-color-scheme: dark`, the same signal a phone in dark mode sends. Sites with a dark theme switch to it, and sites without one don't change.

**Why is the iPhone screenshot 786 px wide?**
The iPhone preset renders a 393 CSS px wide viewport at a 2x pixel ratio, so text is retina-sharp without huge files. Set `deviceScaleFactor: 3` for the exact pixels of a real iPhone 15 (1179 px), or `1` for a 393 px image.

**Can I schedule it?**
Yes. Use Apify Schedules, then send the dataset to Google Sheets, Slack, a webhook, Make or Zapier, or read the files from a named key-value store.

**Something looks wrong?**
Open an issue on the Actor's Issues tab with the run ID and the URL. Rendering fixes are shipped quickly.

### Keywords

website screenshot, screenshot api, full page screenshot, url to pdf, html to pdf, html to image, webpage to png, url to image, mobile screenshot, responsive screenshot, website thumbnail, page capture, web archive, visual monitoring, cookie banner remover, playwright screenshot, chromium screenshot, puppeteer screenshot alternative

# Actor input Schema

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

Pages to capture, one per line. 'https://' is added when missing. Duplicates are removed. Each URL is captured once per device below.

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

Capture every URL on each selected device. Desktop 1920x1080, Laptop 1366x768, Tablet (iPad Air) 820x1180 @2x, Mobile (iPhone 15) 393x852 @2x, Android (Pixel 9) 412x915 @2x. 'Custom' uses the viewport width and height below. Each device is one charged capture.

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

PNG is lossless, JPEG and WebP are smaller (use Quality). PDF renders the page as a printable document (see the PDF section).

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

Capture the whole scrollable page. Off captures only the visible viewport (above the fold).

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

1-100. Ignored for PNG and PDF.

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

Capture just the first visible element matching this selector, e.g. '#pricing' or 'main article'. The capture fails (free) if no element matches. Images only.

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

Optional raw HTML to render as an extra capture (HTML to image / HTML to PDF). Relative links won't resolve; use absolute URLs for images and CSS.

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

200-3840. Default for Custom: 1280.

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

200-4320. Default for Custom: 800. For full-page captures this only affects the layout, not the image height.

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

Pixel density, 0.5-4. Leave empty for the device's own (desktop and laptop 1, tablet and phones 2). 3 matches a real iPhone. 2 gives a crisp retina image twice the width.

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

Full-page screenshots of endless pages are cut at this height (CSS pixels), with a warning in the row.

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

'Load' suits most sites. 'Network idle' waits until no requests for 500 ms (best for SPAs and charts; capped at 20 s, then captured anyway). 'DOM ready' is fastest.

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

Wait until an element matching this CSS selector is visible, e.g. '.chart canvas'. If it never appears the capture fails and is not charged.

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

Wait this long after everything else, just before the capture. Helps with animations and late widgets.

## `scrollToLoadLazyContent` (type: `boolean`):

For full-page captures and PDFs, scroll through the page first so lazy-loaded images and sections appear.

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

Maximum time for the page to load. If the page is usable when it runs out, it is captured anyway with a warning.

## `dismissCookieBanners` (type: `boolean`):

Best effort: clicks 'Reject all' / 'Only necessary' on 25+ consent platforms (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, Google, Sourcepoint, TrustArc...) and in 14 languages. Never clicks 'Accept all'. If there is no reject button, the banner is hidden instead.

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

Elements to remove before the capture, one selector per line, e.g. '#chat-widget', '.newsletter-popup'.

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

Blocks requests to ~150 ad, analytics and tracking networks and collapses common ad slots. Faster captures, cleaner pages.

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

Emulate prefers-color-scheme: dark. Only sites that support dark mode change.

## `disableAnimations` (type: `boolean`):

Stop CSS animations and transitions and ask for reduced motion, so captures are consistent.

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

Injected into the page before the capture, e.g. 'header { position: static !important; }'.

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

Runs in the page before the capture, as the body of an async function (await works). Example: document.querySelector('#tab-2').click(); await new Promise(r => setTimeout(r, 500));

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

Override the device's user agent. Leave empty for a normal Chrome (or iPhone / iPad / Android) user agent.

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

'Single long page' puts the whole page on one tall page, exactly as it looks on screen (up to 200 inches).

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

Landscape orientation.

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

Same margin on all sides, with a unit: px, mm, cm or in, e.g. '10mm' or '0.5in'. 0 for none.

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

Include background colours and images (as on screen).

## `pdfMediaType` (type: `string`):

'Screen' renders the page as it looks in the browser. 'Print' uses the site's print stylesheet, if it has one.

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

Zoom of the rendered page, 0.1-2.

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

By default a page that answers 404, 500, 403 etc. is reported as failed and not charged. Turn on to capture (and pay for) error pages too.

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

How many pages to capture at once. Also limited by memory (about 512 MB per page).

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

Retries for timeouts, connection errors and HTTP 429/5xx. Retries are free.

## `keyValueStoreName` (type: `string`):

Leave empty to use the run's own store. A named store (e.g. 'site-archive') keeps files beyond the data retention period and collects captures from many runs.

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

Optional. Use Apify Proxy to capture pages as seen from another country, or when a site blocks datacenter traffic.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ],
  "devices": [
    "desktop",
    "mobile"
  ],
  "format": "png",
  "fullPage": true,
  "quality": 80,
  "maxHeight": 20000,
  "waitUntil": "load",
  "delayMs": 500,
  "scrollToLoadLazyContent": true,
  "navigationTimeoutSecs": 45,
  "dismissCookieBanners": true,
  "blockAds": false,
  "darkMode": false,
  "disableAnimations": true,
  "pdfFormat": "A4",
  "pdfLandscape": false,
  "pdfMargin": "10mm",
  "pdfPrintBackground": true,
  "pdfMediaType": "screen",
  "pdfScale": 1,
  "captureErrorPages": false,
  "maxConcurrency": 4,
  "maxRetries": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `captures` (type: `string`):

One row per URL and device: file URL, size, HTTP status, title, timings and any error.

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

The PNG / JPEG / WebP / PDF files in the run's key-value store.

## `runSummary` (type: `string`):

Counts, charges and the status of every capture, including skipped ones.

# 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/Web_scraping"
    ],
    "devices": [
        "desktop",
        "mobile"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidewell/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/Web_scraping",
    ],
    "devices": [
        "desktop",
        "mobile",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("tidewell/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/Web_scraping"
  ],
  "devices": [
    "desktop",
    "mobile"
  ]
}' |
apify call tidewell/website-screenshot-pdf --silent --output-dataset

```

## MCP server setup

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