# Website Screenshot API: Full Page, PNG & PDF (`fetchgrove/website-screenshot`) Actor

Capture any public web page as a screenshot or PDF in a real browser: visible area or full page, PNG, JPEG or WebP, up to 3840 px wide, retina, dark mode, cookie banners hidden, Chinese, Japanese and emoji text rendered. You pay only for files you receive; failed pages are free.

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

## Pricing

from $0.56 / 1,000 viewport 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 API: Full Page, PNG & PDF

Turn a list of web pages into screenshots or PDFs in a real browser, with a download link for every file. Viewport or full page, PNG, JPEG or WebP, any screen size, dark mode, and cookie banners hidden.

### What you get

For each URL: an image or PDF file in your run's storage, plus one row with the page title, final URL after redirects, HTTP status, image size in pixels, file size and a link to the file.

### Use it for

- **SEO and ad checks**: see what a page looked like on a given day.
- **Archiving and evidence**: keep dated captures of pages you care about.
- **Thumbnails and previews**: generate images for link cards and galleries.
- **AI agents**: give a model a picture of the page, not just the text.

### How to use it

1. Add one or more URLs.
2. Pick **Viewport** (what fits on screen), **Full page** (the whole scrolling page) or **PDF**.
3. Optional: image format, width and height (320 to 3840 by 320 to 2160), pixel density (1x to 3x), wait condition, extra delay, dark mode.

Leave the input empty and it captures one example page.

### Capture exactly what you need

- **One element**: give a CSS selector and get just that part of the page, even if it is below the fold.
- **An area**: give x, y, width and height measured from the top of the page.
- **Hide parts**: list selectors to hide (chat widgets, banners) without shifting the layout.
- **Wait for content**: wait for a selector to appear before capturing.
- **Phones and tablets**: `iphone-15`, `pixel-8`, `ipad` or `desktop`, with the device's screen size and pixel density.
- **Clean full pages**: fixed and sticky headers stay at the top instead of repeating down the page.

Element and area captures up to one window high cost the viewport price; taller ones cost the full-page price.

### Evidence record (optional)

Turn on **Include evidence** to get, with each file, its SHA-256 hash, the capture time in UTC, the final URL, every redirect on the way, the server's response headers, the browser identity and window size, and the page title. Add **Save HTML** to keep the page's HTML next to the image. Useful for compliance archives and disputes. No extra charge.

### HTML to image and PDF

Send your own HTML instead of URLs and get a PNG, JPEG, WebP or PDF made by the same real browser: invoices, receipts, order confirmations, certificates, social share cards and reports.

- **One document or a list**: up to 50 per run, 5 MB each. Leave URLs empty when you send HTML.
- **Your assets load**: set a base URL and relative images, styles and fonts load from it. Web fonts and scripts work, so charts drawn by script are captured (use Wait for element if they need time).
- **Print-ready PDFs**: A4, Letter, Legal, A3 or A5, portrait or landscape, your margins, backgrounds on or off. Your print styles and page breaks are respected; a size or margin set in your CSS wins.
- **Social cards at the exact size**: set the window to 1200 x 630 and get a 1200 x 630 image (2400 x 1260 at 2x).
- **Same price as a screenshot**: a visible-area image costs the viewport price; full page and PDF cost the full-page price. Empty, failed and blank documents are free.
- **Private by design**: your HTML is never written to the run log, each result carries only a SHA-256 hash of its document, and the HTML is kept as a file only if you turn on Save page HTML. Anything the document loads comes from the public internet only. File links work for anyone who has them, so treat them like the documents.

### Output example

Real output:

```json
{
  "url": "https://www.gov.uk/",
  "finalUrl": "https://www.gov.uk/",
  "httpStatus": 200,
  "output": "viewport",
  "format": "png",
  "width": 1280,
  "height": 800,
  "bytes": 59217,
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<your-store>/records/screenshot-<id>-2.png?signature=<...>"
}
```

### Pricing

You pay only for files you receive. Pages that fail to load, return an error status or show a "verify you are human" check cost nothing and are marked in the results.

| What | Price |
|---|---|
| Viewport screenshot | $0.0008 each ($0.80 per 1,000) |
| Full-page screenshot or PDF | $0.003 each ($3 per 1,000) |
| Run start | $0.00005 per GB of run memory, at least one (at the default 2 GB: $0.0001 per run) |

