# Website Screenshot API — Full Page, Mobile & PDF (`nefes-tools/website-screenshot`) Actor

Screenshot any URL: full-page or viewport, desktop, tablet or mobile, JPEG, PNG or PDF. Hides cookie banners, loads lazy images, captures single elements. Returns a public image link per URL. Pay only for successful screenshots.

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

## Pricing

from $8.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.

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, Mobile & PDF

**Turn any list of URLs into full-page screenshots (PNG, JPEG) or PDFs. Desktop, tablet or mobile. Cookie banners hidden. You pay only for screenshots that succeed.**

| You give | You get, per URL |
|---|---|
| `["apify.com", "https://example.com/pricing"]` | A public link to the image or PDF, plus page title, HTTP status, page height and file size — in one table you can export to CSV, Excel or JSON |

### What it's for

- **Visual monitoring:** schedule it daily and keep a visual history of your pages, competitors' pricing pages or landing pages.
- **Reports and audits:** attach screenshots of client websites to SEO, design or accessibility reports.
- **Mobile checks:** see how a page renders on a phone or tablet without owning one.
- **Archiving and evidence:** save a page as PDF or PNG with a timestamp (`capturedAt`).
- **AI agents:** give your agent eyes — call this Actor via the Apify API or MCP and pass `screenshotUrl` to a vision model.

### Why this one

- ✅ **Pay per successful screenshot.** Timeouts, 404s, blocked pages and invalid URLs cost you nothing. No surprise compute bills.
- ✅ **Full page, done right.** Scrolls the page first so lazy-loaded images actually appear, then captures the whole length.
- ✅ **Cookie banners hidden** (OneTrust, Cookiebot, Didomi, Usercentrics and custom ones) with CSS — nothing is clicked or accepted.
- ✅ **Desktop, Desktop HD, tablet and mobile** presets, custom viewport, dark mode.
- ✅ **PNG, JPEG or PDF.** Screenshot a single element with a CSS selector (e.g. `.pricing-table`).
- ✅ **Hide anything** you don't want in the picture: chat widgets, sticky headers, ads.
- ✅ **Parallel:** 4 pages at a time by default, up to 20.

### How to use

1. Paste your URLs (one per line — bare domains like `apify.com` work too).
2. Pick format and device. Defaults: JPEG, desktop 1280 px, full page.
3. Click **Start**. Open the **Screenshots** tab to see thumbnails, or download the table.

Via API (JavaScript):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<username>/website-screenshot').call({
    urls: ['https://apify.com', 'https://example.com'],
    device: 'mobile',
    format: 'jpeg',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => i.screenshotUrl));
