# Website Screenshot API — URL to PNG, JPEG, PDF | $3.50/1k (`glasswing/website-screenshot-api`) Actor

Screenshot any list of URLs: PNG or JPEG plus optional PDF, desktop/laptop/tablet/mobile viewports, full-page mode, wait for selector or delay, cookie banners and ads hidden. Returns image link, final URL, HTTP status, title, timings.

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

## Pricing

from $3.15 / 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.
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

### What does Website Screenshot API do?

Website Screenshot API turns a list of URLs into **screenshots**: one PNG or JPEG per page, plus an optional A4 PDF. Paste the URLs, pick a viewport (desktop, laptop, tablet or mobile), choose whether you want the visible window or the **full page**, and run. Every screenshot is saved to the run's key-value store, and the dataset gets one row per URL with a direct **image link**, the final URL after redirects, the HTTP status code, the page title and the load timings.

It works as a **screenshot API** for agents, scripts and no-code tools. You need no API key of your own and no browser to manage. Each page opens in a fresh headless Chromium tab. Cookie and consent banners are hidden with CSS, and ad and tracker requests are skipped, so the image shows the page content.

Only the URLs you list are opened. Nothing is crawled, logged into or clicked. A page that shows an anti-bot challenge or a login form is captured as it appears and flagged. The Actor never tries to get past it.

### Use cases for this website screenshot tool

- **Visual monitoring.** Schedule a daily run over your own pages, landing pages or competitors' pricing pages, and keep a dated image of each one.
- **SEO and marketing reports.** Add homepage and SERP-landing screenshots to client reports and audits.
- **QA and responsive checks.** Capture the same URL at desktop, tablet and mobile sizes after each deploy.
- **Archiving and evidence.** Keep a PNG plus a PDF of a page as it looked at a given time, with its status code and final URL.
- **Thumbnails and link previews.** Generate preview images for directories, bookmarks and link lists.
- **AI agents.** Give a vision model a screenshot of a page instead of raw HTML.

### What data you get for every URL

| Field | Type | Description |
|---|---|---|
| `screenshotUrl` | string | Direct link to the image in the run's key-value store (PNG or JPEG) |
| `pageTitle` | string | Page `<title>` after rendering |
| `finalUrl` | string | URL after redirects |
| `statusCode` | integer | HTTP status of the main document |
| `redirected` | boolean | `true` when the final URL differs from the requested one |
| `metaDescription` | string | The page's meta description |
| `format` | string | `png` or `jpeg` |
| `fileSizeBytes` | integer | Size of the image in bytes |
| `screenshotKey` | string | Record key of the image in the key-value store |
| `pdfUrl` | string | Link to the A4 PDF (when **Also save a PDF** is on) |
| `pdfKey` | string | Record key of the PDF |
| `viewport` | string | `desktop`, `laptop`, `tablet`, `mobile` or `custom` |
| `viewportWidth` | integer | Viewport width in CSS pixels |
| `viewportHeight` | integer | Viewport height in CSS pixels |
| `deviceScaleFactor` | number | Pixel ratio (2 = retina) |
| `fullPage` | boolean | Whether the whole scrollable page was captured (`false` when a full-page capture was too heavy and the visible window was saved instead) |
| `pageHeight` | integer | Full height of the page in CSS pixels |
| `capturedHeight` | integer | Height captured (viewport height, or page height up to the maximum) |
| `challengeDetected` | boolean | `true` when the page showed an anti-bot challenge (Cloudflare, DataDome, PerimeterX, AWS WAF, Akamai) |
| `loginWallDetected` | boolean | `true` when the page showed a login form or redirected to a login page |
| `wallReason` | string | Which challenge or login wall was detected |
| `cookieBannersHidden` | integer | Cookie/consent elements hidden with CSS |
| `waitForSelector` | string | The CSS selector the Actor waited for, if any |
| `selectorFound` | boolean | Whether that selector became visible within 15 s |
| `loadTimeMs` | integer | Milliseconds until the HTML arrived |
| `totalTimeMs` | integer | Milliseconds for the whole capture |
| `url` | string | The URL you asked for |
| `status` | string | `ok`, `not_found` or `error` (see below) |
| `error` | string | Reason when `status` is not `ok` |
| `scrapedAt` | string | ISO 8601 time of the capture |

The images themselves are in the run's **Storage > Key-value store** tab, named `screenshot-0001-<domain>.png` and so on. The `screenshotUrl` links open them directly, with no Apify token needed.