Free Apify plan: up to 10 URLs per run.

### Good to know

- **Cookie banners** from the common consent tools are hidden before capture; nothing is ever clicked or accepted for you.
- **Sites that block automated visitors** are reported as failed, never worked around.
- **Languages**: Chinese, Japanese, Korean and emoji render correctly.
- **Very long pages** are captured up to 16,383 pixels tall.
- **Lazy-loaded images** are scrolled into view before a full-page capture.

### Support

Open an issue on this page. We reply within two business days.

# Actor input Schema

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

Public web pages to capture, one per line. A missing https:// is added for you. Addresses with a user name or password ("name:password@") are refused: logging in is not supported. Only the pages listed are opened; links are never followed. Each site's robots.txt is respected: a page it closes to automated visitors (or a redirect to one) gets an error row and is not charged. To render your own HTML instead, leave this list empty and use "HTML documents" below (one run takes URLs or HTML, not both). If both are empty, the run captures https://example.com.

## `output` (type: `string`):

Visible area: what fits in the window size below. Full page: the whole scrollable page, up to 16,383 pixels tall; a page so long or heavy that its screenshot cannot be drawn within 60 seconds gets an error row and is not charged. PDF: web pages print on pages the size of the window, HTML documents on A4; choose a paper size, orientation and margins under "PDF pages".

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

File format for screenshots. Ignored for PDF.

## `width` (type: `integer`):

Width of the browser window in CSS pixels (320 to 3840).

## `height` (type: `integer`):

Height of the browser window in CSS pixels (320 to 2160).

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

Image pixels per CSS pixel (1 to 3). Use 2 for sharp, retina-style screenshots; the image is then twice as wide and tall.

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

Capture the page as a phone or tablet shows it: that device's screen size, pixel density, mobile layout, touch input and browser identity. A device replaces the window width, height and pixel density above. iPhone 15: 393 x 659 at 3x. Pixel 8: 412 x 839 at 2.625x. iPad: 834 x 1194 at 2x (iPad Pro 11 inch, portrait).

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

When the page counts as loaded. Page loaded: all images and styles are in. Page ready: the HTML is ready, images may still be loading. Network quiet: no requests for half a second, best for pages that load content after they open. Each page gets 45 seconds; a page whose HTML is in but that never finishes loading (an image or request that never arrives) is captured as it is when the time is up, and its row says so. A page still busy well past its time (the 45 seconds, the waits and extra wait you set and, for full pages, elements, areas and PDFs, the scroll and drawing time, added up, plus 30 seconds; never more than 3 minutes) is stopped, for example when a script on it never stops: error row, not charged.

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

Extra time to wait after loading, before capturing (0 to 10,000 ms). Helps with animations and late content.

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

Web pages only: hide common cookie consent banners before capturing. Banners are only hidden, never accepted; a page a banner locked from scrolling is unlocked for full-page, PDF, element and area captures. HTML documents are captured as written.

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

Ask the page for its dark colour scheme, on sites that have one.

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

One HTML document as text, or a list of documents (each item is one complete document). From the API, send a string or a list of strings; in this form, write it as JSON, for example \["<h1>Invoice 1001</h1>"]. Scripts in the document run, so charts and templates drawn by script work (use "Wait for element" or "Extra wait" if they need time). Up to 50 documents per run and 5 MB each; an empty or oversized document gets an error row and is not charged. Anything in this field makes the run an HTML run, even blank text (one error row, not charged); leave it out to capture web pages. Leave "URLs" empty: one run takes URLs or HTML, not both. Each document gets 45 seconds to load, including waits; one still busy well past its time (at most 3 minutes) is stopped and not charged. Everything the document loads is fetched from the public internet only: private and local network addresses are blocked, and frames are checked against their site's robots.txt. Your HTML is never written to the run log; it is stored only in the run's own storage, and kept as a file only if you turn on "Save page HTML".

## `baseUrl` (type: `string`):

Where relative addresses in the HTML point, such as https://example.com/assets/. Images, styles and fonts written as logo.png or /css/invoice.css are then loaded from there. Leave empty when the HTML uses only full addresses or embedded images. A document with its own <base href> keeps it. No user name or password in it. HTML documents only.

## `css` (type: `string`):

