Website Screenshot & Visual Change Monitor avatar

Website Screenshot & Visual Change Monitor

Pricing

$2.00 / 1,000 successful page checks

Go to Apify Store
Website Screenshot & Visual Change Monitor

Website Screenshot & Visual Change Monitor

Monitor website visual changes and capture stable PNG screenshots in bulk. Compare every successful render with the last good baseline, get pixel-change ratios, highlighted diffs and changed regions, and send webhook-ready results for QA, ecommerce, compliance and competitor tracking.

Pricing

$2.00 / 1,000 successful page checks

Rating

0.0

(0)

Developer

Vadim Bezrukov

Vadim Bezrukov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Monitor website visual changes between runs with pixel-level comparisons, highlighted diffs, changed regions and webhook-ready events. The same Actor is a website screenshot API for stable PNG captures of one public page or a batch.

Save a monitor input as an Apify Task and run it on a schedule. The first successful run creates a BASELINE; every later complete capture returns UNCHANGED or VISUAL_CHANGE against the last successful capture.

A timeout, blocked page, incomplete render or storage failure never becomes “unchanged” and never replaces a good baseline.

What you get

  • PNG viewport or full-page website screenshots;
  • stable desktop, mobile, locale, timezone and motion settings;
  • bounded lazy-load scrolling, font/image settling and animation suppression;
  • ignoreSelectors for noisy content without layout reflow;
  • hideSelectors for banners or widgets that should be removed;
  • deterministic pixel-change ratio and changed-region bounding boxes;
  • optional highlighted diff PNG on meaningful changes;
  • one explicit result for every requested URL, including invalid, failed and blocked pages;
  • only the two rolling screenshot slots and one diff per monitoring configuration—no hidden screenshot-history database.

Who uses this website visual change monitor?

  • QA and engineering teams comparing production pages after a release;
  • ecommerce teams tracking product, pricing and checkout-page changes;
  • compliance teams keeping timestamped visual evidence of public disclosures;
  • agencies monitoring client landing pages and campaign assets;
  • competitive-intelligence workflows that need a machine-readable change event, not just an image.

Ready-made example Tasks

Quick start

1. Basic screenshot

{
"urls": [{"url": "https://example.com", "externalId": "homepage"}],
"mode": "snapshot",
"fullPage": false
}

2. Full-page screenshot

{
"urls": [{"url": "https://example.com/pricing"}],
"mode": "snapshot",
"fullPage": true,
"scrollForLazyLoad": true
}

3. Batch screenshots

{
"urls": [
{"url": "https://example.com", "externalId": "home"},
{"url": "https://example.com/pricing", "externalId": "pricing"},
{"url": "https://example.com/terms", "externalId": "terms"}
],
"mode": "snapshot"
}

One bad URL does not stop the others. Three inputs always produce three dataset records unless the whole Actor runtime cannot start.

4. Mobile viewport

{
"urls": [{"url": "https://example.com"}],
"mode": "snapshot",
"viewport": {"width": 390, "height": 844},
"fullPage": true
}

Viewport and capture settings are part of the monitor state identity. Mobile and desktop captures never share a baseline.

5. Monitor mode

{
"urls": [{"url": "https://example.com/pricing", "externalId": "competitor-pricing"}],
"mode": "monitor",
"fullPage": true,
"changeThreshold": 0.01
}

Run the same saved Task again. changeThreshold is a fraction: 0.01 means one percent of the normalized common image canvas.

6. Visual diff

{
"urls": [{"url": "https://example.com/landing"}],
"mode": "monitor",
"changeThreshold": 0.005,
"saveDiffImage": true
}

When the ratio reaches the threshold, the dataset row contains VISUAL_CHANGE, changed_regions, and a diff_image_url with changed pixels highlighted in red.

{
"urls": [{"url": "https://example.com"}],
"mode": "monitor",
"ignoreSelectors": [".live-clock", "[data-testid='ad-slot']"],
"hideSelectors": ["#cookie-banner", ".chat-widget"]
}

Ignored elements keep their layout space but their content is invisible. Hidden elements use display:none and can cause layout reflow. Prefer a precise selector over a high global threshold. The Actor intentionally has no fragile built-in database of banner selectors.

8. Daily schedule

  1. Run a monitor input successfully once.
  2. In Apify Console, choose Save as task.
  3. Open Schedules, create a daily schedule, and select that Task.
  4. Keep URL, viewport, full-page, wait, delay and selector settings stable.

Monitor mode uses the persistent named KVS website-screenshot-visual-monitor-state, so independent Task runs find the last successful baseline. Snapshot images and each run's RUN_SUMMARY remain in that run's default storage. A new capture configuration safely creates a separate BASELINE instead of comparing unlike screenshots.

