# Website Screenshot API (`evermore_lancet_yhd/website-screenshot-api`) Actor

Screenshot any list of URLs as PNG, JPEG or WebP: desktop, mobile, tablet or custom size, full page or a single element. Hides cookie banners and ads. Pay only for successful screenshots.

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

## Pricing

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

Take screenshots of any list of web pages: desktop, mobile, tablet or a custom size; the visible
viewport, the full scrolling page, or a single element. Get PNG, JPEG or WebP images with cookie
banners and ads removed. **You pay only for screenshots that succeed. Failed URLs cost nothing.**

### What it does

For each URL in `urls`, the Actor:

1. Opens the page in a real Chromium browser that emulates the device you picked.
2. Waits until the page is loaded (`waitUntil`), without hanging on sites that never stop loading.
3. Removes cookie and consent popups and ads, and loads lazy images if you want the full page.
4. Saves the image to the run's **key-value store**.
5. Adds **one dataset item per URL** with the image link, page title, image size and an
   `ok`/`failed` status plus an error message when something went wrong.

One bad URL never stops the run. Temporary errors (timeouts, network errors, HTTP 429/5xx) are
retried with exponential backoff, and every attempt has a hard time limit.

### Why use this instead of other screenshot Actors

It was built to fix the most common complaints in reviews and issues of other screenshot tools:

| Common complaint                                                         | What this Actor does                                                                                 |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| "It still used up my credits" when the result didn't work                | Pay per successful screenshot. Failed, skipped and 404 URLs are never charged.                       |
| Slow and expensive (minutes and cents per screenshot)                    | A fixed, low price per screenshot. One shared browser, pages captured in parallel, ads blocked.      |
| Full page only shows the part above the fold; missing lazy-loaded images | Real full-page capture in one pass, after scrolling through the page so lazy images load.            |
| Endless scrolling makes runs take forever                                | Lazy-load scrolling is limited to 10 seconds and `maxFullPageHeight`.                                |
| Cookie banners cover the content                                         | Banners are dismissed or hidden by default, and the scroll lock they leave behind is removed.        |
| Sticky headers repeated across the image                                 | No stitching, so a sticky header appears once, at the top. Hide it completely with `hideSelectors`.  |
| Can't set the image height / can't capture only the top or one part      | `custom` device with any width/height, or `selector` to capture one element.                         |
| Runs time out or hang forever                                            | Per-attempt timeout, and a "soft" wait: if a page never reaches `networkidle`, it's captured anyway. |
| Out-of-memory crashes and frozen runs                                    | Parallelism is automatically limited to what the run's memory and CPU can handle.                    |
| Emoji and Asian text render as boxes                                     | Emoji and CJK fonts are installed.                                                                   |
| Certificate errors (`ERR_CERT_AUTHORITY_INVALID`)                        | Pages with invalid or self-signed certificates are still captured.                                   |
| No dataset output, hard to use from the API                              | Every URL produces one dataset item, so `run-sync-get-dataset-items` returns everything you need.    |

### Input example

Only `urls` is required. Everything else is optional.

```json
{
    "urls": ["https://apify.com", "news.ycombinator.com", "https://example.com/missing-page"],
    "device": "mobile",
    "fullPage": true,
    "format": "webp",
    "quality": 80,
    "waitUntil": "load",
    "extraDelayMs": 500,
    "hideCookieBanners": true,
    "blockAds": true,
    "darkMode": false,
    "concurrency": 5
}
```

#### All input fields

