# Website Screenshot API - Bulk, Full Page & Visual Change Alerts (`afterstar/website-screenshots`) Actor

Screenshots of many web pages (desktop or mobile, full page), and alerts when a page visually changes.

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

## Pricing

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

**Pricing: $3.00 per 1,000 screenshots, $7.00 per 1,000 full-page screenshots, in every mode.**

### What does Website Screenshots do?

For each URL: a screenshot in headless Chrome, desktop (1366×768) or mobile (iPhone size), the visible area or the full page, PNG or JPEG, saved with a download link in each row, plus the final URL, HTTP status and page title. In 'New, changed and removed' mode it's a visual change monitor: a page counts as changed when its look moves past a threshold (perceptual hash) on two runs in a row, and the row links the old and the new screenshot.

Run it once, or **schedule it to alert you when a page really changes how it looks**. The tool remembers what it has seen, in a storage in your own Apify
account.

### What you get

|  |  |
|---|---|
| **Shots** | Desktop (1366×768) or mobile (iPhone size); the visible area or the full page; PNG or JPEG |
| **Per URL** | A download link to the screenshot, the final URL, HTTP status and page title |
| **Full page** | Scrolls first so lazy images load; up to 10,000 px tall (taller pages are capped and say so) |
| **Change alerts** | 'Changes' mode compares how each page looks (perceptual hash) and links the old and new shot |
| **Price** | $3.00 per 1,000 screenshots, $7.00 per 1,000 full-page screenshots; failed pages are free |

### Use cases

- Archive how your or your competitors' pages look, weekly.
- Get alerted when a page visually changes (pricing pages, landing pages, partner sites).
- Screenshots for reports, QA and link previews.

### Input

| Field | What it does |
|---|---|
| URLs | Pages, one per line |
| Devices | Desktop, mobile or both |
| Full page | The whole page instead of the visible area |
| Format | PNG or JPEG |
| Wait after load | Extra wait for late content (default 1 s) |
| Hide these elements | Optional CSS selectors (like `#chat-widget`) hidden before the shot |
| Hide common cookie banners | Hides the consent boxes of the common cookie tools before the shot (off by default) |
| Change threshold | How different a page must look to count as changed (default 12) |
| Output mode | All results, only new results, or new + changed + removed results |

### Output

One row per screenshot:

```json
{
  "id": "shot:https://example.com/|desktop|0",
  "changeType": "changed",
  "url": "https://example.com/",
  "device": "desktop",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/…/records/shot-….png",
  "width": 1366,
  "height": 768,
  "partlyLoaded": null,
  "capped": null,
  "pageHeight": null,
  "visualDistance": 31,
  "previousScreenshotUrl": "https://api.apify.com/v2/key-value-stores/…/records/shot-….png"
}
```