9. Webhook on VISUAL_CHANGE

Attach an ACTOR.RUN.SUCCEEDED webhook to the scheduled Task. In the receiver, fetch the run dataset and act only on rows satisfying both conditions:

const changed = datasetItems.filter(
(item) => item.status === 'SUCCESS' && item.change_type === 'VISUAL_CHANGE',
);

Never branch on changed alone: failure rows deliberately use changed: null. The diff URL and changed regions make each matching row usable as an alert payload without comparing two exports downstream.

Input reference

FieldDefaultMeaning
urlsrequired1–500 public HTTP(S) pages, with optional externalId
modesnapshotsnapshot does not touch monitor state; monitor compares successful captures
fullPagetrueFull scrollable page or viewport only
viewport1440 × 900Deterministic CSS-pixel viewport
waitUntilnetworkidlenetworkidle, load, or domcontentloaded
delayMs500Extra render-settling time, 0–30 seconds
navigationTimeoutMs45000Hard per-attempt navigation limit
scrollForLazyLoadtrueBounded scroll before full-page capture
ignoreSelectors[]Preserve layout while hiding selected content
hideSelectors[]Remove selected elements before capture
changeThreshold0.01Changed-pixel fraction required for VISUAL_CHANGE
saveDiffImagetrueSave one highlighted PNG for a meaningful change
proxyConfigurationdirectOptional user-controlled Apify/custom proxy

Browser pool size, concurrency, Chromium arguments and retry timing are not Store inputs. They are conservative internal runtime choices.

Output and failure semantics

Every row carries source, stable source_id, source_url, scraped_at, schema_version and fingerprint, plus screenshot/change fields.

Statuses:

  • SUCCESS — navigation, rendering, screenshot and KVS write all completed;
  • PARTIAL — something rendered, but completeness or storage could not be verified;
  • FAILED — navigation/browser/source failed after bounded retry where sensible, or the check was rejected before capture by a too-low run spending limit;
  • BLOCKED — HTTP 401/403/407/429, CAPTCHA or a recognizable challenge page;
  • INVALID_URL — rejected before browser access, including private/local targets.

Change types exist only on successful monitor captures:

  • BASELINE — first success for this URL and capture configuration;
  • UNCHANGED — changed-pixel ratio is below changeThreshold;
  • VISUAL_CHANGE — ratio reached the threshold.

Full-page dimension changes are normalized onto a common canvas. Added or removed width/height counts as change even when the added region is blank.

See examples/sample_output.json for a complete change event.

API use

Use the standard Apify Actor API with your own Actor ID and token. Never place a token in source control.

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("YOUR_USERNAME/website-screenshot-visual-monitor").call(
run_input={
"urls": [{"url": "https://example.com", "externalId": "homepage"}],
"mode": "monitor",
"changeThreshold": 0.01,
}
)
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())

Use with AI agents through Apify MCP

Expose the Actor as a typed MCP tool:

https://mcp.apify.com?tools=automa-flow/website-screenshot-visual-monitor

Example prompt:

Run automa-flow/website-screenshot-visual-monitor in monitor mode for these
public product-page URLs. Report only VISUAL_CHANGE rows above a 1% threshold,
include changed regions and screenshot/diff URLs, and keep failed captures
separate from unchanged pages.

The first successful monitor run creates a BASELINE. A blocked or partial capture never becomes UNCHANGED and never replaces the last good baseline.

Pricing

The publication price is $0.002 per successful page check ($2 per 1,000). A page check includes the persisted PNG, and in monitor mode it also includes comparison with the last successful baseline, changed-region detection and the optional highlighted diff image. There is no start fee.

Successful page checksActor charge
1$0.002
100$0.20
1,000$2.00

An unchanged monitor check is billable because the page was rendered, persisted and compared. Invalid URLs, blocked pages, partial captures, failed captures and retries are not charged. Platform usage is included in this pay-per-event price; the Apify Console price shown at run time is canonical. If a run's maximum total charge cannot cover every potentially successful URL in the batch, the Actor stops before opening Chromium, writes one free BILLING_LIMIT_TOO_LOW result per otherwise valid URL and fails the run. No monitor baseline is advanced without a deliverable paid result. A batch containing only invalid URLs succeeds with one explicit free INVALID_URL row per input because those are per-item outcomes, not a system failure.

Reliability, proxy and measured infrastructure cost