| Field                     | Type                                          | Default    | Description                                                                                                   |
| ------------------------- | --------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------- |
| `urls`                    | string\[]                                      | (required) | Pages to capture. `https://` is added if missing.                                                             |
| `device`                  | `desktop` | `mobile` | `tablet` | `custom` | `desktop`  | Desktop 1920x1080; mobile 390x844 @2x; tablet 820x1180 @2x; custom uses `width`/`height`/`deviceScaleFactor`. |
| `width`, `height`         | integer                                       | 1920, 1080 | Viewport size for `custom`.                                                                                   |
| `deviceScaleFactor`       | number                                        | 1          | Pixel ratio for `custom` (1 to 3).                                                                            |
| `fullPage`                | boolean                                       | `false`    | Capture the whole page instead of the viewport.                                                               |
| `selector`                | string                                        | (none)     | CSS selector: capture only this element.                                                                      |
| `format`                  | `png` | `jpeg` | `webp`                     | `png`      | Image format.                                                                                                 |
| `quality`                 | integer                                       | 80         | 1 to 100, for jpeg and webp.                                                                                  |
| `waitUntil`               | `load` | `domcontentloaded` | `networkidle` | `load`     | Load event to wait for (soft: never fails the URL).                                                           |
| `extraDelayMs`            | integer                                       | 0          | Extra wait before the capture.                                                                                |
| `hideCookieBanners`       | boolean                                       | `true`     | Dismiss or hide cookie and consent popups.                                                                    |
| `blockAds`                | boolean                                       | `true`     | Block ad and tracker requests and hide empty ad slots.                                                        |
| `darkMode`                | boolean                                       | `false`    | Emulate `prefers-color-scheme: dark`.                                                                         |
| `concurrency`             | integer                                       | 5          | Max pages in parallel; capped at 2 per CPU core (2 GB = 1 page, 8 GB = 4).                                    |
| `timeoutSecs`             | integer                                       | 60         | Time limit for one attempt at one URL.                                                                        |
| `maxRetries`              | integer                                       | 1          | Retries for temporary errors, with 1 s, 2 s, 4 s... backoff.                                                  |
| `hideSelectors`           | string\[]                                      | `[]`       | Extra CSS selectors to hide (chat widgets, sticky headers...).                                                |
| `scrollToLoadLazyContent` | boolean                                       | `true`     | With `fullPage`, scroll first so lazy content loads.                                                          |
| `maxFullPageHeight`       | integer                                       | 15000      | Height cap in CSS pixels for full-page and element shots.                                                     |
| `proxyConfiguration`      | object                                        | (none)     | Optional proxy for sites that block data centers.                                                             |

### Output example

One dataset item per input URL, in the same form for successes and failures:

```json
[
    {
        "url": "https://apify.com",
        "finalUrl": "https://apify.com/",
        "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/screenshot_0001_apify-com.webp",
        "screenshotKey": "screenshot_0001_apify-com.webp",
        "title": "Apify: Full-stack web scraping and data extraction platform",
        "width": 780,
        "height": 14230,
        "format": "webp",
        "device": "mobile",
        "status": "ok",
        "error": null,
        "httpStatus": 200,
        "truncated": false,
        "attempts": 1,
        "takenAt": "2026-10-17T19:30:12.511Z",
        "billedAs": "full-page-screenshot"
    },
    {
        "url": "https://example.com/missing-page",
        "finalUrl": null,
        "screenshotUrl": null,
        "screenshotKey": null,
        "title": null,
        "width": null,
        "height": null,
        "format": "webp",
        "device": "mobile",
        "status": "failed",
        "error": "HTTP 404 Not Found",
        "httpStatus": 404,
        "truncated": false,
        "attempts": 1,
        "takenAt": "2026-10-17T19:30:09.004Z",
        "billedAs": null
    }
]
```

- `screenshotUrl` opens the image directly. The same file is in the run's default key-value store
  under `screenshotKey` (`screenshot_<position in input>_<host>.<format>`).
- `width` and `height` are the real pixel dimensions of the image (viewport x pixel ratio).
- `status` is `ok` only when the image was stored. Only `ok` items are charged; `billedAs` says
  whether as `screenshot` or `full-page-screenshot` (`null` = not charged).
- `truncated: true` means the page was taller than `maxFullPageHeight` and was cut off.

### Pricing

This Actor uses **pay-per-event** pricing:

| Event                  | Price                                              | When it's charged                                                     |
| ---------------------- | -------------------------------------------------- | --------------------------------------------------------------------- |
| `screenshot`           | **$0.005** per screenshot ($5 per 1,000)           | Viewport or element (`selector`) screenshot, once the image is stored |
| `full-page-screenshot` | **$0.01** per full-page screenshot ($10 per 1,000) | `fullPage: true` screenshot, once the image is stored                 |

Full-page screenshots cost more because the whole page is rendered and encoded (often 10-15x the
pixels of one screen). If you only need what's visible, leave `fullPage` off. Each dataset item's
`billedAs` field shows which event it was charged as.

- Failed URLs (timeouts, 404s, unknown domains, missing selector, invalid URL) are **never charged**.
- Retries are free: a URL that succeeds on its third attempt is charged once.
- Platform compute is included in the price, so slow sites don't cost you more.
- If you set a **maximum cost per run**, the Actor stops starting new screenshots when the limit is
  reached and reports the rest as `failed` with the reason "maximum cost per run was reached"
  (not charged).

### Use from the API, code or AI agents

**For AI agents (Apify MCP server):** call this Actor with `{"urls": [...]}` and read the dataset
items. Every input URL gets exactly one item. Check `status`: when it's `"ok"`, use `screenshotUrl`.
When it's `"failed"`, `error` says why in plain language and nothing was charged. Use
`device: "mobile"` for a phone view, `selector` for one element, and `fullPage: true` only when the
whole page is needed (it costs $0.01 instead of $0.005).

**One HTTP call, results back directly** (waits until the run finishes):

```bash
curl -X POST "https://api.apify.com/v2/acts/<username>~website-screenshot-api/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://apify.com"], "fullPage": true, "format": "jpeg"}'
```