#### Result status (tri-state output)

| `status` | Meaning | Billed? |
|---|---|---|
| `ok` | The page loaded and its screenshot was saved. A login page is still `ok`, with `loginWallDetected: true`. | Yes |
| `not_found` | The page answered HTTP 404 or 410. The screenshot of the error page is attached. | No |
| `error` | The page did not load (DNS, timeout), answered another HTTP error, or showed an anti-bot challenge. The screenshot of what was shown is attached when there was one. | No |

### How to take website screenshots with this Actor

1. Click **Try for free** to open the Actor in Apify Console.
2. Paste your URLs into **URLs**, one per line. Bare domains such as `apify.com` work too.
3. Pick a **Viewport preset**, and switch on **Full-page screenshot** if you want the whole page.
4. Click **Start**. Rows appear in the dataset as pages are captured. Open the **Overview** tab to see the images inline, or export the rows as JSON, CSV or Excel.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array of strings | 5 example pages | URLs to capture, one per line |
| `viewport` | string | `desktop` | `desktop` 1920x1080, `laptop` 1366x768, `tablet` 820x1180, `mobile` 390x844 at 2x |
| `viewportWidth` / `viewportHeight` | integer | - | Custom size that overrides the preset |
| `deviceScaleFactor` | integer | preset | 1, 2 (retina) or 3 |
| `fullPage` | boolean | `false` | Capture the whole scrollable page (scrolls once first to load lazy images) |
| `maxPageHeight` | integer | `10000` | Cut full-page screenshots at this height in pixels |
| `format` | string | `auto` | `auto` (PNG for window shots, JPEG for full-page shots), `png` or `jpeg` |
| `jpegQuality` | integer | `80` | JPEG quality, 10-100 |
| `savePdf` | boolean | `false` | Also save an A4 PDF of each page (no extra charge) |
| `waitUntil` | string | `load` | `load`, `domcontentloaded` (faster) or `networkidle` (for dashboards and charts) |
| `waitForSelector` | string | - | Wait until this CSS selector is visible (up to 15 s) |
| `delayMs` | integer | `0` | Extra pause before the screenshot, up to 30 s |
| `hideCookieBanners` | boolean | `true` | Hide common consent banners with CSS. Nothing is clicked. |
| `blockAds` | boolean | `true` | Skip requests to about 100 ad, analytics and tag-manager networks, plus video streams |
| `hideSelectors` | array of strings | - | Extra CSS selectors to hide (chat widgets, sticky headers) |
| `scrollToLoad` | boolean | `true` | Scroll once before a full-page capture so lazy images render |
| `navigationTimeoutSecs` | integer | `30` | Page load timeout; a slow page that already answered is captured as it is |
| `maxItems` | integer | `100` | Stop after this many URLs |
| `proxyConfiguration` | object | off | Optional Apify Proxy (datacenter) |

Desktop screenshots of a few pages:

```json
{
    "startUrls": ["https://apify.com", "python.org", "https://en.wikipedia.org/wiki/Web_scraping"]
}
```

Full-page mobile screenshots as JPEG, with a PDF, waiting for a chart to render:

```json
{
    "startUrls": ["https://example.com/dashboard"],
    "viewport": "mobile",
    "fullPage": true,
    "format": "jpeg",
    "savePdf": true,
    "waitForSelector": "canvas.chart",
    "delayMs": 1000
}
```

### Output

A real row from a test run on 2026-10-19:

```json
{
    "url": "https://www.wikipedia.org/",
    "status": "ok",
    "scrapedAt": "2026-10-19T10:13:55.885Z",
    "finalUrl": "https://www.wikipedia.org/",
    "statusCode": 200,
    "pageTitle": "Wikipedia",
    "metaDescription": "Wikipedia is a free online encyclopedia, created and edited by volunteers around the world and hosted by the Wikimedia Foundation.",
    "screenshotUrl": "https://api.apify.com/v2/key-value-stores/357607d0-778f-4e27-811c-27d3a8c45232/records/screenshot-0002-wikipedia.org.png",
    "screenshotKey": "screenshot-0002-wikipedia.org.png",
    "format": "png",
    "fileSizeBytes": 148582,
    "viewport": "desktop",
    "viewportWidth": 1920,
    "viewportHeight": 1080,
    "deviceScaleFactor": 1,
    "fullPage": false,
    "pageHeight": 1101,
    "capturedHeight": 1080,
    "redirected": false,
    "challengeDetected": false,
    "loginWallDetected": false,
    "cookieBannersHidden": 0,
    "loadTimeMs": 615,
    "totalTimeMs": 1759
}
```