The Actor reuses one Chromium process, creates an isolated context per URL and runs at internal concurrency 2. Every HTTP(S) navigation and page resource is checked against local, private and reserved networks, and service workers are disabled so they cannot bypass that guard. Direct access is the default. Enable a proxy only when you are permitted to access the page and direct anonymous access is insufficient. The Actor does not automatically escalate to residential proxies and never attempts universal anti-bot bypass.

The 2026-09-01 30-site runtime probe in the pinned Apify image measured:

  • Chromium startup: 0.934 seconds;
  • average viewport capture: 5.406 seconds; p95: 11.752 seconds;
  • peak container memory: 1.01 GiB;
  • average PNG: 241.6 KiB; maximum: 729.8 KiB;
  • 24 complete public pages and 6 explicit 401/403 blocked pages;
  • one false change above 1% among 24 accessible pages (4.17%), caused by a highly dynamic Booking.com render.

The deterministic 10/100/500 benchmark in the final image measured these 500-URL cases at concurrency 2:

ScenarioWall timePeak RSSCore compute at 2 GiBBounded KVS bytes
Viewport snapshots38.3 s824 MiB0.0213 CU8.44 MB
Full-page snapshots139.0 s946 MiB0.0772 CU18.00 MB
Unchanged monitor checks58.2 s828 MiB0.0324 CU8.62 MB
Changed checks with diff199.8 s877 MiB0.1110 CU23.33 MB

At the current Free/Starter platform rate of $0.20/CU, the measured core compute scales to about $0.009, $0.031, $0.013 and $0.044 per 1,000 URLs respectively. Those are lower bounds: KVS/Dataset operations, timed storage, external website traffic, browser startup, retries and proxy usage are separate. At current operation rates, an exact unchanged check additionally needs two KVS reads, one state write and one Dataset row; a changed+diff check needs two reads, three KVS writes and one Dataset row. See Apify pricing.

The default memory is therefore 2 GiB. Use selectors for dynamic sites and review the first diff before automating a high-impact response. Run-level metrics are stored in RUN_SUMMARY, including billable and charged page-check counts.

This Actor is for public webpages only. Do not submit private dashboards, credentials, cookies, authenticated profiles or URLs carrying secrets. It does not bypass login, paywalls, CAPTCHA, account permissions or access controls.

Website terms and robots policies differ. You are responsible for having the right to access, capture, process and retain each page. A screenshot can contain public personal data or copyrighted material; visual monitoring grants no right to republish it.

Monitoring storage is bounded per URL/configuration to two rolling screenshots, one diff and compact state in the persistent named monitoring KVS. Dataset observations remain history-ready, but the Actor does not build a screenshot-history store. Pixel-identical checks reuse the existing PNG and update only successful-check metadata, avoiding a duplicate KVS artifact write.

Known limitations

  • Pixel comparison detects visible differences, not their business meaning.
  • Personalization, A/B tests, ads, live counters and geography can create genuine pixel changes; use precise selectors and an observed threshold.
  • Font/browser upgrades can change rendering. The pinned image reduces this risk; an intentional runtime/configuration change re-baselines safely.
  • Full-page captures above 50,000 CSS pixels in height or 30 million rendered pixels return PARTIAL/PAGE_TOO_LARGE instead of risking an out-of-memory run.
  • Closed shadow DOM, cross-origin frames and canvas animations cannot always be selectively ignored with CSS selectors.
  • Login-required and strongly protected pages are intentionally unsupported.

FAQ and troubleshooting

Can I use this as a website screenshot API?

Yes. Run it through the Apify API with one URL or a batch of up to 500 URLs. Each input produces an explicit dataset row on an ordinary run, and screenshot files are stored in the Actor's key-value store.

How does visual change detection work?

The first successful monitor run creates a baseline. Later runs normalize the two PNG canvases, compare pixels and report UNCHANGED or VISUAL_CHANGE based on changeThreshold. Failed or incomplete checks never replace the baseline.

Why does a page report changes every day?

Ads, clocks, cookie banners, rotating content, personalization and A/B tests are real pixel changes. Add precise ignoreSelectors or hideSelectors, keep the viewport stable and raise the threshold only after reviewing representative diffs.

Why is a screenshot blocked or partial?

BLOCKED means the site returned an authorization/rate-limit response or a recognizable challenge. PARTIAL means the Actor could not prove a complete, persisted capture. The Actor does not bypass login, CAPTCHA or access controls; use a permitted proxy only when direct access is insufficient.

Local development

docker build --target test -t website-screenshot-visual-monitor:test .
docker run --rm website-screenshot-visual-monitor:test
docker build -t website-screenshot-visual-monitor .
apify run

CI tests use only generated images and a local HTML fixture server. The separate manual live benchmark is under experiments/website-screenshot-visual-monitor/ and never contacts third-party sites from CI.