# Website Screenshot API: Full Page, Bulk & Sitemap (`bjgomes/website-screenshot`) Actor

Take full-page screenshots of any list of URLs or a whole website from its sitemap, with cookie banners hidden and lazy images loaded. PNG, JPEG, WebP or PDF, desktop, tablet or mobile. Also a real-time API that returns the image directly.

- **URL**: https://apify.com/bjgomes/website-screenshot.md
- **Developed by:** [Brian Gomes](https://apify.com/bjgomes) (community)
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 screenshots

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

**Take clean screenshots of any web page, a list of pages or a whole website.** Cookie banners are hidden, lazy-loaded images are loaded, and chat widgets don't float over the middle of the image. You get JPEG, PNG, WebP or PDF, on desktop, tablet or mobile. Use it as a batch run, or as a real-time API that answers with the image itself.

- **No cookie banners or pop-ups.** Banners from OneTrust, Cookiebot, Didomi, Quantcast, TrustArc, Usercentrics and 15 more consent platforms are hidden, along with any other fixed banner that talks about cookies, and sign-up and discount pop-ups. So is the dark overlay behind them, and the page can scroll again.
- **The whole page, really loaded.** It scrolls through the page first, so lazy images and sections that load on scroll are in the shot.
- **No ads or trackers.** Ad, analytics and tracking scripts aren't loaded, so pages load faster and come out cleaner. You can turn this off.
- **No floating widgets.** Chat bubbles and bars stuck to the bottom of the window are hidden on full-page captures. The header stays at the top.
- **Capture what you need.** The full page, the visible screen, a fixed height, or one element by CSS selector.
- **A whole site from its sitemap.** Give it a website and it screenshots every page its sitemap lists.
- **Real-time API.** `GET https://bjgomes--website-screenshot.apify.actor/?url=https://example.com` answers with the image.
- **Failed screenshots are free.** A page that times out or can't load gives you a row that says why, and isn't charged.

### Quick start

1. Put a page in **Page URLs to screenshot**.
2. Click **Start**.
3. Open the **Output** tab. Each row has a link to its image. Every image is also in the run's **Key-value store**.

### What does Website Screenshot API do?

For each URL and device, it opens the page in Chromium, waits for it to load, cleans it up, and captures it:

1. Loads the page and waits for the `load` event (at most 15 seconds, since ads can hold it back for minutes), then up to 3 seconds more for the network to go quiet.
2. Waits for an element you name, or for an extra delay, if you set one.
3. Hides cookie banners, pop-ups and their overlays, chat widgets, and any element you list.
4. Scrolls down the page so lazy content loads, then back to the top.
5. Captures the page, saves the image, then adds its row to the dataset. A row never points at an image that isn't saved yet.

Here's what a few rows look like:

| url | device | width × height | statusCode | cookieBannersHidden |
|---|---|---|---|---|
| https://example.com/ | desktop | 1920 × 4210 | 200 | 2 |
| https://example.com/pricing | mobile | 780 × 6532 | 200 | 1 |
| https://example.com/old-page | desktop | 1920 × 1080 | 404 | 0 |

(Example data. A page that answers 404 is still captured: you see what a visitor sees.)

### Real-time API (Standby mode)

The same Actor answers HTTP requests directly. Send a URL and get the image back as the response, with no run to start and no dataset to read.

```bash
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://bjgomes--website-screenshot.apify.actor/?url=https://example.com&device=mobile&format=jpeg" \
  -o example.jpg
```

- Every input option works as a query parameter, with the same name: `captureMode`, `format`, `quality`, `height`, `selector`, `viewportWidth`, `viewportHeight`, `maxHeight`, `waitUntil`, `waitForSelector`, `delayMs`, `hideCookieBanners`, `hideStickyElements`, `scrollToLoad`, `blockAds` and `timeoutSecs`. Use `device` (one of `desktop`, `tablet`, `mobile`) instead of `devices`, and give `hideSelectors` as a comma-separated list.
- Or `POST /` with the same options as a JSON body.
- The response headers describe the image: `X-Final-Url`, `X-Status-Code` (what the page answered), `X-Truncated`, `X-Width` and `X-Height`.
- A request that fails gets a JSON error, and isn't charged: `400` for bad input or a private address, `422` when the page can't be loaded, `504` when it took longer than `timeoutSecs`, `503` when too many screenshots are already in progress (try again in a few seconds), and `402` when your maximum charge is reached.
- The **Endpoints** tab on this page lets you try it from your browser.

In Standby mode you pay for each screenshot, plus Apify's compute for the time the server is up. That's how Apify bills every Standby Actor.

### Input

| Field | What it does | Default |
|---|---|---|
| Page URLs to screenshot (`urls`) | Single pages, each captured as given. | none |
| Whole websites or sitemaps (`startUrls`) | A website or a sitemap URL. Every page in its sitemap is captured. Pages robots.txt disallows are skipped. | none |
| Maximum URLs (`maxUrls`) | Stop after this many URLs. Each is captured once per device. | 100 |
| Devices (`devices`) | `desktop`, `tablet` (an iPad, 820 × 1180 at 2×) and `mobile` (an iPhone, 390 × 844 at 2×). | desktop |
| What to capture (`captureMode`) | `fullPage`, `viewport` (the visible screen), `height` (the top of the page, down to Height) or `element` (one element). | fullPage |
| Height (`height`) | For a fixed-height capture, in pixels. | 1080 |
| Element selector (`selector`) | For an element capture, a CSS selector like `#pricing`. | none |
| Image format (`format`) | `jpeg`, `png`, `webp` or `pdf` (A4 pages, with backgrounds). JPEG is fast and small; a full-page PNG is lossless but much larger and slower to make. | jpeg |
| Quality (`quality`) | For JPEG and WebP, 1 to 100. | 80 |
| Desktop width and height (`viewportWidth`, `viewportHeight`) | The desktop browser window. | 1920 × 1080 |
| Maximum height (`maxHeight`) | A full-page capture stops here. The row says `truncated` when the page was taller. | 15,000 |
| Wait until (`waitUntil`) | `load`, `domcontentloaded` or `networkidle`. | load |
| Wait for element (`waitForSelector`) | Wait until this element is visible before capturing. | none |
| Extra delay (`delayMs`) | Wait this long after loading, for animations. | 0 |
| Hide cookie banners and pop-ups (`hideCookieBanners`) | See above. | on |
| Hide chat widgets and bottom bars (`hideStickyElements`) | See above. | on |
| Scroll to load images (`scrollToLoad`) | See above. | on |
| Block ads and trackers (`blockAds`) | Don't load ads, analytics and tracking scripts. | on |
| Also hide these elements (`hideSelectors`) | CSS selectors for anything else to hide, like a newsletter popup. | none |
| Screenshots at once (`maxConcurrency`) | Pages loading at the same time. Loading pages is CPU work, and Apify gives a run 1 CPU per 4 GB of memory: keep it at 2 for every 4 GB. | 2 |
| Timeout per screenshot (`timeoutSecs`) | The longest a page may take to load and get ready. Capturing the image then gets up to 45 seconds more. | 60 |

Example input:

```json
{
  "urls": [{ "url": "https://example.com" }],
  "startUrls": [{ "url": "https://example.org/sitemap.xml" }],
  "devices": ["desktop", "mobile"],
  "format": "jpeg",
  "maxUrls": 200
}
```

### Output

One row per URL and device, in the run's dataset. Export it as CSV, JSON, Excel, XML or HTML, or read it through the Apify API. The images are in the run's key-value store.

| Field | What it holds |
|---|---|
| `type` | `screenshot`, or `error` for one that couldn't be taken. |
| `screenshotUrl` | A link to the image. |
| `screenshotKey` | The image's key in the run's key-value store. |
| `url`, `finalUrl` | The URL asked for, and where it ended up after redirects. |
| `device`, `format` | As captured. |
| `width`, `height`, `bytes` | The image's size in pixels and bytes. |
| `truncated` | `true` when a full page was taller than Maximum height and was cut off there. |
| `statusCode`, `title` | What the page answered, and its title. |
| `cookieBannersHidden`, `popupsHidden`, `stickyHidden` | How many cookie banners, pop-ups, and widgets and bars were hidden. |
| `loadTimeMs` | How long the page took to load. |
| `capturedAt` | When it was captured. |
| `error` | Empty for a screenshot. Otherwise what went wrong, like `timed out after 60 s`. |
| `foundIn`, `startUrl` | `input` or `sitemap`, and for a sitemap page, the website it came from. |

### How much does it cost?

You pay per screenshot. There's no monthly fee.

- **Each screenshot: $0.008**, which is $8 per 1,000, whatever the capture mode or format. One URL on two devices is two screenshots.
- **Not charged:** screenshots that fail (timeouts, pages that won't load, private addresses), pages a site blocks (401, 403, 407 or 429), and pages robots.txt disallows.
- Apify's standard start fee applies to each run. In Standby mode, Apify's compute is added (see above).

A worked example: a 500-page site, on desktop and mobile, is 1,000 screenshots, which costs $8.00.

Set Maximum URLs, or a maximum cost per run, for a hard ceiling. The run stops cleanly when the next screenshot wouldn't fit.

### How it handles the hard cases

- **Cookie banners.** Known consent platforms are hidden by a style sheet, so a banner the site adds again stays hidden. Any other fixed banner that mentions cookies or consent is hidden too, before and after scrolling, since some banners only show up late. It hides banners; it never clicks accept or reject.
- **Ads and trackers.** Requests to 75 ad, analytics and tracking domains (Google Ads and Analytics, Tag Manager, Meta, Criteo, Taboola, Outbrain, Hotjar and others) are refused before they leave. On news and shopping sites that's most of the browser's work. A page you ask for by name is always loaded, even if it's on one of those services.
- **Pop-ups.** A dimmed layer over the whole window is hidden, and so is a dialog floating in the window or anything fixed with an email field in it, which covers most newsletter and discount pop-ups. A fixed header at the top, with its Sign up button, stays.
- **Lazy loading.** Images marked `loading="lazy"` are switched to load right away, and the page is scrolled a screen at a time, for up to 10 seconds, so content that loads on scroll appears. Then it waits up to 3 seconds for those images.
- **Very tall pages.** Infinite feeds keep growing. A full-page capture stops at Maximum height and says so in `truncated`.
- **Slow and stuck pages.** Loading and getting the page ready has one timeout. Capturing the image then gets up to 45 seconds of its own, since a very tall PNG can take a while to encode, so a slow page doesn't leave its capture no time. Past either, you get a row that says which, and it isn't charged. After 20 failures in a row the run stops, rather than working through a site that's down.
- **Fonts.** Color emoji and Chinese, Japanese and Korean fonts are installed, so text doesn't turn into boxes.
- **Private addresses.** Every request the page makes, including redirects, images and frames, goes through an address check. Anything on a private network, loopback, or a cloud metadata address is refused. A page that points there gives you an error row.
- **robots.txt.** Pages you list yourself are captured the way a person opening them would see them. Pages that come from a sitemap follow the site's robots.txt.
- **Bot protection and login walls.** It runs a standard Chromium with no tricks. When a site answers 401, 403, 407 or 429 (a bot wall, a login page or a rate limit), you get an error row that says so, not a screenshot of the block page, and it isn't charged. A page that answers 404 or 500 is captured, since that's what visitors see.
- **Strict security headers.** Some sites forbid added styles and scripts with a Content-Security-Policy. The browser is allowed past it for the clean-up, so banners are still hidden on those sites.

### FAQ

**How do I take a full-page screenshot of a website?**
Put the URL in Page URLs to screenshot and run it. Full page is the default.

**How do I screenshot every page of my website?**
Put your homepage in Whole websites or sitemaps. It reads your sitemap and captures every page it lists, up to Maximum URLs.

**Can I get a screenshot from my own code without starting a run?**
Yes, use the real-time API above. One GET request returns the image.

**How do I capture just one part of a page?**
Set What to capture to One element and give a CSS selector, like `#pricing` or `.hero`.

**Why is a banner or pop-up still showing?**
Some sites use their own with no tell-tale words or markup. Add its CSS selector to Also hide these elements.

**Should I use JPEG or PNG?**
JPEG, unless you need every pixel exact. A full-page PNG of a long page can be 10 MB or more and takes many times longer to make. On a long page, a PNG can run out of time where a JPEG wouldn't.

**Why is the image cut off at the bottom?**
The page is taller than Maximum height. Raise it, up to 20,000 pixels.

**Is it charged when a screenshot fails?**
No. Only finished screenshots are charged. A site that blocks the browser isn't charged either.

**Why did a site come back as "blocked the browser"?**
Some large sites (news, shops, travel) block automated browsers with bot protection. This Actor doesn't try to get around it, so those sites return an error row, free of charge.

### Limits

- It doesn't log in, and it doesn't fill in forms.
- It doesn't get around bot protection or CAPTCHAs.
- Full-page captures stop at 20,000 pixels.
- PDF output prints the page on A4 pages. The capture mode doesn't apply to PDFs.
- It runs at 4 GB by default, which on Apify is one CPU, enough for 2 screenshots at a time. A typical page takes 10 to 30 seconds from opening to the finished image. For a large site, give the run more memory and raise Screenshots at once to match (2 per 4 GB).

### Integrations

This Actor runs on Apify, so everything the platform does works with it.

- **API.** Start a run and read the results from your own code, in any language, or use the real-time API above. [Apify API](https://docs.apify.com/integrations/api)
- **Schedules.** Capture your pages every week and keep a visual history. [Schedules](https://docs.apify.com/actors/running/schedules)
- **Tasks.** Save your input once as a task and run it again with one click, or on a schedule. [Tasks](https://docs.apify.com/actors/running/tasks)
- **Webhooks.** Get a call to your own URL when a run succeeds or fails. [Webhooks](https://docs.apify.com/integrations/webhooks)
- **Zapier, Make and n8n.** Take a screenshot as one step of a workflow. [Zapier](https://docs.apify.com/integrations/zapier), [Make](https://docs.apify.com/integrations/make), [n8n](https://docs.apify.com/integrations/n8n)
- **AI agents (MCP).** Claude, ChatGPT and other MCP clients can find and run this Actor through the Apify MCP server. [Apify MCP server](https://docs.apify.com/integrations/mcp)
- **Other Actors.** Pass this Actor's results to another Actor when a run finishes. [Actor-to-Actor](https://docs.apify.com/integrations/actors)

### The SEO toolkit

Five Actors that work together. All are priced per result, with no monthly fee.

| Actor | What you get | Price |
|---|---|---|
| [Website SEO Audit & Crawler](https://apify.com/bjgomes/site-audit-seo) | A 0–100 SEO score and a fix list for every page, with week-over-week changes | $5 / 1,000 pages |
| [Broken Link Checker](https://apify.com/bjgomes/broken-link-checker-from-sitemap) | Every 404, server error and redirect chain, with the page it's on | $1 / 1,000 links |
| [Bulk PageSpeed Insights](https://apify.com/bjgomes/bulk-pagespeed-insights-for-a-sitemap) | Lighthouse score and Core Web Vitals for every URL, mobile and desktop | $2.50 / 1,000 tests |
| [Sitemap URL Extractor](https://apify.com/bjgomes/sitemap-url-extractor-status-checker) | Every URL in a site's sitemaps, with lastmod and status codes | $0.50 / 1,000 URLs |
| **Website Screenshot API** (this one) | A clean full-page screenshot of every URL, with cookie banners and pop-ups hidden | $8 / 1,000 screenshots |

### Support

Found a bug or need a feature? Open an issue on this Actor's **Issues** tab. Include the run ID and the URL you gave it.

If it saved you time, a quick review on the **Reviews** tab helps other people find it.

# Actor input Schema

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

Single pages, each captured as given. Add as many as you like.

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

A website (example.com) to find its sitemaps from robots.txt, /sitemap.xml and /sitemap_index.xml, or a sitemap URL to read directly. Every page the sitemap lists is captured, up to Maximum URLs. Pages that robots.txt disallows are skipped.

## `maxUrls` (type: `integer`):

Stop after this many URLs across all inputs. Each URL is captured once per device.

## `devices` (type: `array`):

Capture each URL as it looks on these devices. Each device is a separate screenshot.

## `captureMode` (type: `string`):

Full page: the whole page, top to bottom, up to Maximum height. Visible screen: only what fits in the browser window. Fixed height: the top of the page, down to Height. One element: the first element that matches Element selector.

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

With Fixed height, how much of the page to capture, from the top.

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

With One element, a CSS selector for the element to capture, like #pricing or .hero.

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

JPEG is fast and small, and right for most uses. PNG is lossless, but a full-page PNG is much larger and slower to make. WebP is smaller still. PDF prints the page on A4 pages, with backgrounds.

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

From 1 to 100. Higher is sharper and larger.

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

The desktop browser window's width. Tablet and mobile use their own sizes.

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

The desktop browser window's height. It's the size of a Visible screen capture.

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

A Full page capture stops at this height. A taller page is cut off there, and its row says truncated.

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

When the page counts as loaded. Load works for most sites: it waits for the load event (at most 15 seconds, since ads can hold it back for minutes), then up to 3 seconds for the network to go quiet. Network idle waits up to 15 seconds for it. DOM ready doesn't wait for images.

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

Wait until an element matching this CSS selector is visible before capturing. If it never shows up, the row says so and isn't charged.

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

Wait this long after the page loads, for animations to finish.

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

Hide cookie and consent banners, sign-up and discount pop-ups, and the dark overlays behind them, and let the page scroll again. It hides them; it doesn't click accept or reject.

## `hideStickyElements` (type: `boolean`):

Hide chat widgets. On a Full page or Fixed height capture, also hide fixed bars stuck to the bottom of the window, which otherwise float over the middle of the image. A fixed header at the top stays.

## `scrollToLoad` (type: `boolean`):

Scroll through the page before capturing, so lazy-loaded images and sections appear, then scroll back to the top.

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

Don't load ads, analytics and tracking scripts. Pages load faster, screenshots are cleaner, and fewer runs time out. Turn it off to capture a page exactly as a visitor without an ad blocker sees it.

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

CSS selectors for anything else to hide, like a promo popup (#newsletter-modal).

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

How many pages load at the same time. Loading pages is CPU work, and Apify gives a run 1 CPU per 4 GB of memory: keep it at 2 for every 4 GB.

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

The longest a page may take to load and get ready. Capturing the image then gets up to 45 seconds more, for very tall pages. Past either, its row says it timed out, and it isn't charged.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://apify.com"
    }
  ],
  "maxUrls": 10,
  "devices": [
    "desktop"
  ],
  "captureMode": "fullPage",
  "height": 1080,
  "format": "jpeg",
  "quality": 80,
  "viewportWidth": 1920,
  "viewportHeight": 1080,
  "maxHeight": 15000,
  "waitUntil": "load",
  "delayMs": 0,
  "hideCookieBanners": true,
  "hideStickyElements": true,
  "scrollToLoad": true,
  "blockAds": true,
  "maxConcurrency": 2,
  "timeoutSecs": 60
}
```

# Actor output Schema

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

One row per URL and device, with a link to the image and what the page answered.

## `images` (type: `string`):

Every image this run saved, in the run's key-value store.

# 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": [
        {
            "url": "https://apify.com"
        }
    ],
    "maxUrls": 10
};

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

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

```

## MCP server setup

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