# Website Screenshot & PDF API – Full-Page, Mobile, Batch (`batuweb/website-screenshot`) Actor

- **URL**: https://apify.com/batuweb/website-screenshot.md
- **Developed by:** [Batulzii Ulziikhuu](https://apify.com/batuweb) (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 $0.80 / 1,000 screenshot captureds

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 & Batch

Capture **full-page or viewport screenshots** of any list of URLs as **PNG, JPEG, WebP or PDF** – with desktop, tablet, mobile or custom viewports, **dark mode**, **automatic cookie-banner hiding**, **lazy-image scrolling**, **element capture** and **batch concurrency**. Every capture is saved to the run's key-value store and listed in a dataset with the page title, final URL, HTTP status, size and a direct file link.

**You pay only for successful screenshots.** Pages that answer with an HTTP error (4xx/5xx), time out, are blocked by robots.txt, point to non-public addresses or are invalid cost nothing.

### What you get

| Feature | Details |
|---|---|
| Full-page **and** viewport captures | Full page up to `maxFullPageHeight` (default 10,000 px) |
| Device presets | Desktop, laptop, tablet, mobile, custom size, retina (1x–3x) |
| Formats | PNG, JPEG, **WebP**, **PDF**, JPEG/WebP quality setting |
| Cookie banners hidden | ≈45 consent managers hidden by CSS – nothing is clicked, no consent is given |
| Lazy-loaded images | Optional auto-scroll before capturing |
| Single element | Capture only `#pricing`, `header` etc. |
| Timing controls | Wait for selector, extra delay, network idle |
| Batch | Up to 5,000 URLs per run, up to 10 pages in parallel |
| Clear per-URL error codes | Every failed URL gets a free error record |
| Pay per successful capture | Budget-aware: stops when your max cost per run is reached |

Apify's own [Screenshot URL](https://apify.com/apify/screenshot-url) Actor also offers URL lists, PDF output, scrolling, delay/network-idle timing and selectors to hide. This Actor adds WebP output, device presets, built-in cookie-banner hiding, element capture and per-URL error codes with pay-per-success pricing – pick whichever fits your workflow.

### Use cases

- **Visual monitoring** – schedule daily screenshots of your site or competitors' landing pages.
- **SEO & client reports** – mobile vs desktop above-the-fold captures for audits.
- **Archiving / compliance** – save pages as PDF with backgrounds.
- **Link previews & thumbnails** – small WebP/JPEG captures for directories and apps.
- **QA** – check layouts in dark mode or on retina devices after each deploy.

### Input example

```json
{
  "urls": [{ "url": "https://apify.com" }, { "url": "https://example.com" }],
  "format": "webp",
  "quality": 80,
  "fullPage": true,
  "device": "mobile",
  "hideCookieBanners": true,
  "darkMode": false,
  "waitForSelector": "",
  "delayMs": 0,
  "maxConcurrency": 3
}
```

Empty input `{}` captures a full-page PNG of https://apify.com.

Main options: `format` (png, jpeg, webp, pdf), `quality`, `fullPage`, `device` (desktop, laptop, tablet, mobile, custom) + `viewportWidth`/`viewportHeight`/`deviceScaleFactor`, `darkMode`, `hideCookieBanners`, `selectorsToHide`, `scrollToBottom`, `maxFullPageHeight` (default 10,000 px), `selector` (element capture), `waitUntil`, `waitForSelector`, `delayMs`, `navigationTimeoutSecs`, PDF `pdfPaper`/`pdfLandscape`/`pdfPrintBackground`, `maxConcurrency`, `maxRetries`, `maxScreenshots`, `captureErrorPages`, `ignoreRobotsTxt`, `proxyConfiguration`.

### Output

One dataset record per URL (successful and failed), plus the file in the key-value store:

```json
{
  "success": true,
  "url": "https://apify.com",
  "finalUrl": "https://apify.com/",
  "status": 200,
  "title": "Apify: Marketplace of ready-to-run tools for AI",
  "format": "png",
  "device": "desktop",
  "width": 1366,
  "height": 10000,
  "truncated": true,
  "fileSize": 939755,
  "imageUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/0001-apify-com-b2cbecea.png",
  "key": "0001-apify-com-b2cbecea.png",
  "errorCode": null,
  "error": null
}
```

Failed URLs get `success: false` and an `errorCode` such as `TIMEOUT`, `DNS_ERROR`, `HTTP_404`, `HTTP_403`, `SELECTOR_NOT_FOUND`, `ROBOTS_DISALLOWED`, `BLOCKED_PRIVATE_ADDRESS`, `TOO_MANY_REDIRECTS` or `INVALID_URL`. PDFs report `pageCount`; `truncated: true` means the height limit cut the capture. A run summary is stored as `OUTPUT` in the key-value store. Export everything as JSON, CSV or Excel, or fetch it via the Apify API.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Screenshot captured (PNG/JPEG/WebP) | **$0.002** ($0.0015 Bronze, $0.001 Silver, $0.0008 Gold+) |
| PDF captured | $0.003 ($0.0025 / $0.002 / $0.0015) |
| Actor start | $0.00005 per GB of memory |

1,000 full-page screenshots cost about **$2** on the free plan, with platform compute included. Failed, blocked and skipped URLs are free. The run's maximum cost is respected: the Actor stops starting new captures when your limit is reached.

### API usage

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('batuweb/website-screenshot').call({ urls: [{ url: "https://example.com" }], format: 'webp' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].imageUrl);
```

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("batuweb/website-screenshot").call(run_input={"urls": [{"url": "https://example.com"}], "device": "mobile"})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["imageUrl"])
```

### Responsible use

- **Only capture pages you are authorised to capture** – your own sites, clients' sites with permission, or public pages whose terms allow it. You are responsible for the URLs you submit and for how you use the images (copyright, personal data / GDPR, the site's terms of use).
- **robots.txt is respected by default**, including for every redirect hop. URLs disallowed for automated agents are skipped free of charge; if robots.txt is unreachable (server error, network error) the site is treated as fully disallowed, as RFC 9309 requires. The browser identifies itself with the token `WebsiteScreenshotBot`. `ignoreRobotsTxt` is meant only for sites you own or are explicitly allowed to capture.
- **Respecting robots.txt is not permission.** robots.txt is a crawler courtesy signal, not access authorisation (RFC 9309), and it does not replace a site's terms, copyright or privacy law. The safeguards here reduce risk; they are not legal clearance for any particular use.
- The Actor does **not** bypass logins, paywalls, CAPTCHAs or bot protection. Pages that answer with an HTTP error (e.g. `HTTP_403`) give a free error record. A block or CAPTCHA page served with HTTP 200 looks like a normal page and is captured as rendered. The proxy is off by default.
- Only public websites: localhost, private networks, link-local/cloud-metadata addresses and their IPv6 / encoded variants are blocked – for the page, every redirect, every subresource and robots.txt alike. All browser traffic leaves through a local guard that checks each connection's resolved address.
- Cookie banners are hidden visually; nothing is clicked or accepted on your behalf.

