# Website Screenshot — PNG, JPEG, WebP or PDF of any public page (`hermeskd/website-screenshot`) Actor

Page URLs or raw HTML in, a screenshot or PDF print of each out: full page or viewport, one element, desktop, tablet or mobile. Obeys robots.txt. Pay per capture.

- **URL**: https://apify.com/hermeskd/website-screenshot.md
- **Developed by:** [Logan Parker](https://apify.com/hermeskd) (community)
- **Categories:** SEO tools, Developer tools, Agents
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 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

Give it page URLs or raw HTML; it returns a screenshot of each — full page or viewport, a single element by CSS selector, desktop, tablet or mobile — or a PDF print of the page, as a file in the run's key-value store. It obeys robots.txt and refuses platforms whose terms forbid automated access. A vision-capable agent uses it to check how a page looks — its own deploy, a layout bug, a design reference — by rendering it to an image it can view, at one flat price per capture.

### Call it from an agent

Through Apify's MCP server (`https://mcp.apify.com`), tool `call-actor`:

```json
{"actor": "hermeskd/website-screenshot", "input": {"urls": ["https://docs.apify.com/platform/actors/publishing/monetize"], "format": "png"}, "callOptions": {"maxTotalChargeUsd": 0.05, "timeout": 600}, "waitSecs": 30}
```

The call returns the run's status and its `defaultDatasetId`. Read the records with `get-dataset-items`:

```json
{"datasetId": "<defaultDatasetId from the call>", "limit": 50}
```

Each record's `outputUrl` is the image or PDF itself (also readable with `get-key-value-store-record` and the record's `key`). As a dedicated tool, add `https://mcp.apify.com?tools=hermeskd/website-screenshot` to your MCP client. `maxTotalChargeUsd` caps what the run can charge; when it is reached the remaining inputs come back as `charge_limit_reached` records and are not charged.

### Input

| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
| `urls` | array of strings | one of | — | Page URLs, 1–50 per run. |
| `html` | string | one of | — | Raw HTML to render instead of URLs (≤ 2 MB). |
| `format` | enum `png` · `jpeg` · `webp` · `pdf` | no | `png` | `pdf` prints the page. |
| `fullPage` | boolean | no | `true` | Capture the whole scroll height. |
| `device` | enum `desktop` · `tablet` · `mobile` | no | `desktop` | Viewport preset (1280×800, 820×1180, 390×844). |
| `selector` | string | no | — | Capture only this element. |
| `waitForSelector` | string | no | — | Wait for this element before capture (≤ 15 s). |
| `delayMs` | integer | no | 0 | Extra wait, ≤ 10,000. |
| `darkMode` | boolean | no | `false` | Emulate a dark colour scheme. |

Give `urls` or `html`, not both. Every limit above is enforced by the input schema and again in code. Images are captured at a device scale factor of 1, so a desktop image is 1,280 pixels wide.

### Output

One record per input; the file is stored in the run's default key-value store.

