# Website Screenshot API - Full Page, No Cookie Banners (`apisight/website-screenshot-api`) Actor

Bulk screenshots of any URL: full page, single element or viewport, six device presets, PNG or JPEG. Removes cookie banners and reports whether it actually worked, so you can trust the image. Blocked, unreachable and mis-targeted pages are never charged.

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

## Pricing

from $20.00 / 1,000 website screenshot api - full page, no cookie banners

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, and no cookie banners

Capture clean screenshots of any URL, in bulk. Built for the two things that ruin automated
screenshots: **cookie-consent overlays** and **lazy-loaded images that never render**.

You are charged **only for screenshots that were actually produced**. Blocked, unreachable
and mis-targeted pages are returned with a reason and cost you nothing.

***

### Why this one

Most screenshot tools hand you a photograph of a consent dialog. This one removes it, and
tells you honestly whether it succeeded.

| | This Actor |
|---|---|
| **Cookie banners** | Dismisses 26 known consent platforms (OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast, Sourcepoint, TrustArc, Complianz, Borlabs, CookieYes, Klaro, Iubenda, Axeptio, …) plus a structural detector that finds unbranded consent modals by shape and wording, in 12 languages |
| **Honest reporting** | Every record says whether a banner was `detected`, how it was `handled`, and whether it was `stillVisible` afterwards — so you can tell "this site had no banner" from "the banner is still in your image" |
| **Lazy-loaded images** | Scrolls the page before a full-page capture so images actually render instead of leaving blank placeholders |
| **Ads and trackers** | Blocked by default: cleaner images, and ad-heavy pages load much faster |
| **Devices** | Six presets from 1920×1080 desktop to 360×640 mobile, with correct pixel density, touch support and mobile user agent |
| **Bot protection** | Detected and reported as `blocked` rather than silently returning a screenshot of a challenge page — and never charged |
| **Very long pages** | Clipped at 20,000 px and flagged `truncated` instead of producing an unusable file |

#### Measured, not claimed

On a 12-site European sample (news, retail, grocery, travel), the current build detected a
consent overlay on 5 sites, removed it on **5 of 5**, left **0** still visible, and
correctly classified 1 bot-protected site as `blocked` without billing for it. One site's
dialog carried no cookie-ish markup at all — it was found by shape and wording instead.

***

### Input

Minimum:

```json
{ "startUrls": ["apify.com", "https://stripe.com/pricing"] }
```

Common options:

```json
{
  "startUrls": ["bbc.com", "lidl.nl"],
  "device": "mobile",
  "fullPage": true,
  "format": "jpeg",
  "quality": 85,
  "dismissConsent": true,
  "blockAds": true,
  "selector": "",
  "hideSelectors": [".sticky-header", "#chat-widget"],
  "darkMode": false,
  "waitUntil": "load",
  "delayMs": 0,
  "lazyLoadMs": 3000,
  "timeoutSecs": 30,
  "maxConcurrency": 4
}
```

| Option | What it does |
|---|---|
| `device` | `desktop` (1920×1080), `desktop-small`, `laptop` (retina), `tablet`, `mobile` (390×844 @3x), `mobile-small` |
| `fullPage` | Whole scrollable page (default) or just the viewport |
| `selector` | Capture one element, e.g. `.pricing-table`. Not found → reported as an error, not charged |
| `hideSelectors` | Hide your own list of elements (sticky headers, chat widgets) before capture |
| `waitUntil` | `load` (default), `domcontentloaded` (fastest), `networkidle` (most complete, slowest) |
| `lazyLoadMs` | Scroll budget for triggering lazy images. `0` disables it |
| `proxyConfiguration` | Apify Proxy, on by default — many sites block shared cloud IPs, and blocked pages earn you nothing |

### Output

One dataset record per requested page:

```json
{
  "inputUrl": "lidl.nl",
  "finalUrl": "https://www.lidl.nl/",
  "status": "ok",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/…/records/0001-www.lidl.nl.jpg",
  "format": "jpeg",
  "widthPx": 1366,
  "heightPx": 7480,
  "fileSizeBytes": 412233,
  "device": "desktop-small",
  "fullPage": true,
  "pageTitle": "Lidl.nl",
  "loadTimeMs": 6120,
  "consent": { "detected": true, "handled": true, "method": "cmp-click",
               "cmp": "OneTrust", "stillVisible": false },
  "truncated": false,
  "capturedAt": "2026-10-01T20:14:07+00:00"
}
```

`screenshotUrl` is a direct link to the image — open it in a browser or fetch it with any
HTTP client. Three dataset views ship: **Overview**, **Cookie consent**, and **Problems**.

#### Statuses, and what you pay for

| Status | Meaning | Charged |
|---|---|---|
| `ok` | A screenshot was produced and stored | **yes** |
| `blocked` | Bot protection served an interstitial instead of the page | no |
| `error` | Unreachable, nothing painted before timeout, or the selector was absent | no |

