# Website Screenshot API: Bulk, Full Page, Mobile & PDF (`altronite/website-screenshot`) Actor

Website screenshot tool and API: bulk URLs to PNG, JPEG or PDF, full-page or element capture, device presets, cookie banners hidden. Failed URLs are never charged.

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

## Pricing

from $0.60 / 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: Bulk, Full Page, Mobile & PDF

A website screenshot tool and API for batches of URLs. Capture public web pages as a **PNG, JPEG or PDF**: the visible screen, the **full scrolling page** or **one element** by CSS selector, at desktop, laptop, tablet or mobile size. Common cookie banners are hidden with CSS and robots.txt is respected by default. A URL that can't be captured becomes an error row with the reason, is **never charged**, and doesn't stop the run.

![Input: a list of URLs. Output: desktop and mobile PNG screenshots, plus an uncharged error row for a 404 page](https://api.apify.com/v2/key-value-stores/mc85vPyUacSDLYPwt/records/website-screenshot-input-output.png)

![One link in, one screenshot row out](https://api.apify.com/v2/key-value-stores/aZVzOVjoal7bHifMa/records/website-screenshot-demo.gif)

### Who it's for

- **QA and front-end teams**: check mobile and desktop layouts across many pages in one run, or capture a single component with `selector`.
- **SEO and marketing**: snapshot your own and competitors' landing and pricing pages.
- **Monitoring and archiving**: schedule runs for a dated visual record, as images or PDFs.
- **AI agents and LLM pipelines**: give a model an image of a page; every row has a `status` field to branch on.

### Quick recipes: bulk, full page and mobile screenshots

Copy one of these into the **Input** tab (JSON view) and press **Start**.

**Bulk screenshots from a URL list** (visible screen, desktop):

```json
{ "urls": ["https://example.com", "https://example.org", "https://example.net"] }
```

**Full-page screenshot of every page** (the whole scrolling page as one PNG):

```json
{ "urls": ["https://example.com"], "fullPage": true }
```

**Website to PDF in bulk** (one PDF per URL, using the site's print styles):

```json
{ "urls": ["https://example.com", "https://example.org"], "format": "pdf" }
```

**Mobile screenshots** (390×844 at 3x), as compressed JPEGs:

```json
{ "urls": ["https://example.com"], "device": "mobile", "format": "jpeg", "quality": 80 }
```

**Screenshot one element** (by CSS selector):

```json
{ "urls": ["https://example.com"], "selector": "p" }
```

**Screenshot API for a JavaScript-heavy page** (wait for a selector, then capture the full page):

```json
{ "urls": ["https://example.com/pricing"], "fullPage": true, "waitUntil": "networkidle", "waitForSelector": "h1", "delayMs": 1000 }
```

**Tablet screenshots of competitor landing pages** (dark theme where the site supports it):

```json
{ "urls": ["https://example.com", "https://example.org"], "device": "tablet", "darkMode": true }
```

### What you get

**One dataset row per non-blank URL when the input contains at least one valid URL.** The file is saved in the run's key-value store and `screenshotUrl` links to it. The **Output** tab links to the dataset and stored files. Overview links to each image or PDF.

```json
{
  "status": "ok",
  "url": "https://example.com",
  "finalUrl": "https://example.com/",
  "httpStatus": 200,
  "title": "Example Domain",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<store-id>/records/screenshot-0000-327c3fda87ce.png",
  "storageKey": "screenshot-0000-327c3fda87ce.png",
  "format": "png",
  "device": "desktop",
  "viewportWidth": 1920,
  "viewportHeight": 1080,
  "fullPage": false,
  "bytes": 74188,
  "takenAt": "2026-10-02T22:39:37+00:00"
}
```

Successful rows contain `status`, `url`, `finalUrl`, `httpStatus`, `title`, `screenshotUrl`, `storageKey`, `format`, `device`, `viewportWidth`, `viewportHeight`, `fullPage`, `bytes` and `takenAt`.

`finalUrl` is the address after redirects; `title` is cut at 300 characters. Viewport sizes are CSS pixels, not file dimensions. `fullPage` records the requested option even when element capture or PDF ignores it. `bytes` is the stored file size. `takenAt` is the UTC row creation time.

Every row has `url`, `status` and `takenAt`. Uncharged error rows also have `error` (at most 500 characters), `input` and `scrapedAt` (same time as `takenAt`). `url` is normalized, or the raw input if normalization failed. Rejected HTTP pages and bot checks also have `httpStatus`; successful rows use null if no navigation response was available.

```json
{ "status": "error", "url": "https://example.com/missing", "httpStatus": 404, "error": "The page answered 404", "input": "https://example.com/missing", "takenAt": "2026-10-03T10:12:04+00:00", "scrapedAt": "2026-10-03T10:12:04+00:00" }
```

### Use it from code or an AI agent

Run this actor with the Apify API, the Apify Python or JavaScript (JS) clients, or as a tool through the Apify MCP server. Pass the same input JSON through any of these interfaces.

Every output row has a `status` field; inspect it before processing the row. For `ok` rows, fetch `screenshotUrl`; `error` rows explain failed captures.

Minimal input:

```json
{
  "urls": [
    "https://example.com"
  ]
}
```

### How to use it

1. **URLs**: one per line, up to 1,000 per run. Omit `urls` to use `["https://apify.com"]` for a single daily-test capture. `example.com` works too: `https://` is added for you.
2. **Device**: `desktop` (1920×1080), `laptop` (1366×768), `tablet` (810×1080 at 2x), `mobile` (390×844 at 3x), or `custom` with your own `viewportWidth` × `viewportHeight` (200-3840 × 200-2160, at 1x).
3. **What to capture**: the visible screen (default), **Full page** for the whole scrolling page, or an **Element selector** to capture only that element. If a selector is set, Full page is ignored.
4. **Format**: `png` (lossless), `jpeg` (set `quality` 1-100, default 80), or `pdf`.
5. **Waiting**: `waitUntil` is `load` (default), `domcontentloaded` or `networkidle`. `waitForSelector` waits until an element is visible. `delayMs` adds an extra wait after load, e.g. for animations, up to 30 s.
6. **Options**: `hideCookieBanners` (on), `darkMode` (asks the page for its dark theme), `respectRobotsTxt` (on), `timeoutSecs` (5-180, default 45), and `maxConcurrency` (1-7 pages in parallel, default 3; allow about 512 MB of memory per page; memory defaults to 1 GB and supports up to 4,096 MB; raise memory for very long full-page captures).
7. **Lazy images**: `scrollToLoad` (on) scrolls before PNG/JPEG capture, at most 20 steps / about 5 s, then returns to the top. PDF does not scroll.
8. **HTTP errors**: `chargeHttpErrors` (off) rejects 4xx/5xx pages without charging. Set it to true to capture and charge these pages; `httpStatus` still flags their status. Detected bot checks remain uncharged regardless of this option.

Example: two pages on a phone screen.

```json
{ "urls": ["https://example.com", "https://example.com/about"], "device": "mobile", "format": "jpeg" }
```

### Pricing

This actor uses **pay-per-event** pricing: every run has a small start fee (`apify-actor-start`), charged once per run (per GB above 1 GB), plus a per-capture charge (`screenshot`) for each screenshot or PDF delivered, meaning each `ok` row. The rates are on the actor's Pricing tab.

- **Not charged:** error rows. That covers URLs that failed to load after the retry, robots.txt skips, invalid URLs, a `selector` or `waitForSelector` that wasn't found, and any other capture error.
- **Charged:** successful captures (`ok` rows), including HTTP error pages only when `chargeHttpErrors` is true. Default HTTP rejections and detected bot checks are never charged. Successful-status login or paywall pages can still be captured and charged; there is no general login-wall detector.
- A URL listed twice is captured, and charged, twice.

Built for batches: the start fee is per run, so capturing many URLs in one run is the cheapest way to use it.

Check the Pricing tab for the current rates. A URL that fails costs nothing.

### Reliability

- **One bad URL never fails the run.** Any failure becomes an error row with the reason.
- **Navigation and screenshot/PDF browser errors or timeouts are retried once**, after a short random pause in a fresh browser context. Missing `selector` or `waitForSelector` elements, HTTP rejections, bot checks and robots skips are uncharged and not retried.
- **Timeouts apply per step.** `timeoutSecs` limits page loading, waiting for `waitForSelector`, and taking the image, each separately. PDF uses the browser print API without a separate timeout argument. Robots fetching has a 10 s timeout; scrolling takes about 5 s at most, and `delayMs` adds up to 30 s. These are not a total per-URL deadline.
- **Chinese, Japanese, Korean and emoji fonts** are installed, so text doesn't render as empty boxes.
- **Invalid input** (for example, no valid URL at all) stops the run at the start with a message saying what to fix. Nothing is charged.

### Use cases

1. **QA teams validating responsive layouts**: A QA engineer submits a list of product page URLs with `device: "mobile"` and reviews the PNG files in the dataset to spot overflow, broken components, or text clipping across a batch.

2. **SEO analysts tracking competitor pricing pages**: An SEO specialist schedules weekly runs capturing competitors' pricing pages as PDFs, building a dated visual record that shows when plans or promotions changed.

3. **AI pipeline developers feeding pages to a vision model**: A developer sends URLs through the synchronous API, checks each row's `status` field, and fetches `screenshotUrl` for successful captures to pass as images to a multimodal LLM.

4. **Web designers building client portfolios**: A designer captures client sites at desktop and tablet viewports, uses the element selector to grab just the hero section, and uses the JPEG outputs in their portfolio.

5. **Marketing teams monitoring their own landing pages**: A marketing team captures their landing pages weekly, comparing the downloaded images across runs to spot unintended layout shifts or broken images.

6. **Compliance officers archiving public disclosures**: A compliance officer captures regulatory disclosure pages as PDFs on a schedule, downloading each file to keep a dated visual record for audits.

### FAQ

**Does it respect robots.txt?**
Yes, by default. Before capturing a URL, it reads that site's robots.txt (cached per origin) and skips URLs disallowed for all user agents (`User-agent: *`). A robots.txt that returns any 4xx status or cannot be reached counts as "allowed". One that returns a server error (5xx) counts as "disallow everything", using a conservative policy. Rules use longest-match precedence, `*` wildcards and `$` end anchors; Allow wins ties. The final redirect target is checked before capture if its host differs. This happens after navigation, so the redirected request has already occurred. Only wildcard-agent groups are used, and percent-encoding normalization is not fully implemented. Turn `respectRobotsTxt` off only for pages you have the right to capture.

**How do I take a full page screenshot of a website?**
Set `fullPage` to `true`. The actor scrolls the page first so lazy-loaded images appear (`scrollToLoad`, on by default), then saves the whole scrolling page as one image.

**How do I take screenshots of many URLs at once?**
Put up to 1,000 URLs in `urls` and run once. You get one row per URL with a link to its file, and a URL that fails becomes an uncharged error row without stopping the run.

**Can it save a website as a PDF?**
Yes. Set `format` to `pdf` to get one PDF per URL using the site's print styles. For more PDF options (paper size, margins, page numbers, merging into one file) see URL to PDF under Related tools.

**Can AI agents call it?**
Yes, through the Apify API (the synchronous run endpoints suit small batches) or as a tool via Apify's MCP server. Check `status` on each row, then fetch `screenshotUrl`.

**Does it get past logins, paywalls or bot protection?**
No. It never logs in, uses no proxy and doesn't evade bot protection. Recognized bot checks become uncharged errors. Other protections may not be recognized; successful-status login and paywall pages can be captured and charged.

**How are cookie banners hidden?**
With CSS only; nothing is clicked or accepted. It targets common consent-manager containers and "cookie-consent" patterns. Custom pop-ups may still show.

**Why are some images missing further down a full-page shot?**
The default `scrollToLoad` pass helps load images before PNG/JPEG capture. Its 20-step / about 5 s bound cannot cover every very long page or slow image. Disable it if scrolling triggers unwanted behavior. PDFs do not scroll.

**How long are the files kept?**
They follow your Apify plan's data retention for run storage. Download anything you want to keep.

### Limits

- Up to 1,000 URLs per run, http and https only.
- PDFs are produced by the browser print engine on Letter-size pages using the site's print styles, so they can differ from the on-screen look. `fullPage` and `selector` don't apply to PDFs.
- Extremely tall pages may hit browser image-size limits. Try `pdf` for those.
- No WebP output, raw-HTML input, custom cookies or headers, proxies or geo-targeting.
- `darkMode` changes only sites that support a dark colour scheme.

### Related tools

- [URL to PDF](https://apify.com/altronite/url-to-pdf): turn web pages into print-ready PDFs.
- [Google Sheets Export & Import](https://apify.com/altronite/google-sheets-import-export): send any run's results to a Google Sheet.

### Support

Is a page not capturing correctly? Open an issue on the **Issues** tab with the URL and your input.

# Actor input Schema

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

Pages to capture, one per line. A missing scheme defaults to https://. Up to 1,000 per run.

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

Desktop 1920×1080, laptop 1366×768, tablet 810×1080 (2x), mobile 390×844 (3x), or custom (set the viewport below).

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

Used only when Device is Custom.

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

Used only when Device is Custom.

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

Capture the whole scrollable page instead of the visible viewport.

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

Optional CSS selector, e.g. `#main`. When set, only that element is captured (PNG/JPEG).

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

PNG (lossless), JPEG (smaller, quality below) or PDF (printable, full page).

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

1–100, JPEG only.

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

When the page counts as loaded. Network idle is safest for heavy single-page apps but slower.

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

Optional CSS selector that must be visible before capturing, e.g. `#main`.

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

Extra wait after load, e.g. for animations. Max 30,000 ms.

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

Hide common consent pop-ups with CSS. Nothing is clicked or accepted.

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

Ask the page for its dark colour scheme (prefers-color-scheme: dark).

## `respectRobotsTxt` (type: `boolean`):

Skip URLs the site's robots.txt disallows. Turn off only for pages you have the right to capture.

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

Per navigation, selector wait and image capture step. Navigation and capture failures are retried once; missing selectors are not retried.

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

Pages captured at the same time. Raise memory with it (about 512 MB per page).

## `chargeHttpErrors` (type: `boolean`):

Capture and charge pages with HTTP status 400 or higher. Bot checks are always uncharged errors.

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

Before PNG/JPEG capture, scroll toward the bottom and back to the top, at most 20 steps / about 5 seconds. Does not apply to PDF.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Web_scraping"
  ],
  "device": "desktop",
  "viewportWidth": 1280,
  "viewportHeight": 800,
  "fullPage": false,
  "format": "png",
  "quality": 80,
  "waitUntil": "load",
  "delayMs": 0,
  "hideCookieBanners": true,
  "darkMode": false,
  "respectRobotsTxt": true,
  "timeoutSecs": 45,
  "maxConcurrency": 3,
  "chargeHttpErrors": false,
  "scrollToLoad": true
}
```

# Actor output Schema

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

Rows with image or PDF links, plus uncharged errors.

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

Keys of captured images and PDFs in the default 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": [
        "https://apify.com",
        "https://en.wikipedia.org/wiki/Web_scraping"
    ]
};

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

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

```

## MCP server setup

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