**JavaScript:**

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<username>/website-screenshot-api').call({ urls: ['https://apify.com'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => (i.status === 'ok' ? i.screenshotUrl : `${i.url}: ${i.error}`)));
```

**Monitoring pages:** schedule the Actor (for example daily) to keep a visual history of a page.

### Tips and limitations

- **SPAs and dashboards:** use `waitUntil: "networkidle"` and maybe `extraDelayMs: 1000`.
- **Element not found:** the URL fails with `No element matches selector "..."` and isn't charged.
  Check the selector in your browser's DevTools.
- **Bot protection:** some sites (Cloudflare challenges, login walls) answer with 403 or a challenge
  page. A 403 is reported as `failed`; try `proxyConfiguration` with residential proxies.
- **HTTP errors:** pages that answer with HTTP 4xx/5xx are reported as `failed` and not charged, even
  if they show an error page.
- Very long pages are cut at `maxFullPageHeight` (default 15,000 px). WebP images can't be taller
  than 16,383 pixels, so WebP full-page shots are limited to that height.
- Cookie banner removal covers the common consent platforms and thousands of site-specific banners,
  but not every one in existence. Add stubborn ones to `hideSelectors`.

# Actor input Schema

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

Web pages to screenshot, one per line. The scheme is optional ("example.com" becomes "https://example.com"). Each URL produces exactly one dataset item and, if it succeeds, one image in the key-value store. Invalid or unreachable URLs are reported as status "failed" and are not charged.

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

Which device to emulate. "desktop" = 1920x1080 viewport. "mobile" = 390x844 phone (2x pixel ratio, touch, mobile user agent). "tablet" = 820x1180 (2x, touch). "custom" = use the Width, Height and Pixel ratio fields below.

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

Viewport width in CSS pixels. Used only when device is "custom".

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

Viewport height in CSS pixels. Used only when device is "custom". With fullPage off, this is exactly the height of the image (times pixel ratio), so use it to capture just the top part of a page.

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

Device pixel ratio for device "custom" (1 = normal, 2 = retina). The image is width x ratio pixels wide.

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

If true, capture the whole scrollable page instead of only the visible viewport. Lazy-loaded images are loaded first by scrolling (see "Scroll to load lazy content"). Very long pages are cut at "Max full-page height" and marked truncated: true. Priced as a full-page screenshot ($0.01 instead of $0.005), because the whole page is rendered.

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

CSS selector of a single element to capture instead of the page, e.g. "#main", "header" or ".product-card". The first matching element is used. If nothing matches, the URL fails with a clear error and is not charged. Leave empty to capture the page.

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

"png" is lossless (best for text/UI, larger files). "jpeg" and "webp" are smaller; webp is usually ~30% smaller than jpeg at the same quality.

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

Compression quality from 1 to 100 for jpeg and webp. Ignored for png.

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

When the page counts as loaded. "load" (default) waits for images and scripts; "domcontentloaded" is fastest; "networkidle" waits until no network requests for 500 ms (best for single-page apps). If the event never happens (e.g. a site that polls forever), the screenshot is taken anyway instead of failing.

## `extraDelayMs` (type: `integer`):

Additional wait in milliseconds after the page loaded and overlays were hidden, right before the screenshot. Use it for animations, charts or slow widgets. 0 = no extra delay.

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

Dismiss or hide cookie/consent popups (OneTrust, Cookiebot, Didomi, Quantcast, Usercentrics, Sourcepoint and thousands of others) and unlock scrolling they block.

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

Block requests to major ad networks and trackers and hide leftover ad slots. Also makes pages load faster.

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

Emulate the dark color scheme (prefers-color-scheme: dark). Only sites that support dark mode will look different.

## `concurrency` (type: `integer`):

Maximum number of pages captured in parallel. The Actor runs at most 2 pages per CPU core (Apify gives 1 core per 4 GB of memory), so the default 2 GB run captures one page at a time. Give the run more memory (up to 8 GB) to go faster on large batches.

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

Maximum time for one attempt at one URL (loading, waiting and capturing). A slow URL never blocks the rest of the run.

## `maxRetries` (type: `integer`):

How many times to retry a URL after a temporary failure (timeout, network error, HTTP 429/5xx), with exponential backoff (1 s, 2 s, 4 s...). Each retry of a slow page can take up to timeoutSecs, so raise this only for flaky sites. Permanent failures such as HTTP 404 or an unknown domain are not retried.

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

CSS selectors of elements to hide before the screenshot, e.g. chat widgets, sticky headers or newsletter popups: \["#intercom-container", ".sticky-header"].

## `scrollToLoadLazyContent` (type: `boolean`):

With full page on, scroll through the page first so lazy-loaded images and sections appear, then scroll back to the top. Bounded to 10 seconds and Max full-page height, so infinite feeds can't stall the run.

## `maxFullPageHeight` (type: `integer`):

Full-page and element screenshots are cut at this height in CSS pixels (the item gets truncated: true). Protects against infinite-scroll pages. WebP is additionally limited to 16383 image pixels.

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

Optional proxy for sites that block data-center IPs or show region-specific content. Leave empty to connect directly.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://www.wikipedia.org"
  ],
  "device": "desktop",
  "width": 1920,
  "height": 1080,
  "deviceScaleFactor": 1,
  "fullPage": false,
  "format": "png",
  "quality": 80,
  "waitUntil": "load",
  "extraDelayMs": 0,
  "hideCookieBanners": true,
  "blockAds": true,
  "darkMode": false,
  "concurrency": 5,
  "timeoutSecs": 60,
  "maxRetries": 1,
  "hideSelectors": [],
  "scrollToLoadLazyContent": true,
  "maxFullPageHeight": 15000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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://apify.com",
        "https://www.wikipedia.org"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("evermore_lancet_yhd/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 = {
    "urls": [
        "https://apify.com",
        "https://www.wikipedia.org",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("evermore_lancet_yhd/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 '{
  "urls": [
    "https://apify.com",
    "https://www.wikipedia.org"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call evermore_lancet_yhd/website-screenshot-api --silent --output-dataset

```

## MCP server setup

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