A stylesheet added to every HTML document after its own styles, before it is captured, such as body { font-family: Georgia, serif } or .draft-stamp { display: none }. HTML documents only (for web pages, use "Hide elements").

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

Paper size for PDFs. Empty: HTML documents print on A4 (or the size the document sets with a CSS @page rule) with their print styles, as a browser's Save as PDF does; web pages print on pages the size of the window and look as they do on screen. Choosing a size here prints either on that paper.

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

Turn the paper sideways. For PDFs on paper: every HTML document, and web pages when a paper size is chosen above (pages the size of the window follow the window instead).

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

PDF page margins, written like a CSS margin: one to four lengths in mm, cm, in or px. 10mm: all sides. 15mm 10mm: top and bottom, then left and right. 1in 0.5in 1in 0.5in: top, right, bottom, left. At least 1 inch of the page must be left between them. Empty: no margin. A document that sets its own margins with a CSS @page rule keeps them.

## `printBackground` (type: `boolean`):

Include background colours and images in PDFs. Turn off for a lighter, printer-friendly file.

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

CSS selector, such as #pricing or main h1. Only the first matching element is captured, all of it, even when it is taller than the window. It is waited for for up to 45 seconds after the page loads; if it does not appear, or it is inside an element you hide below, that URL gets an error row and is not charged. Pseudo-elements such as ::before cannot be captured. Charged as a visible-area screenshot when the captured element is at most one window high (the window height above, or the device's); a taller element is charged as a full-page screenshot. Works with screenshots, not PDF.

## `clip` (type: `object`):

An area of the page in CSS pixels, measured from the top-left corner of the page (not the window): {"x": 0, "y": 0, "width": 800, "height": 600}. x and y from 0 to 100,000, width from 1 to 3,840, height from 1 to 16,383; at pixel density above 1 the image must also stay within 16,383 pixels tall and 64 million pixels. Any part beyond the edge of the page is left out (the row then says truncated); an area that starts outside the page gives an error row and is not charged. Charged as a visible-area screenshot when the captured part is at most one window high (the window height above, or the device's); a taller area is charged as a full-page screenshot. Not with "Capture one element", not with PDF.

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

CSS selectors of elements to hide before capturing, one per line, such as chat buttons, ads or a newsletter popup. Hidden elements keep their space, so nothing else moves, and everything inside them is hidden too. Up to 50.

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

CSS selector to wait for after the page loads: the capture starts once a matching element is visible. Waits up to 45 seconds; if it never appears, that URL gets an error row and is not charged. Pseudo-elements such as ::before cannot be waited for. The extra wait above runs after this.

## `fixStickyHeaders` (type: `boolean`):

For full-page screenshots and PDFs: headers, bars and buttons that stay on screen while you scroll are put back in their place in the page, so they appear once instead of covering content or repeating on every PDF page. Elements you hide, and ones nobody can see, are left alone. The page layout can shift slightly where they were.

## `includeEvidence` (type: `boolean`):

Add an evidence record to each saved screenshot or PDF: the SHA-256 hash of the file, the capture time (UTC), the browser identity, the window size and the page title; for a web page also the requested and final URL, every redirect with its status code and the page's response headers (cookies left out); for an HTML document the SHA-256 hash of the HTML as you sent it and the base URL. No extra charge.

## `saveHtml` (type: `boolean`):

With the evidence record on: also save the HTML next to the screenshot, linked from the record. For a web page, the HTML as the browser received it, decoded (compression removed, so not byte for byte what the server sent), with its own SHA-256 hash; a JSON page is saved as .json. For an HTML document, the document exactly as you sent it. Saved only in the run's own storage. No extra charge.

## Actor input object example

```json
{
  "urls": [
    "https://example.com"
  ],
  "output": "viewport",
  "format": "png",
  "width": 1280,
  "height": 800,
  "deviceScaleFactor": 1,
  "device": "desktop",
  "waitUntil": "load",
  "delayMs": 0,
  "hideCookieBanners": true,
  "darkMode": false,
  "pdfLandscape": false,
  "printBackground": true,
  "fixStickyHeaders": false,
  "includeEvidence": false,
  "saveHtml": false
}
```

# Actor output Schema

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

One row per URL or HTML document with a link to its file.

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

The image and PDF files saved by the run.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchgrove/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://example.com"] }

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

```

## MCP server setup

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