### Limits and honest caveats

- **Consent dismissal is best-effort.** Sites that put consent behind a full-page gate
  (some German publishers' "pay or accept" walls) are not bypassed — that is a paywall
  decision, not a cookie banner, and the record will show `detected: true` with
  `stillVisible: true` so you know.
- Pages taller than **20,000 px** are clipped and flagged `truncated`.
- Full-page screenshots of infinite-scroll feeds capture what has loaded within the
  lazy-load budget, not an unbounded feed.
- Heavily protected sites may be `blocked` even through a proxy. You are not charged.
- PNG files of long pages get large; use `jpeg` with `quality` for bulk work.

### Tips

- Bulk auditing a site list? Use `desktop-small` + `jpeg` at `quality: 70` — much smaller
  files, nearly identical legibility.
- Comparing layouts across breakpoints? Run the same list once per `device`.
- Capturing a single component? `selector` beats cropping afterwards.
- A page that captures too early usually needs `waitUntil: "networkidle"` rather than a
  bigger `delayMs`.

# Actor input Schema

## `startUrls` (type: `array`):

URLs or bare domains to screenshot. Both "example.com" and "https://example.com/pricing" work. Up to 1,000 per run.

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

Viewport and pixel density to emulate. Mobile presets also set touch support and a mobile user agent, so responsive layouts render the way real visitors see them.

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

Capture the entire scrollable page instead of just the visible viewport. Pages taller than 20,000 px are clipped and flagged with 'truncated' rather than producing an unusable file.

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

PNG is lossless and best for text and UI. JPEG is far smaller for photo-heavy pages and honours the quality setting.

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

1-100. Applies to JPEG only; ignored for PNG.

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

Screenshot just this element instead of the page, e.g. ".pricing-table" or "#hero". Leave empty to capture the page. If the selector is not found the target is reported as an error and is not charged.

## `dismissConsent` (type: `boolean`):

Accepts or hides cookie-consent dialogs before capturing. Recognises OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast, Sourcepoint, TrustArc, Complianz, Borlabs, CookieYes and ~15 more, with a generic fallback in 12 languages. Without this, screenshots of EU sites are usually just a photo of a consent overlay.

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

Drops requests to known ad, analytics and tracker hosts. Produces cleaner images and loads ad-heavy pages substantially faster.

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

Extra elements to hide before capture, e.g. a sticky header or a chat widget.

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

Emulates prefers-color-scheme: dark.

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

How long to wait before capturing. 'load' suits most pages; 'networkidle' is better for heavy single-page apps but slower; 'domcontentloaded' is fastest.

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

Additional wait in milliseconds after the page is ready. Useful for intro animations.

## `lazyLoadMs` (type: `integer`):

Time budget for scrolling the page to trigger lazy-loaded images before a full-page capture. Set to 0 to skip. Without it, full-page shots of modern sites contain blank image placeholders.

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

Seconds to wait for a page before giving up. A page that times out but has already painted is still captured.

## `locale` (type: `string`):

Browser locale, e.g. "en-US", "de-DE", "nl-NL". Affects language negotiation and date formats.

## `timezone` (type: `string`):

IANA timezone, e.g. "Europe/Amsterdam". Leave empty for the default.

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

Overrides the device preset's user agent. Leave empty unless you have a specific reason.

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

How many pages to capture at once. Browser pages are memory-hungry; raise the Actor's memory if you raise this.

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

Routes the browser through Apify Proxy. Strongly recommended: many sites block shared cloud IPs, and blocked targets are never charged, so proxying yields more usable screenshots for the same spend.

## Actor input object example

```json
{
  "startUrls": [
    "apify.com",
    "stripe.com",
    "bbc.com"
  ],
  "device": "desktop",
  "fullPage": true,
  "format": "png",
  "quality": 85,
  "dismissConsent": true,
  "blockAds": true,
  "hideSelectors": [],
  "darkMode": false,
  "waitUntil": "load",
  "delayMs": 0,
  "lazyLoadMs": 3000,
  "timeoutSecs": 30,
  "locale": "en-US",
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All capture results: direct image URL, format, pixel dimensions, file size, page title, load time, which cookie-consent platform was dismissed, and whether a very tall page was clipped.

# 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 = {
    "startUrls": [
        "apify.com",
        "stripe.com",
        "bbc.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("apisight/website-screenshot-api").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 = {
    "startUrls": [
        "apify.com",
        "stripe.com",
        "bbc.com",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("apisight/website-screenshot-api").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 '{
  "startUrls": [
    "apify.com",
    "stripe.com",
    "bbc.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call apisight/website-screenshot-api --silent --output-dataset

```

## MCP server setup

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

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/8Dc3nvEs45LuOL449/builds/YxY66z9spEIXFwwKE/openapi.json