```

### Output

One row per URL:

```json
{
    "url": "https://apify.com/",
    "finalUrl": "https://apify.com/",
    "httpStatus": 200,
    "title": "Apify: Marketplace of ready-to-run tools for AI",
    "screenshotUrl": "https://api.apify.com/v2/key-value-stores/.../records/apify-com-e99c9a65d70a.jpg",
    "format": "jpeg",
    "device": "desktop",
    "fullPage": true,
    "viewportWidth": 1280,
    "pageWidth": 1280,
    "pageHeight": 10036,
    "bytes": 312804,
    "captureMs": 262,
    "consentWall": false,
    "error": null,
    "capturedAt": "2026-09-23T14:03:11.402Z"
}
```

Failed pages get a row too, with `error` filled in (e.g. `page returned HTTP 404`) — and no charge.

Files are stored in the run's key-value store. They are kept according to your Apify data retention settings; download them if you need them longer.

### Pricing

**$8 per 1,000 successful screenshots** ($0.008 each) plus **$0.005 per run** for starting the browser. Only screenshots that were actually produced are charged.

Example: a daily run with 20 pages for a month = 600 screenshots + 30 runs ≈ **$4.95**.

Set **Maximum cost per run** in the run options and the Actor stops cleanly when it's reached.

### Input options

| Option | Default | What it does |
|---|---|---|
| `urls` | — | Pages to capture |
| `format` | `jpeg` | `jpeg`, `png` or `pdf` |
| `device` | `desktop` | `desktop` (1280×800), `desktop-hd` (1920×1080), `tablet` (820×1180), `mobile` (412×915) |
| `fullPage` | `true` | Whole page, or only the visible viewport |
| `scrollToBottom` | `true` | Scroll first so lazy images load |
| `hideCookieBanners` | `true` | Hide cookie/consent pop-ups with CSS |
| `delayMs` | `1000` | Extra wait before capture |
| `waitUntil` | `load` | `load`, `domcontentloaded` or `networkidle` |
| `waitForSelector` | — | Wait for an element before capturing |
| `elementSelector` | — | Capture just one element |
| `hideSelectors` | — | Elements to hide |
| `darkMode` | `false` | Request the dark color scheme |
| `viewportWidth` / `viewportHeight` | device | Custom size |
| `quality` | `80` | JPEG quality |
| `timeoutSecs` | `60` | Per-page timeout |
| `maxHeight` | `20000` | Cut full-page screenshots at this height (px) |
| `maxConcurrency` | `4` | Pages in parallel |

### Limitations — read before you buy

- **Public pages only.** No logins, no cookies you supply.
- **We respect website rules.** Pages disallowed by the site's `robots.txt` and pages that refuse automated access (HTTP 401/403/429) are reported as errors and **not charged**. The Actor never tries to get around CAPTCHAs or bot checks — if a site shows one instead of its content, that is what the screenshot shows.
- **"Accept or subscribe" walls are not removed.** Some publishers (e.g. many European news sites) require you to accept tracking or pay before showing content. That is a condition of access, so the Actor leaves it visible and sets `consentWall: true` so you can filter those rows.
- Full-page screenshots are cut at 20,000 px by default (`maxHeight`, up to 50,000). Endless-scroll feeds are captured as far as they have loaded.
- Videos are not loaded (they don't show in a still image anyway).

### FAQ

**Is there a free alternative?** Apify's own "Website Screenshot Generator" has no Actor fee, but you pay the compute time — which depends on how slow each page is, and failed or timed-out pages cost the same. Here the price is fixed upfront, failures are free, and you get mobile/tablet views, PDF, element screenshots and cookie-banner hiding.

**Can I use it from an AI agent?** Yes. It's a plain pay-per-event Actor with a small input, so it works through the Apify MCP server and API.

**Something doesn't look right?** Open an issue on the Actor's Issues tab with the URL and settings.

# Actor input Schema

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

Web pages to screenshot, one per line. Full URLs or bare domains (https:// is added). Each successful URL = one charged screenshot; failed pages are free.

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

JPEG (small, fast — best for most uses), PNG (lossless, much larger and slower on long pages) or PDF (the page as a document).

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

Desktop 1280×800, Desktop HD 1920×1080, Tablet 820×1180 or Mobile 412×915 (Android Chrome).

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

Capture the whole scrollable page. Turn off to capture only the visible viewport.

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

Scroll through the page before a full-page screenshot, so images that load on scroll are shown instead of empty placeholders. Adds a few seconds per page.

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

Hide cookie and consent pop-ups (OneTrust, Cookiebot, Didomi, Usercentrics, custom banners) with CSS. Nothing is clicked or accepted. 'Accept or subscribe' walls are not hidden — they are a condition of access; such pages are flagged with consentWall=true.

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

Wait this long after the page loads, so animations and lazy content can settle.

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

When the page counts as loaded. 'Network idle' is slower but safer for heavy pages.

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

Optional. Wait until this element appears before capturing, e.g. '#main-chart'.

## `elementSelector` (type: `string`):

Optional. Screenshot just one element instead of the page, e.g. 'header' or '.pricing-table'. Ignored for PDF.

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

Optional. Elements to hide before capturing, e.g. chat widgets or sticky headers.

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

Ask the page for its dark color scheme (works on sites that support prefers-color-scheme).

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

Optional. Overrides the device width.

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

Optional. Overrides the device height.

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

1–100. Only used for JPEG.

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

Give up on a page after this many seconds. Failed pages are not charged.

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

Full-page screenshots are cut at this height. Very tall pages (long articles, endless feeds) are slow and huge; 20,000 px covers almost every normal page.

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

How many pages to capture at once. Each page needs about 1 GB of run memory; the Actor slows down automatically if memory runs out.

## Actor input object example

```json
{
  "urls": [
    "https://www.apple.com/iphone/",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ],
  "format": "jpeg",
  "device": "desktop",
  "fullPage": true,
  "scrollToBottom": true,
  "hideCookieBanners": true,
  "delayMs": 1000,
  "waitUntil": "load",
  "darkMode": false,
  "quality": 80,
  "timeoutSecs": 60,
  "maxHeight": 20000,
  "maxConcurrency": 4
}
```

# Actor output Schema

## `screenshots` (type: `string`):

One row per URL: screenshotUrl, page title, HTTP status, size, error (failed pages are not charged).

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

The captured JPEG, PNG or PDF files.

# 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://www.apple.com/iphone/",
        "https://en.wikipedia.org/wiki/Web_scraping"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nefes-tools/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://www.apple.com/iphone/",
        "https://en.wikipedia.org/wiki/Web_scraping",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("nefes-tools/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://www.apple.com/iphone/",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ]
}' |
apify call nefes-tools/website-screenshot --silent --output-dataset

```

## MCP server setup

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