A page behind an anti-bot wall comes back like this, free of charge, with the screenshot of the challenge attached:

```json
{
    "url": "https://www.example-shop.com/",
    "status": "error",
    "error": "DataDome challenge shown instead of the page (HTTP 403); it was not circumvented. The screenshot of what was shown is attached; this row is free.",
    "statusCode": 403,
    "challengeDetected": true,
    "wallReason": "DataDome challenge"
}
```

### How much does a website screenshot cost?

Pay per result: **$3.50 per 1,000 screenshots**, plus a start fee of $0.005 per GB of run memory ($0.02 per run at the default 4 GB). The PDF option, full-page mode, cookie-banner hiding and ad blocking are all included. Pages that fail, answer 404 or show an anti-bot challenge are free.

| Event | Price | Charged when |
|---|---|---|
| `apify-actor-start` | $0.005 per GB of memory | Once per run, when it starts. |
| `result` | $0.0035 | Each URL whose screenshot was saved with status `ok`. |

| Screenshots | Cost (default 4 GB run) |
|---|---|
| 100 | $0.37 |
| 1,000 | $3.52 |
| 10,000 | $35.02 |

Browser compute is included. The Actor stops gracefully when your run's maximum charge is reached.

### Integrations, exports and API access

Call it from the Apify API, the JavaScript or Python client, the Apify CLI, or on a **Schedule** for daily visual monitoring. Send the rows to Google Sheets, Airtable, Slack, Make, n8n or Zapier, and copy the images to your own storage from `screenshotUrl`. AI agents can call it through the Apify MCP server and read the image links from the dataset.

### Limitations

- **Anti-bot walls are not circumvented.** Some large sites (for example those behind Cloudflare, DataDome or PerimeterX challenges) show a challenge page to cloud browsers. You get a free `error` row with `challengeDetected: true` and the screenshot of the challenge. No CAPTCHA is solved and no challenge is waited out.
- **No logins.** Pages behind a login show the login page, flagged with `loginWallDetected`.
- Cookie banners are hidden with CSS for the common consent platforms. An uncommon banner can still show; add its selector to **Extra CSS selectors to hide**.
- Full-page screenshots stop at **Maximum full-page height** (10,000 px by default). Endless feeds and very long articles are cut there. If a very long page is too heavy to render in one piece, the visible window is saved instead and the row says `fullPage: false`.
- Very heavy pages can take 20-60 seconds each, because the page is fully rendered before the screenshot.
- Images are kept as long as the run's storage is retained (see your Apify plan's data retention). Copy them elsewhere if you need them permanently.
- One URL gives one screenshot. Use a separate run, or list the URL again with another viewport, for more sizes.

### FAQ

**Do I need an API key or a browser?** No. The Actor runs headless Chromium on Apify. You only provide URLs.

**How do I get the image files?** Click a `screenshotUrl` link, open the run's key-value store, or download the records with the Apify API. The links work without an Apify token.

**Can I capture a mobile version of a page?** Yes. Pick the `mobile` viewport (390x844 at 2x with an iPhone user agent and touch), or set your own width and height.

**Why does a page show a "Just a moment..." or "Access denied" screen?** The site's bot protection blocked the cloud browser. The row is free and flagged with `challengeDetected`. The Actor never tries to get past such pages.

**Does it follow links or crawl a site?** No. Only the URLs you list are opened, each once.

**Why are ads missing from my screenshots?** **Block ads and trackers** is on by default because ads slow pages down. Turn it off to capture the page with its ads.

**Can I use it from an AI agent or MCP client?** Yes. It runs with no input at all, every row is self-describing (`status` plus `error`), and the image links work without a token.

### Related Actors