### FAQ

**How long can a capture be?** `maxFullPageHeight` (500–16,000 CSS px, default 10,000) applies to every mode: full-page images and element captures are cut at that height, and PDFs get as many pages as fit into that length (at least one). Cut captures are marked `truncated: true`. WebP is additionally limited to 16,383 image pixels per side.

**Why did a page fail with HTTP 403?** The site blocks automated browsers. We don't try to get around that; such failures are free.

**Why `ROBOTS_DISALLOWED` although the URL looks allowed?** Either it redirects to a disallowed path, or the site's robots.txt returned a server error / could not be reached, which counts as "disallow everything".

**Can I capture pages behind a login?** No, this tool only captures publicly reachable pages.

### More tools by batuweb

- [SEO Audit Tool – Site Crawler, Broken Links & Meta Checker](https://apify.com/batuweb/seo-audit-toolkit)
- [Google Trends Scraper](https://apify.com/batuweb/google-trends-scraper)
- [AI Search Visibility Checker](https://apify.com/batuweb/ai-search-visibility-checker)

Built with Playwright (Apache-2.0), Chromium (BSD-3-Clause), proxy-chain (Apache-2.0), ipaddr.js (MIT) and sharp (Apache-2.0). Not affiliated with any of the websites you capture.

# Actor input Schema

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

Pages to capture. Only capture pages you are authorised to capture (your own sites, clients' sites with permission, or public pages whose terms allow it). Duplicates are removed.

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

Output file type. PDF is billed as a separate 'PDF captured' event.

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

1–100. Ignored for PNG and PDF.

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

Capture the whole scrollable page (up to 'Max full-page height'). Off = only the visible viewport.

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

Viewport preset. Tablet and mobile also send a mobile user agent and enable touch.

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

Overrides the preset width (required for 'Custom', default 1366).

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

Overrides the preset height (default 768 for 'Custom').

## `deviceScaleFactor` (type: `string`):

Device pixel ratio (retina = 2). Empty = preset default.

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

Emulate prefers-color-scheme: dark (works on sites that support it).

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

Hides ~45 common consent managers (OneTrust, Cookiebot, Usercentrics, Didomi, Sourcepoint, Borlabs …) by CSS. Nothing is clicked or consented.

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

One selector per line, e.g. chat widgets or popups.

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

Scrolls through the page before a full-page or PDF capture so lazy-loaded images appear.

## `maxFullPageHeight` (type: `integer`):

Height limit for every mode: full-page images and element captures are cut at this height, PDFs get as many pages as fit into this length (at least one). Cut captures show truncated: true. WebP is also limited to 16,383 image pixels.

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

Screenshot only the first matching visible element, e.g. 'header' or '#pricing'. Image formats only.

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

Navigation event to wait for.

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

Optional CSS selector that must be visible before capturing.

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

Wait this long after the page is ready (animations, charts).

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

Max time for loading a page.

## `pdfPaper` (type: `string`):

Only for PDF.

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

Only for PDF.

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

Only for PDF.

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

Pages captured at the same time. Raise memory for >4.

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

Retries after timeouts or connection errors. Failed URLs are never charged.

## `maxScreenshots` (type: `integer`):

Stop after this many successful captures (0 = no limit). The run's max cost limit is also respected.

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

Off: 404/500 pages become free error records. On: they are captured and charged.

## `ignoreRobotsTxt` (type: `boolean`):

By default URLs (and redirect targets) disallowed by the site's robots.txt are skipped (free); an unreachable robots.txt counts as full disallow. Only enable for sites you own or are explicitly authorised to capture. robots.txt is not permission either way.

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

Off by default (direct, fastest). If you enable a proxy and it can't start, the run stops before any page is requested.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://apify.com"
    }
  ],
  "format": "png",
  "quality": 80,
  "fullPage": true,
  "device": "desktop",
  "darkMode": false,
  "hideCookieBanners": true,
  "scrollToBottom": true,
  "maxFullPageHeight": 10000,
  "waitUntil": "load",
  "delayMs": 0,
  "navigationTimeoutSecs": 45,
  "pdfPaper": "a4",
  "pdfLandscape": false,
  "pdfPrintBackground": true,
  "maxConcurrency": 3,
  "maxRetries": 1,
  "maxScreenshots": 0,
  "captureErrorPages": false,
  "ignoreRobotsTxt": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `screenshots` (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": [
        {
            "url": "https://apify.com"
        }
    ]
};

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

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

```

## MCP server setup

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

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/SgXAWU0acu9ygG0Tl/builds/6tvWKhk57iKgJdyQx/openapi.json
