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

Full-page screenshots for a list of URLs, without cookie banners. Lazy images load, sticky headers show once, a height cap stops runaway pages, and a hard time limit per URL keeps runs from sticking. PNG, JPEG or WebP; desktop, tablet or mobile. Pay per screenshot, failed pages are free.

- **URL**: https://apify.com/accountable\_eel/website-screenshot.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (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 $2.50 / 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 API: Full Page, No Cookie Banners

Paste a list of URLs and get one clean **full page screenshot** per URL: the whole page from top to
bottom, **without cookie banners**, with lazy-loaded images actually loaded and sticky headers shown
once instead of stamped across the page. PNG, JPEG or WebP; desktop, tablet or mobile. Every image
lands in the run's key-value store with a public link in the results table.

You pay per screenshot. A page that fails to load, runs out of time or answers with an HTTP error
is free.

### Who it's for

- **Agencies and QA teams** capturing a client's pages for reports, audits, visual regression or
  before/after comparisons, **for a list of URLs** at once rather than one tab at a time.
- **SEO and marketing teams** archiving competitors' landing pages, pricing pages and homepages
  on a schedule, for a monthly "what changed" deck.
- **Developers and no-code builders** who need a screenshot step in an n8n, Make, Zapier or Clay
  flow, or an AI agent that has to "look" at a page.
- Anyone who needs a **screenshot url alternative** after getting a cut-off page, a cookie wall
  over the content, or a run that never finished: this actor is built around those three problems.

### Why this one

- **Real full-page captures.** Before the capture the page is scrolled to the bottom so lazy
  images and "load on scroll" sections render, pages whose content scrolls inside an inner box
  are expanded, and a 100vh hero doesn't balloon. The capture is exactly as tall as the page.
- **No cookie banners.** A curated list covers the consent tools most sites use (OneTrust,
  Cookiebot, Didomi, Usercentrics, TrustArc, Quantcast, Sourcepoint, CookieYes, Osano, iubenda,
  Complianz, Klaro, Google Funding Choices and 15+ more), plus a check for home-made cookie bars:
  a fixed bar or dialog that talks about cookies and has an Accept / Reject button. The banner, its
  grey backdrop and the scroll lock it sets are **hidden, never clicked**: no consent is given for
  you. Each row says whether a banner was hidden and which one.
- **Sticky headers and chat bubbles appear once.** Fixed and sticky elements are pinned to where
  they sit at the top of the page before the capture.
- **Height control.** A height limit (default 15,000 px) stops an endless feed from producing a
  giant image, and the row tells you when a page was cut (`truncated`).
- **Runs don't get stuck.** Every page has a hard time limit (default 45 s). A slow page comes
  back with whatever had rendered, free, and the run moves on.
- **Pay only for real screenshots.** Errors, time-outs and HTTP 4xx/5xx pages are free rows
  (the image is still attached so you can see what happened).

### What you get

One row per URL:

| Field | Type | Description |
| --- | --- | --- |
| `url` | text | The URL you passed in (https:// added when missing). |
| `found` | boolean | `true` when the page was captured and charged, `false` for a free failed row. |
| `finalUrl` | text | Where the page ended up after redirects. |
| `statusCode` | number | HTTP status of the page. |
| `title` | text | The page's title. |
| `screenshotUrl` | link | Public link to the image in the run's key-value store. |
| `screenshotKey` | text | The image's key in the key-value store. |
| `format` | text | `png`, `jpeg` or `webp`. |
| `viewport` | text | Screen size used, e.g. `desktop 1440x900`. |
| `width`, `height` | number | Image size in px, read from the saved file. |
| `pageHeight` | number | Full page height before the height limit. |
| `truncated` | boolean | `true` when the page was taller than the limit and was cut there. |
| `bytes` | number | Image file size. |
| `bannerHidden` | boolean | `true` when a visible cookie/consent banner was hidden. |
| `bannersHidden` | array | Which: `cmp:<selector>` for a known consent tool, `heuristic:<element>` for a home-made bar. |
| `note` | text | Anything worth knowing (cut at the limit, followed a bot check to the real page, inner scroll area expanded, bad selector ignored). |
| `error` | text | Why the page failed. **A row with an error is never charged.** |
| `capturedAt` | date | When the capture was taken. |

The **Screenshots** view in the dataset shows the images as thumbnails next to each URL.

### Price

**$5 per 1,000 screenshots** ($0.005 each) on the free Apify plan, down to **$2.50 per 1,000** on
paid plans, plus a tiny per-run start fee. Only captured pages are charged. A page that fails to
load, hits the time limit, answers with an HTTP error or never gets past a bot check costs nothing.
Set **Maximum cost per run** in the run options and the actor stops cleanly when it's reached.

### How to use

1. **In the Apify Console.** Open the actor, paste your URLs (one per line) and click **Start**.
   The results table fills as each page is captured; click a `screenshotUrl` to open the image.
2. **Via the API.** One POST, results back in the same call:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~website-screenshot/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"urls":["https://www.booking.com/","https://apify.com/"],"format":"jpeg"}'
   ```
3. **On a schedule.** Save your input as a Task and add a Schedule (daily, weekly) to archive the
   same pages over time.

### Input

```json
{
  "urls": ["https://www.booking.com/", "apify.com"],
  "viewport": "desktop",
  "fullPage": true,
  "maxHeight": 15000,
  "format": "png",
  "hideCookieBanners": true,
  "scrollToLoadLazy": true,
  "perUrlTimeoutSecs": 45
}
```

| Input | Default | What it does |
| --- | --- | --- |
| `urls` | | One URL per line. `example.com` works too. Duplicates are captured once. |
| `viewport` | `desktop` | `desktop` 1440x900, `tablet` 768x1024, `mobile` 390x844 (phone browser, so sites serve their mobile layout), or `custom`. |
| `viewportWidth`, `viewportHeight` | 1440, 900 | Window size when `viewport` is `custom`. |
| `fullPage` | `true` | Whole page, or just the window. |
| `maxHeight` | 15000 | Height limit in px for full-page captures. Maximum 16,384 (a browser can't paint a taller single image). |
| `format` | `png` | `png`, `jpeg` or `webp`. |
| `quality` | 80 | JPEG / WebP quality, 1–100. |
| `delaySeconds` | 0 | Extra wait after load, for animations or slow widgets. |
| `hideCookieBanners` | `true` | Hide consent banners (never clicks them). |
| `hideSelectors` | | Your own CSS selectors to hide, e.g. `.newsletter-popup`. |
| `scrollToLoadLazy` | `true` | Scroll down first so lazy images and sections load. |
| `perUrlTimeoutSecs` | 45 | Hard time limit per page, 10–300 s. |
| `maxConcurrency` | 2 | Pages captured in parallel. |

### Sample output

```json
{
  "url": "https://www.booking.com/",
  "found": true,
  "finalUrl": "https://www.booking.com/",
  "statusCode": 200,
  "title": "Booking.com | Official site | The best hotels, flights, car rentals & accommodations",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<store id>/records/001-www-booking-com.png",
  "screenshotKey": "001-www-booking-com.png",
  "format": "png",
  "viewport": "desktop 1440x900",
  "width": 1440,
  "height": 3013,
  "pageHeight": 3013,
  "truncated": false,
  "bytes": 1301117,
  "bannerHidden": true,
  "bannersHidden": ["cmp:#onetrust-banner-sdk"],
  "note": null,
  "error": null,
  "capturedAt": "2026-09-25T19:35:41.241Z"
}
```

A failed page, which is free:

```json
{
  "url": "https://example.com/blocked",
  "found": false,
  "statusCode": 403,
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<store id>/records/002-example-com-blocked.png",
  "bannerHidden": false,
  "error": "Page answered HTTP 403; screenshot attached, not charged."
}
```

### Use it from n8n, Make, Zapier, Clay or an AI agent

- **n8n / Make / Zapier:** use the Apify integration's "Run actor and get dataset items" with
  `accountable_eel/website-screenshot` and `{"urls": [...]}`. Each item carries `screenshotUrl`,
  ready to drop into Slack, Google Drive or a Google Sheet `IMAGE()` cell.
- **AI agents (Apify MCP server):** the agent passes a list of URLs and gets back image links plus
  titles and status codes, one small row per page.
- A single URL sent as a plain string instead of a list is accepted.

### Tips

- **Smaller files:** `format: "jpeg"` with `quality: 70` is usually 5–10 times smaller than PNG
  for photo-heavy pages.
- **A popup that isn't a cookie banner** (newsletter, app promo, sign-in nudge): add its selector
  to `hideSelectors`.
- **Mobile layouts:** `viewport: "mobile"` sends a phone browser identity and touch support, so
  sites serve their real mobile page, not the desktop page squeezed narrow.
- **Very long pages** (feeds, long articles): lower `maxHeight` to what you actually need; the
  capture is faster and the file smaller.

### vs. alternatives

The most common complaints about basic screenshot actors, and what this one does instead:

| Complaint | This actor |
| --- | --- |
| "Full page" comes out cut off | Scrolls to load lazy content, expands inner scroll areas, captures the page's real height |
| Cookie banner covers the content | Hidden (30+ consent tools and home-made bars), never clicked |
| Sticky header repeated down the image | Fixed and sticky elements pinned so they appear once |
| No control over height | `maxHeight`, plus a `truncated` flag and the real `pageHeight` |
| Runs stuck on one slow page | Hard time limit per page; the slow page is free and the run moves on |
| Paying for failures | Errors, time-outs, HTTP 4xx/5xx pages and uncleared bot checks are free |

### FAQ

**Does hiding the cookie banner accept cookies?**
No. Nothing on the page is clicked. The banner is hidden with CSS, so the site sees an
ordinary first visit that never answered its consent question.

**Which cookie banners are covered?**
OneTrust, Cookiebot, Didomi, Usercentrics, TrustArc, Quantcast Choice, Sourcepoint, CookieYes,
Osano, Termly, Complianz, Borlabs, iubenda, Klaro, CookieScript, Axeptio, tarteaucitron, Google
Funding Choices, Commanders Act, Ketch, consentmanager, Civic Cookie Control, HubSpot, Shopify,
Cookie Consent, Cookie Notice, GDPR Cookie Compliance, Evidon, CookieHub and Amazon's own banner,
plus home-made cookie bars in English and major European languages. Sites that redirect to a
separate consent page before showing any content (some Google and Yahoo properties) can't be
handled by hiding.

**Where are the images stored, and for how long?**
In the run's default key-value store. `screenshotUrl` is a direct public link. Apify keeps a
run's storage for your plan's data-retention period (7 days on the free plan). Copy the images
elsewhere (Google Drive, S3) if you need them longer.

**Why is my screenshot cut at 15,000 px?**
That's the default `maxHeight`, so an endless feed can't produce a gigantic file. Raise it up to
16,384 px. The row's `pageHeight` tells you how tall the page really was.

**Will it get past bot protection?**
Pages are opened in a real Chrome browser, which passes most sites. When a site shows a
"checking your browser" page first, the actor waits for it to hand over to the real page and
captures that (the row's `note` says so). A site that blocks cloud servers outright, or a check
that never clears, gives a row with the HTTP status and an `error`, and that row is free.

**Can I log in first or screenshot a page behind a password?**
Not in this version.

**What happens when a page is slow?**
Each page has a hard limit (`perUrlTimeoutSecs`, default 45 s). If the page hasn't finished
loading by then, you get what had rendered, marked with an `error`, free of charge, and the run
carries on with the next URL.

### Related actors

- `accountable_eel/jsonld-structured-data-lookup`: structured data (JSON-LD) from a list of URLs.
- `accountable_eel/dns-record-lookup`: DNS records for a list of domains.

# Actor input Schema

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

One URL per line. "example.com" works too (https:// is added). You're charged only for a page that was captured — a page that fails to load, times out or answers with an HTTP error is free.

## `viewport` (type: `string`):

Desktop is 1440x900, tablet 768x1024, mobile 390x844 (with a phone browser identity, so sites serve their mobile layout). Pick "Custom" to set your own width and height below.

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

Used only when Screen size is Custom. 320 to 3840.

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

Used only when Screen size is Custom. This is the window height; with Full page on, the image is as tall as the page. 320 to 2400.

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

On: capture the whole page from top to bottom (up to the height limit below). Off: capture only what fits in the window.

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

A full-page capture stops at this height, so an endless feed can't produce a giant image. The row says when a page was cut (truncated). Browsers can't paint a single image taller than 16384 px, so that is the ceiling.

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

PNG is sharp and lossless. JPEG and WebP are 3 to 10 times smaller (WebP images can't be taller than 16383 px).

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

1 to 100. Ignored for PNG.

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

Hides consent pop-ups from OneTrust, Cookiebot, Didomi, Usercentrics, TrustArc, Quantcast, Sourcepoint and 20+ other consent tools, plus home-made cookie bars. It only hides them: nothing is clicked, so no consent is ever given for you.

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

Optional. One CSS selector per line, e.g. ".newsletter-popup" or "#chat-widget". Matching elements are hidden before the capture.

## `scrollToLoadLazy` (type: `boolean`):

Scrolls down the page before the capture so images and sections that load on scroll are in the screenshot, instead of grey boxes.

## `delaySeconds` (type: `integer`):

Waits this long after the page has loaded, for animations or slow widgets. 0 to 30.

## `perUrlTimeoutSecs` (type: `integer`):

Hard stop for each page, so one slow site can't hold up the run. A page that doesn't finish loading in time comes back with whatever had rendered, free of charge. 10 to 300.

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

How many pages to capture in parallel. More is faster but needs more memory per run.

## Actor input object example

```json
{
  "urls": [
    "https://www.booking.com/",
    "https://apify.com/"
  ],
  "viewport": "desktop",
  "viewportWidth": 1440,
  "viewportHeight": 900,
  "fullPage": true,
  "maxHeight": 15000,
  "format": "png",
  "quality": 80,
  "hideCookieBanners": true,
  "hideSelectors": [],
  "scrollToLoadLazy": true,
  "delaySeconds": 0,
  "perUrlTimeoutSecs": 45,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

No description

## `screenshots` (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": [
        "https://www.booking.com/",
        "https://apify.com/"
    ],
    "hideSelectors": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/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.booking.com/",
        "https://apify.com/",
    ],
    "hideSelectors": [],
}

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

```

## MCP server setup

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