- [Domain Intelligence Report](https://apify.com/glasswing/domain-intelligence-report) - use it next to a screenshot to check the site's DNS, mail setup, TLS certificate, registration and tech stack.
- [PDF Text Extractor](https://apify.com/glasswing/pdf-text-extractor) - use it to pull the text out of PDFs, including the ones this Actor saves.
- [Google Ads Transparency Scraper](https://apify.com/glasswing/google-ads-transparency-scraper) - use it to see which ads a company runs, then screenshot its landing pages.
- [Keyword Suggestions Scraper](https://apify.com/glasswing/keyword-suggestions-scraper) - use it to find the search terms whose landing pages you want to monitor.

### Legal notice

This Actor opens only the URLs you provide and saves what a normal browser shows for them. You are responsible for having the right to capture and store those pages, and for how you use the images, including any copyright, trademark or personal data they contain. The Actor does not log in, solve CAPTCHAs or circumvent access controls. It is not affiliated with any website it captures.

# Changelog

This Actor's version history is a separate document: https://apify.com/glasswing/website-screenshot-api/changelog.md

# Actor input Schema

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

Web pages to capture, one per line. Bare domains such as apify.com are accepted (https:// is added). Each URL is opened once in a fresh browser tab.

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

Browser window to emulate: desktop 1920x1080, laptop 1366x768, tablet 820x1180 (touch, iPad user agent) or mobile 390x844 at 2x (touch, iPhone user agent).

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

Optional. Overrides the preset width (240-3840).

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

Optional. Overrides the preset height (240-4320).

## `deviceScaleFactor` (type: `integer`):

Optional. 1 = normal, 2 = retina (twice the pixels, bigger files), 3 = maximum. Default comes from the preset (mobile 2, others 1).

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

Capture the whole scrollable page instead of only the visible window. The page is scrolled once first so lazy-loaded images appear.

## `maxPageHeight` (type: `integer`):

Full-page screenshots are cut at this height so endless feeds stay a sensible size.

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

Auto (default) saves PNG for window screenshots and JPEG for full-page ones (full-page PNGs are 5-10x bigger). PNG is lossless; JPEG is much smaller for photo-heavy pages.

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

10-100, used whenever a JPEG is saved.

## `savePdf` (type: `boolean`):

Also print each page to an A4 PDF (with backgrounds) and return its URL in pdfUrl. Included in the price.

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

When the page counts as loaded: load (default), domcontentloaded (faster) or networkidle (waits up to 10 s for the network to go quiet; best for dashboards and charts).

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

Optional. Wait until an element matching this CSS selector is visible (up to 15 s) before the screenshot. The row reports selectorFound.

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

Optional pause after loading and before the screenshot, for animations (0-30000).

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

Hide the consent banners of common cookie platforms (OneTrust, Cookiebot, Usercentrics, Didomi, Quantcast, TrustArc, Sourcepoint and others) with CSS. Nothing is clicked or accepted.

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

Skip requests to about 100 ad, analytics and tag-manager networks (Google Ads, Taboola, Outbrain, Criteo, Hotjar and others) plus video streams. Pages load faster and screenshots show the content without ad slots filling in. Turn off to capture ads too.

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

Optional. CSS selectors of elements to hide before the screenshot (chat widgets, sticky headers, ads).

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

For full-page screenshots, scroll down once before capturing so lazy-loaded images and sections render.

## `navigationTimeoutSecs` (type: `integer`):

How long to wait for a page to load (5-60). A page that answered but is still loading is captured as it is.

## `maxItems` (type: `integer`):

Stop after this many URLs. Each saved screenshot with status ok is one billable result; failed pages are free.

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

Optional. Off by default: pages are opened directly from Apify's servers. Enable Apify Proxy (datacenter) to capture from other IPs or countries.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.python.org/",
    "https://www.wikipedia.org/",
    "https://apify.com/",
    "https://en.wikipedia.org/wiki/Web_scraping",
    "https://www.iana.org/help/example-domains"
  ],
  "viewport": "desktop",
  "fullPage": false,
  "maxPageHeight": 10000,
  "format": "auto",
  "jpegQuality": 80,
  "savePdf": false,
  "waitUntil": "load",
  "delayMs": 0,
  "hideCookieBanners": true,
  "blockAds": true,
  "scrollToLoad": true,
  "navigationTimeoutSecs": 30,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [
        "https://www.python.org/",
        "https://www.wikipedia.org/",
        "https://apify.com/",
        "https://en.wikipedia.org/wiki/Web_scraping",
        "https://www.iana.org/help/example-domains"
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("glasswing/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 = {
    "startUrls": [
        "https://www.python.org/",
        "https://www.wikipedia.org/",
        "https://apify.com/",
        "https://en.wikipedia.org/wiki/Web_scraping",
        "https://www.iana.org/help/example-domains",
    ],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("glasswing/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 '{
  "startUrls": [
    "https://www.python.org/",
    "https://www.wikipedia.org/",
    "https://apify.com/",
    "https://en.wikipedia.org/wiki/Web_scraping",
    "https://www.iana.org/help/example-domains"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call glasswing/website-screenshot-api --silent --output-dataset

```

## MCP server setup

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