Every row has every key (`null` when there's no value). `changeType` is `new`, `changed`, `unchanged` or
`removed`; a `changed` row lists `changedFields` and the `previous` values. If a source fails or rate-limits the
request, you get one row with `changeType: "status"` and a `statusCode` instead of an error, and it's free.

### Schedule it

1. Fill in the input and **save it as a task**.
2. Run it once with **Baseline only** switched on: it remembers the current results and lists nothing. Or skip this
   and accept that the first scheduled run lists everything once.
3. Set **Output mode** to **Only new results** or **New, changed and removed**, and add a **schedule**.
4. Connect an **integration** (Slack, Google Sheets, Zapier, Make, n8n), or put a **webhook URL** in the input.

Each saved task keeps its own memory; switching a task between modes keeps it. Each run saves a summary (`OUTPUT` in
the run's key-value store) with the counts.

**Apify webhook template** (Integrations → Webhook → payload):

```json
{"datasetUrl": "https://api.apify.com/v2/datasets/{{resource.defaultDatasetId}}/items?clean=true&format=json"}
```

### How much does it cost?

- **$3.00 per 1,000 screenshots** of the visible area, **$7.00 per 1,000 full-page screenshots** (on paid Apify plans 10-30% less). In 'changes' mode you pay for every screenshot checked, but only the changes are listed.
- **Example:** 1,000 screenshots cost $3.00 (full page: $7.00); a daily screenshot of 50 pages costs about $4.50 a month (full page: about $10.50), plus Apify's start fee: $0.00010 per run.
- Status rows (errors) are free. Set a spending limit on the run and the tool stops there.

### Good to know

- You pay per screenshot taken; pages that fail to load (or answer an error) are free.
- Each shot has 18 seconds in total (full page: 35 seconds). A slow page is captured as soon as its content is there, even if trackers are still loading; a page that can't be captured in time gets a free 'timed out' row.
- Full-page shots scroll down the page first, so images that load on scroll appear, and wait up to 4 seconds for images still loading. If the page couldn't be scrolled to the end or images were still loading, the shot is taken anyway and the row says `partlyLoaded: true`.
- If a pop-up locks the page so its lower part never renders, the tool undoes the lock (the pop-up stays in the picture). A full-page shot that still comes out blank below the first screen is retried once; if it's still blank, it's attached free with an error saying so.
- **Full-page shots are at most 10,000 pixels tall** (on mobile, 5,000 page pixels at 2x). A taller page is captured from the top down to that height, and the row says `capped: true` with the page's real height in `pageHeight`. Most pages fit; long news front pages and articles (such as BBC, 13,000 pixels, or Wikipedia's 'United States', 76,000 pixels) are capped.
- Pop-ups and cookie banners appear as a first-time visitor sees them, unless you hide them: 'Hide common cookie banners'
  hides the consent boxes of the common cookie tools (OneTrust, Cookiebot, Didomi, Usercentrics and others; never
  accepted, only hidden), and 'Hide these elements' hides anything else you name. Banners made another way may stay.
- Screenshot links are stored in the run's storage; download them if you need them long term.
- A visual change needs to hold on two runs in a row, so a one-off pop-up or rotating banner doesn't alert you.

### How it compares

| Instead of | With this tool |
|---|---|
| Taking screenshots by hand | A whole list in one run, desktop or mobile, visible area or full page |
| A monthly screenshot plan | Pay per screenshot taken; nothing when you don't run it |
| Change monitors that compare page text | Compares how the page looks, and a change must hold on two runs in a row, so a one-off pop-up doesn't alert you |

### FAQ

**What sizes can I get?** Desktop (1366×768) or mobile (iPhone size), the visible area or the full page, as PNG or JPEG.

**How tall can a full-page shot be?** Up to 10,000 pixels (on mobile 5,000 page pixels at 2x). A taller page is captured from the top down to that height, and the row says `capped: true` with the page's real height.

**What about cookie banners and pop-ups?** By default they appear as a first-time visitor sees them. Switch on 'Hide common cookie banners' to hide the consent boxes of the common cookie tools (hidden, never accepted), and list anything else, like a chat widget, in 'Hide these elements'. If a pop-up locks the page so it can't scroll, the tool undoes the lock.

**How do change alerts work?** Set Output mode to 'New, changed and removed results' and schedule it. A page counts as changed when its look moves past a threshold on two runs in a row, and the row links the old and the new screenshot.

**Am I charged when a page fails?** No. Pages that don't load, answer an error or time out give a free row.

**How long are the screenshots kept?** They're in the run's storage in your Apify account, under Apify's data retention; download them if you need them for longer.

**Source:** the pages you provide, rendered in headless Chrome (Playwright). Check only sites you're allowed to.

### More Afterstar tools

- [Website Tech Stack Detector - CMS, Analytics & Frameworks](https://apify.com/afterstar/tech-stack-detector): Shows which technologies (shop system, CMS, analytics, frameworks) each website in your list uses, and what changed.

Found a problem or need a field? Open an issue on the Issues tab.

# Actor input Schema

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

One page URL per line.

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

Take each screenshot as a desktop, a phone, or both.

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

Capture the whole scrolling page, not just the visible area (at most 10,000 pixels tall; a taller page is cut there and the row says capped). Priced as a full-page screenshot.

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

The image format.

## `waitMs` (type: `integer`):

Extra wait after the page loads, for late content (max 8,000). Each shot has 18 seconds in total (full page: 35 s).

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

Optional: elements to hide before the shot, one CSS selector per line (like #chat-widget or .newsletter-popup).

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

Hide the consent boxes of the common cookie tools (OneTrust, Cookiebot, Didomi, Usercentrics and others) before the shot. They're only hidden, never accepted; banners made another way may stay.

## `changeThreshold` (type: `integer`):

In 'New, changed and removed' mode: how different (perceptual-hash distance, 0-256) a page must look, on two runs in a row, to count as changed.

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

The most URLs per run.

## `outputMode` (type: `string`):

'All' lists everything found now. 'Only new' lists results never seen in earlier runs. 'New, changed and removed' also lists results that changed or disappeared. The first scheduled run counts everything as new; use 'Baseline only' to start cheaply.

## `baselineOnly` (type: `boolean`):

Saves what exists now without listing it, so the next scheduled run shows only what's new. Charged like a run with nothing new.

## `stateKey` (type: `string`):

Leave empty: each saved task keeps its own memory. Set a name to share one memory between tasks, or a new name to start over.

## `resetState` (type: `boolean`):

Starts from an empty memory: every result counts as new again.

## `webhookUrl` (type: `string`):

We POST a summary plus up to 100 new, changed or removed results here when something changed. Works with Slack incoming webhooks (choose the Slack format), Zapier, Make and n8n.

## `webhookFormat` (type: `string`):

JSON for automation tools; 'Slack message' sends one line of text.

## `webhookAlways` (type: `boolean`):

By default the webhook is called only when something changed or a source failed.

## Actor input object example

```json
{
  "urls": [
    "https://example.com"
  ],
  "devices": [
    "desktop"
  ],
  "fullPage": false,
  "format": "png",
  "waitMs": 1000,
  "hideCookieBanners": false,
  "changeThreshold": 12,
  "maxItems": 500,
  "outputMode": "all",
  "baselineOnly": false,
  "stateKey": "",
  "resetState": false,
  "webhookUrl": "",
  "webhookFormat": "json",
  "webhookAlways": false
}
```

# Actor output Schema

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

No description

## `files` (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://example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("afterstar/website-screenshots").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://example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("afterstar/website-screenshots").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://example.com"
  ]
}' |
apify call afterstar/website-screenshots --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afterstar/website-screenshots"
        }
    }
}
```

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/5CnnkP11jxkgv9Gwk/builds/6RS0qE6gToDRvD3VT/openapi.json
