# Website Screenshot API - $1/1k, Webpage to Image, URL to PNG (`martin10291/website-screenshot-api`) Actor

Website screenshot API: turn any public URL into a full-page or viewport PNG, JPEG or PDF with real Chromium. Desktop, tablet and mobile viewports, wait for selector, delay, dark mode, hide cookie banners and any element, capture one element, only-new mode for monitoring. Direct file URLs. Works fro

- **URL**: https://apify.com/martin10291/website-screenshot-api.md
- **Developed by:** [Martin Petrov](https://apify.com/martin10291) (community)
- **Categories:** SEO tools, AI, Integrations
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 results

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 - Webpage to Image, URL to PNG, JPEG & PDF

You need **screenshots of web pages at scale**: link previews and thumbnails, visual QA after a deploy, archiving competitor pages, SEO reports, or an **AI agent** that has to "look" at a page. Running your own headless browser fleet is a chore, and most screenshot SaaS plans charge a monthly fee. This actor takes a list of URLs and returns **full-page or viewport screenshots as PNG, JPEG or PDF**, each with a **direct download URL**. You pay **$1 per 1,000 screenshots**; failed pages are free.

It uses real **Playwright Chromium** with its honest User-Agent. No proxies, no CAPTCHA solving, no login pages.

### What you get

- **screenshotUrl**: direct link to the PNG / JPEG / PDF file (stored in the run's key-value store).
- **title, finalUrl** (after redirects), **statusCode**, **loadTimeMs**.
- **width, height, deviceScaleFactor, pageHeight, truncated, bytes, sha256** of the file.
- **contentHash** of the visible text (used by only-new mode), **warning** when a page loaded only partly.
- A **RUN_SUMMARY** record with counts, failures and `lastError`.

### Features

- **Full page or viewport** (thumbnail) capture, with a max height for very long pages.
- **Devices**: desktop 1366x768, desktop HD 1920x1080, laptop, tablet (2x), mobile 390x844 (3x, touch), or a custom viewport.
- **Formats**: PNG, JPEG (quality setting) or **PDF** (one long page with backgrounds).
- **Wait options**: load / domcontentloaded / networkidle, wait for a CSS selector, extra delay.
- **Hide cookie banners** (OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast and more) and any element you list. Nothing is clicked or accepted.
- **Element screenshot**: capture just `header`, `#pricing` or `.product-card`.
- **Dark mode**, **lazy-image scrolling**, 1-4 pages in parallel.
- **Only-new mode** with `monitorId`: get (and pay for) a screenshot only when the page text changed. Great for visual change monitoring.

### Use cases

- Website thumbnails and link previews for directories, CRMs and newsletters.
- Visual regression and uptime proof after deploys; daily archive of landing pages.
- SEO and marketing agency reports (desktop + mobile shots of client and competitor pages).
- Give an AI agent eyes: screenshot a URL and pass the image to a vision model.
- Monitor competitor pricing pages and get a screenshot only when they change.

### API example (curl)

Replace `YOUR_APIFY_TOKEN` with your token (Apify Console, Settings, Integrations). One request runs the actor and returns the results with the file URLs:

```bash
curl -X POST "https://api.apify.com/v2/acts/martin10291~website-screenshot-api/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com", "https://www.python.org"], "device": "mobile", "fullPage": true, "format": "png"}'
```

Python:

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("martin10291/website-screenshot-api").call(run_input={"urls": ["https://example.com"], "format": "jpeg"})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["url"], item["screenshotUrl"])
```

### MCP example (Claude, Cursor, any MCP client)

Every Apify actor is also an MCP tool. Add this actor to the Apify MCP server:

```
https://mcp.apify.com?tools=martin10291/website-screenshot-api
```

In Claude Desktop or Cursor add it as a remote MCP server (OAuth login to Apify, or `Authorization: Bearer YOUR_APIFY_TOKEN`). Then ask: "Take a mobile screenshot of https://example.com", and the agent calls this actor and gets the image URL back.

### Input

| Field | Type | Description |
|---|---|---|
| `urls` | array | Public pages to capture (a bare domain works too). |
| `device` | string | desktop, desktop-hd, laptop, tablet, mobile. |
| `fullPage` | boolean | Whole page (default) or viewport only. |
| `format` | string | png (default), jpeg, pdf. |
| `jpegQuality` | integer | 10-100, JPEG only. |
| `viewportWidth`, `viewportHeight` | integer | Custom viewport, 0 = preset. |
| `maxHeight` | integer | Cut full-page captures at this height (default 10000 px). |
| `waitUntil` | string | load, domcontentloaded, networkidle. |
| `delayMs` | integer | Extra wait before the capture. |
| `waitForSelector` | string | Wait until this element is visible. |
| `elementSelector` | string | Capture only this element. |
| `hideCookieBanners` | boolean | Hide common consent popups (default on). |
| `hideSelectors` | array | Extra CSS selectors to hide. |
| `darkMode` | boolean | prefers-color-scheme: dark. |
| `scrollToLoadImages` | boolean | Scroll first so lazy images load (default on). |
| `maxUrls`, `concurrency`, `timeoutSecs` | integer | Limits and speed. |
| `onlyNew`, `monitorId` | boolean, string | Only pages whose text changed since the last run. |
| `includeFailed` | boolean | Also save failed pages (charged). |

### Output example

```json
{
  "url": "https://www.python.org",
  "finalUrl": "https://www.python.org/",
  "statusCode": 200,
  "title": "Welcome to Python.org",
  "device": "desktop",
  "format": "png",
  "fullPage": true,
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/shot-www.python.org-4863eab2e0.png",
  "width": 1366,
  "height": 2731,
  "pageHeight": 2731,
  "truncated": false,
  "bytes": 463550,
  "loadTimeMs": 3120,
  "warning": null,
  "takenAt": "2026-11-03T14:33:16Z"
}
```

### Pricing

**$1.00 per 1,000 screenshots** (pay per result) plus the usual Apify platform usage. Failed pages are not charged unless you turn on `includeFailed`. Default memory 1 GB handles 2 pages in parallel.

### Limits and honesty

- Public pages only: no login, no paywalls, no CAPTCHA solving, no proxies. Sites that block headless browsers return an error (listed in RUN_SUMMARY).
- Private and internal network addresses are refused.
- Screenshot files stay in the run's key-value store for your Apify data retention period; download them if you need them longer.
- Mobile mode uses a mobile viewport with touch and pixel ratio 3, with the real Chromium User-Agent.

### Use it as an integration (chain it after another actor)

This actor can run automatically after any actor whose results contain a URL.

1. Open the source actor in Apify Console and go to the **Integrations** tab.
2. Click **Add integration** -> **Actor**, pick this actor and set **Dataset** to `{{resource.defaultDatasetId}}` (leave `urls` empty). Set your other options and save.
3. When the source run finishes, this actor reads its dataset and processes every row. The field is detected automatically; set **Dataset field** if your column has another name.

You can also start it by hand with `datasetId` (pick a dataset or paste its ID) to process an existing dataset.

Good sources to chain from:

- compass/crawler-google-places (Google Maps Scraper)
- martin10291/local-business-leads-osm
- apify/website-content-crawler
- martin10291/shopify-store-products
- apify/google-search-scraper

# Actor input Schema

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

Public web pages to capture, one URL per entry (http or https; a bare domain like example.com also works). Login pages and private network addresses are not supported.

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

Viewport preset: desktop 1366x768, desktop-hd 1920x1080, laptop 1280x800, tablet 820x1180 (2x), mobile 390x844 (3x, touch). The real Chromium browser is used for all of them.

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

Capture the whole scrollable page (up to Max height). Off = only the visible viewport, like a thumbnail.

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

PNG (lossless), JPEG (smaller files, set JPEG quality) or PDF (one long page with backgrounds).

## `jpegQuality` (type: `integer`):

Only for JPEG: 10-100. Lower = smaller file.

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

Optional. Overrides the device preset width (320-3840). 0 = use the preset.

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

Optional. Overrides the device preset height (240-2160). 0 = use the preset.

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

Full-page captures are cut at this height (very long pages make huge files). The truncated flag tells you when this happened.

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

When the page counts as loaded: load (default), domcontentloaded (fastest), networkidle (no network for 500 ms, best for heavy JavaScript apps).

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

Wait this long after loading before the capture (animations, charts, fonts). 0-30000.

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

Optional. Wait until this element is visible before the capture, for example #main or .chart-ready.

## `elementSelector` (type: `string`):

Optional CSS selector. When set, only the first matching element is captured (PNG/JPEG), for example header, #pricing or .product-card.

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

Hide common cookie/consent popups (OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast and more) with CSS so they do not cover the page. Nothing is clicked or accepted.

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

Optional CSS selectors to hide before the capture (chat widgets, sticky headers, ads), one per entry.

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

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

## `scrollToLoadImages` (type: `boolean`):

For full-page captures: scroll through the page first so lazy-loaded images appear.

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

Hard limit on URLs captured in one run (also limits what you pay for).

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

How many pages are captured at the same time (1-4). 2 is safe with 1 GB memory; use 4 with 2 GB or more.

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

Give up on a page that does not load within this time. If the page is partly loaded, it is still captured with a warning.

## `onlyNew` (type: `boolean`):

For monitoring: save (and pay for) a screenshot only when the visible text of the page changed since the last run with the same Monitor ID.

## `monitorId` (type: `string`):

Separate memory for different watch lists (used with only-new mode).

## `includeFailed` (type: `boolean`):

When on, pages that fail (HTTP error, timeout, refused address) are also saved with an error message and are charged like normal results. When off, failures are only listed in the RUN_SUMMARY record.

## `datasetId` (type: `string`):

Optional. Pick a dataset (or paste its ID) to read the input from, for example the result of Google Maps Scraper. When this actor runs as an integration, the dataset of the triggering run is used automatically (payload.resource.defaultDatasetId), so you can leave this empty. If set, the values found in the dataset replace the main input list.

## `datasetField` (type: `string`):

Optional. The item field that holds the value, for example website, url or videoUrl (dot paths like author.url work). Empty = detected automatically.

## `datasetMaxItems` (type: `integer`):

Upper limit of unique values taken from the dataset, to keep the cost under control.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://en.wikipedia.org/wiki/Screenshot",
    "https://www.python.org"
  ],
  "device": "desktop",
  "fullPage": true,
  "format": "png",
  "jpegQuality": 80,
  "viewportWidth": 0,
  "viewportHeight": 0,
  "maxHeight": 10000,
  "waitUntil": "load",
  "delayMs": 0,
  "hideCookieBanners": true,
  "hideSelectors": [],
  "darkMode": false,
  "scrollToLoadImages": true,
  "maxUrls": 100,
  "concurrency": 2,
  "timeoutSecs": 30,
  "onlyNew": false,
  "monitorId": "default",
  "includeFailed": false,
  "datasetMaxItems": 1000
}
```

# Actor output Schema

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

One item per captured page: screenshotUrl (direct link to the PNG/JPEG/PDF), title, final URL, status code, size and timing.

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

Key-value store with the image and PDF files and the RUN_SUMMARY record.

# 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/Screenshot",
        "https://www.python.org"
    ]
};

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

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

```

## MCP server setup

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