| Field | Meaning |
|---|---|
| `url` | Input URL or `html:`. |
| `finalUrl` | The page's URL after redirects; `html:` for raw HTML. |
| `ok` | Success flag. |
| `status` | HTTP status. |
| `title` | The page's title. |
| `format` | `png`, `jpeg`, `webp` or `pdf`. |
| `width` | Image width in pixels; null for `pdf`. |
| `height` | Image height in pixels; null for `pdf`. |
| `pages` | Pages in the PDF print (`pdf` only). |
| `key` | Record key. |
| `outputUrl` | API record URL. |
| `warnings` | `height_capped` when a full page taller than 16,384 px (16,383 px in WebP, the format's limit) was cut to that height; `blocked_subresources` when the fetch policy refused something the page asked for (a frame or resource on a deny-listed host, a private address or a robots-disallowed path, a prefetch hint), which was not loaded. |
| `error` | `{code, message}`. |

Example record for the call above, as this Actor returned it in its deploy check on 2026-10-16 (the signature on `outputUrl`, a read grant for that one file, is shortened to `…` here):

```json
{
  "url": "https://docs.apify.com/platform/actors/publishing/monetize",
  "finalUrl": "https://docs.apify.com/actors/publishing/monetize",
  "ok": true,
  "status": 200,
  "title": "Monetize Actors | Platform | Apify Documentation",
  "format": "png",
  "width": 1280,
  "height": 4010,
  "key": "screenshot-000-f7f775a538.png",
  "outputUrl": "https://api.apify.com/v2/key-value-stores/kAhMa5nfRVExhhgNl/records/screenshot-000-f7f775a538.png?signature=…",
  "warnings": ["blocked_subresources"]
}
```

### Events and prices

Pay per event; platform usage is included in the price, never passed through.

| Event | Title | Charged when | FREE / BRONZE | SILVER | GOLD / PLATINUM / DIAMOND |
|---|---|---|---|---|---|
| `screenshot-captured` | Screenshot captured | One image captured. | $0.002 | $0.0018 | $0.0016 |
| `pdf-rendered` | Page printed to PDF | One page printed to PDF; charged instead of screenshot-captured. | $0.003 | $0.0027 | $0.0024 |

Apify also charges its standard `apify-actor-start` event: $0.00005 per GB of run memory (1 event, $0.00005, at the default 1,024 MB). The minimum maximum-charge a caller can set is $0.01.

Worked example on the FREE tier: screenshots of 10 pages cost 10 × $0.002 = $0.02, plus $0.00005 for the start, $0.02005 in all; printing the same 10 pages to PDF costs $0.03 plus the start. Nothing is charged for a failed capture.

### Limits and run time

- 1–50 URLs per run, or one HTML document up to 2 MB.
- Full-page captures stop at 16,384 px of height, 16,383 px in WebP (the format's limit); warning `height_capped`.
- Navigation waits up to 30 seconds for the page's own document. The capture is then taken once the page's fonts have loaded and its network has gone quiet (no request starting or finishing for half a second; a connection that merely stays open does not hold the capture), and never more than 5 seconds after the document: the page's `load` event is not waited for, and requests still in flight are left unfinished — so content a page draws later than that (a late ad, a chat bubble, an analytics widget) can be missing; to wait for a specific element use `waitForSelector`. With the desktop device the page's own timers are paused from the moment of capture (after `waitForSelector` and `delayMs`), so the image and the `title` show the page at the same instant. `waitForSelector` waits up to 15 seconds; `delayMs` adds up to 10 seconds. At most 2 page loads a second go to one host. A host's name is looked up once per run and given 10 seconds; each of its addresses is given 10 seconds to connect, 30 in all; a request past those limits fails like any other failed request.
- Default run memory 1,024 MB (from 1,024 to 4,096 MB): pages are captured one at a time at the default, one per 1,024 MB of run memory when a caller raises it (at most three at once); records keep the input order.
- Typical run time: a few seconds per page after the browser starts. The default run timeout is 600 seconds.

### Errors

Errors are records with `ok: false` and `error.code`; none is charged.

| `error.code` | Meaning |
|---|---|
| `download_failed` | Navigation failed. |
| `http_error` | Non-2xx status. |
| `selector_not_found` | `selector` or `waitForSelector` absent, or not valid CSS. |
| `robots_disallowed` | robots.txt forbids it. |
| `blocked_host` | Deny-listed host, or a private or local address. |
| `login_required` | Sign-in wall; not attempted. |
| `timeout` | Not finished within the run's time budget. |
| `internal_error` | Unexpected failure. |
| `charge_limit_reached` | The run reached its maximum total charge before this input; it was not processed. |
| `invalid_input` | The run input passed the input schema but cannot be used (for example both `urls` and `html`, or neither); the message names the field. One record per input given, or one with `url: null` when none was given; nothing is processed. |

The run fails only when every input failed with an Actor-side code (`internal_error`, `timeout`), so a broken Actor is visible. Every other run succeeds — including one where nothing was captured because of the caller's own inputs (bad URL, robots, deny-list, not found, invalid input, the charge limit), or where those and Actor-side failures are mixed.

### Sources and terms

Public pages the caller names, under the Hermes fetch policy. Process only pages you have the right to capture. robots.txt is obeyed for the user-agent `HermesBot` (and `*`): with `urls`, for the page and every frame in it, on every redirect hop and on any later navigation, before the browser requests it; with `html`, every URL in the caller's HTML is one the caller names, so every request the page makes — documents, frames, scripts, styles, images, fonts, data — is checked. A page whose own document is refused, at load or later, ends as `robots_disallowed` or `blocked_host` and is not charged; a refused frame or resource is left out and the record carries `blocked_subresources`. Popup windows, `target=_blank` links and forms never open a second page; prefetch and prerender hints are removed from caller HTML. Platforms whose terms forbid automated access, and people-search sites, are on a deny-list: they are refused as pages and are not loaded as embedded resources. Private and local addresses are refused for every request the browser makes, after DNS and on every redirect. The browser keeps its own user-agent string with `HermesBot/1.0 (+https://apify.com/hermeskd)` appended. No login, cookies, credentials, CAPTCHA solving, evasion or proxies are used.

### Not affiliated

This Actor is not affiliated with Apify's own screenshot Actors, with Google (Chromium) or with any site it renders. Chromium and Playwright are used under their open-source licences.

### Related Hermes Actors

- `lighthouse-audit` — Lighthouse performance, accessibility and SEO scores.
- `seo-page-audit` — on-page SEO checks.
- `domain-availability` — whether a domain name is registered.
- `dns-and-email-auth` — DNS records and email authentication (SPF, DKIM, DMARC).
- `broken-links` — broken links on a site.
- `sitemap-urls` — every URL in a site's sitemaps.

### Changelog

- 1.0 (2026-10-16) — input the Actor cannot use is answered with `invalid_input` records and the run succeeds; a run fails only when every input failed on the Actor's side.
- 1.0 (2026-10-14) — first release: PNG, JPEG, WebP and PDF; full page, viewport and element capture; three device presets; dark mode.

# Actor input Schema

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

Page URLs, 1–50 per run. Give urls or html.

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

Raw HTML to render instead of URLs (≤ 2 MB). Give urls or html.

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

png, jpeg or webp image; pdf prints the page.

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

Capture the whole scroll height (up to 16,384 px); false captures the viewport.

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

Viewport preset: desktop 1280×800, tablet 820×1180, mobile 390×844.

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

Capture only this element (CSS selector).

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

Wait for this element before capture (≤ 15 s).

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

Extra wait before capture, ≤ 10,000 ms.

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

Emulate a dark colour scheme.

## Actor input object example

```json
{
  "urls": [
    "https://docs.apify.com/platform/actors/publishing/monetize"
  ],
  "format": "png",
  "fullPage": true,
  "device": "desktop",
  "delayMs": 0,
  "darkMode": false
}
```

# Actor output Schema

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

One record per input with ok, title, format, width and height, the file key and its URL, or an error code.

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

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

# 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://docs.apify.com/platform/actors/publishing/monetize"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hermeskd/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": ["https://docs.apify.com/platform/actors/publishing/monetize"] }

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

```

## MCP server setup

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