Website Visual Change Monitor avatar

Website Visual Change Monitor

Pricing

Pay per event

Go to Apify Store
Website Visual Change Monitor

Website Visual Change Monitor

📸 Capture public webpages, compare them with persistent visual baselines, quantify changed pixels, and review highlighted PNG diffs with crop and mask controls.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Stas Persiianenko

Stas Persiianenko

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Website Visual Change Monitor captures public webpages in a controlled Chromium browser, compares each screenshot with its previous baseline, and produces visual evidence that automations can act on.

Track pricing pages, policy notices, landing pages, product layouts, competitor campaigns, and public web QA surfaces without reviewing every page manually.

Each URL produces a dataset record with change metrics plus links to the current screenshot, baseline screenshot, and a highlighted PNG diff.

What does Website Visual Change Monitor do?

The Actor turns a repeated screenshot workflow into structured monitoring data.

  • 🌐 Opens up to 20 public HTTP or HTTPS pages per run.
  • 📸 Captures the viewport or the full scrollable page.
  • 🧭 Uses a fixed viewport, locale, timezone, color scheme, and device scale.
  • 🧹 Masks or hides noisy elements such as clocks, ads, and cookie notices.
  • ✂️ Crops monitoring to a chosen CSS selector when only one region matters.
  • 🔍 Compares PNG pixels with configurable color tolerance.
  • 📊 Reports changed pixels, total pixels, and change percentage.
  • 🚨 Marks whether the meaningful-change threshold was exceeded.
  • 🟥 Generates a highlighted visual overlay showing changed pixels.
  • 📦 Stores images in a key-value store and metadata in the default dataset.
  • ♻️ Updates, freezes, or resets the persistent baseline.

The Actor is a visual monitor, not a text scraper. It detects rendered changes even when the underlying HTML structure is unfamiliar.

Who is it for?

Competitive intelligence teams

Watch public competitor pricing, feature, campaign, and positioning pages. Send only threshold-exceeding results into Slack, email, or a review queue.

Ecommerce and merchandising teams

Record visual changes to public category pages, offers, promotional banners, and product layouts on a recurring schedule.

Compliance and regulatory teams

Keep timestamped visual evidence of public policy, disclosure, terms, or regulatory pages and inspect highlighted differences.

Brand and marketing teams

Monitor important public landing pages for accidental visual changes, removed claims, broken layouts, or campaign replacements.

Web QA and engineering teams

Use stable browser settings, crop selectors, and masks for lightweight visual regression checks against deployed public pages.

Why use this visual change monitor?

A screenshot by itself is difficult to automate.

This Actor adds the state and metadata needed for recurring decisions:

  1. A persistent baseline identifies what the page looked like before.
  2. Pixel comparison quantifies the size of the change.
  3. A configurable threshold separates noise from meaningful updates.
  4. Changed regions point reviewers toward the affected area.
  5. PNG artifacts preserve visual evidence.
  6. Dataset records work with exports, webhooks, schedules, and APIs.

You can run it manually, but the highest-value workflow is a scheduled Actor task with a webhook that reacts only when thresholdExceeded is true.

How the comparison works

Chromium renders every page with these deterministic defaults:

  • viewport: 1280 × 800 CSS pixels;
  • device scale factor: 1;
  • locale: en-US;
  • timezone: UTC;
  • color scheme: light;
  • reduced motion enabled;
  • CSS animations and transitions disabled.

After navigation and an optional settling delay, the Actor captures a PNG.

When a baseline exists, both images are normalized to the same canvas dimensions and compared with pixelmatch.

Changed pixels are highlighted in red on a copy of the current image.

If a page grows or shrinks, the added or removed area contributes to the visual change percentage.

Persistent baselines

A baseline is isolated by normalized URL and scheduled Actor task ID.

For ad-hoc Actor runs, monitorId supplies the stable namespace. Choose a specific identifier and reuse it on later runs.

A successful capture becomes the next baseline when updateBaseline is enabled.

Set updateBaseline to false when every future capture should be compared against one fixed reference.

Set resetBaseline to true after an intentional redesign or when starting a new monitoring series.

Baseline metadata uses capture timestamps to avoid replacing an observed newer baseline with an older capture.

Reduce false positives with masks and crops

Dynamic content can create visual noise even when the important page content has not changed.

Use maskSelectors for elements that should keep their layout but not their changing pixels.

Examples:

  • .current-time
  • [data-testid="ad-slot"]
  • .cookie-banner
  • .rotating-testimonial

Masked elements become a stable black rectangle in both screenshots.

Use hideSelectors when removing an element will not cause unwanted layout reflow.

Use cropSelector to capture only one area, such as:

  • #pricing
  • .product-details
  • main article
  • [data-testid="offer-card"]

A crop selector that matches nothing produces an explicit error record instead of silently capturing the wrong area.

Input

FieldTypeDefaultPurpose
startUrlsarrayrequiredPublic webpage URLs, maximum 20
monitorIdstringrequiredStable namespace for ad-hoc baselines
viewportWidthinteger1280Browser width in CSS pixels
viewportHeightinteger800Browser height in CSS pixels
fullPagebooleantrueCapture the complete scrollable page
waitUntilstringnetworkidleNavigation lifecycle condition
waitAfterLoadMsinteger1000Additional render settling delay
navigationTimeoutSecsinteger45Per-page navigation timeout
cropSelectorstringemptyCapture one matching element
maskSelectorsstring[][]Stable masks for noisy elements
hideSelectorsstring[][]Elements removed before capture
pixelThresholdnumber0.1Pixel color tolerance from 0 to 1
changeThresholdPercentnumber0.1Meaningful-change percentage
resetBaselinebooleanfalseStart a new baseline
updateBaselinebooleantrueAdvance baseline after success
baselineStoreNamestringprovidedAdvanced named KVS override
proxyConfigurationobjectdirectOptional Apify Proxy settings

Example input

{
"startUrls": [
{ "url": "https://distill.io/pricing/" }
],
"monitorId": "distill-pricing-monitor",
"viewportWidth": 1280,
"viewportHeight": 800,
"fullPage": true,
"waitUntil": "networkidle",
"waitAfterLoadMs": 1000,
"maskSelectors": [".cookie-banner"],
"changeThresholdPercent": 0.1,
"updateBaseline": true
}

The first run initializes a baseline and reports zero changed pixels.

A later run with the same task or monitorId performs the comparison.

Output data

One dataset row represents one requested webpage.

FieldDescription
urlNormalized requested URL
finalUrlURL after public redirects
statussuccess or error
titleRendered document title
capturedAtCurrent capture timestamp
baselineCapturedAtCompared baseline timestamp
baselineInitializedWhether this was the first baseline
baselineUpdatedWhether current capture advanced the baseline
viewportRendering dimensions and full-page mode
imageWidth, imageHeightActual PNG dimensions
currentScreenshotUrlCurrent PNG in KVS
baselineScreenshotUrlBaseline PNG in KVS
diffScreenshotUrlRed highlighted diff overlay
changedPixelsNumber of changed pixels
totalPixelsTotal compared canvas pixels
changePercentChanged pixels as a percentage
changeThresholdPercentConfigured meaningful threshold
thresholdExceededWhether the threshold was reached
changeRegionsUp to 20 largest changed bounding regions
errorPer-URL failure explanation

Example output

{
"url": "https://distill.io/pricing/",
"finalUrl": "https://distill.io/pricing/",
"status": "success",
"title": "Start free. Stay free. Scale when ready. - Distill",
"capturedAt": "2026-07-23T04:00:00.000Z",
"baselineCapturedAt": "2026-07-22T04:00:00.000Z",
"baselineInitialized": false,
"baselineUpdated": true,
"changedPixels": 4812,
"totalPixels": 5004800,
"changePercent": 0.096147,
"changeThresholdPercent": 0.1,
"thresholdExceeded": false,
"changeRegions": [
{ "x": 318, "y": 940, "width": 202, "height": 84 }
],
"currentScreenshotUrl": "https://api.apify.com/v2/key-value-stores/.../records/...",
"baselineScreenshotUrl": "https://api.apify.com/v2/key-value-stores/.../records/...",
"diffScreenshotUrl": "https://api.apify.com/v2/key-value-stores/.../records/..."
}

Values above illustrate the output shape; actual metrics depend on the page and baseline.

Schedule a recurring monitor

  1. Save a successful Actor run as an Apify task.
  2. Keep the URL, viewport, crop, mask, and wait settings stable.
  3. Add an hourly, daily, or weekly schedule to the task.
  4. Add a run-succeeded webhook.
  5. Filter the dataset result on thresholdExceeded downstream.
  6. Route the diff image to the responsible reviewer.

Scheduled task IDs automatically isolate baseline namespaces.

Use separate tasks for independent monitoring policies, even when they target the same URL.

Webhooks and alert workflows

The Actor deliberately emits structured state instead of sending alerts to one fixed destination.

This makes it suitable for:

  • Slack alerts through Make or Zapier;
  • email summaries after scheduled runs;
  • compliance evidence pipelines;
  • dataset monitoring dashboards;
  • custom HTTP webhooks;
  • incident or QA ticket creation.

A webhook consumer should check both status and thresholdExceeded.

A successful run can include a per-URL error when other URLs were captured correctly.

How much does it cost to monitor website visual changes?

The Actor uses pay-per-event pricing:

  • Start: $0.005 per run.
  • Visual comparison result: tiered pricing; current BRONZE price is $0.0094431 per URL result.

Higher Apify plans receive lower per-result tier prices.

Browser runtime depends on page load speed, full-page height, and settling delay.

Keep URL batches focused and avoid unnecessary long waits.

The Apify Console shows the exact maximum charge before a run starts.

API usage with JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/website-visual-change-monitor').call({
startUrls: [{ url: 'https://distill.io/pricing/' }],
monitorId: 'distill-pricing-monitor',
changeThresholdPercent: 0.1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].thresholdExceeded, items[0].diffScreenshotUrl);

Reuse an Actor task for scheduled baseline continuity.

API usage with Python

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/website-visual-change-monitor').call(run_input={
'startUrls': [{'url': 'https://distill.io/pricing/'}],
'monitorId': 'distill-pricing-monitor',
'changeThresholdPercent': 0.1,
})
for item in client.dataset(run['defaultDatasetId']).iterate_items():
print(item['status'], item.get('changePercent'))

API usage with cURL

curl -X POST \
"https://api.apify.com/v2/acts/automation-lab~website-visual-change-monitor/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"startUrls": [{"url": "https://distill.io/pricing/"}],
"monitorId": "distill-pricing-monitor",
"changeThresholdPercent": 0.1
}'

Fetch results from the run's defaultDatasetId after it succeeds.

Use with Apify MCP

Claude Code setup

$claude mcp add --transport http apify "https://mcp.apify.com?tools=automation-lab/website-visual-change-monitor"

Claude Desktop, Cursor, and VS Code setup

Add the same HTTP MCP server in Claude Desktop, Cursor, or VS Code. A compatible MCP JSON configuration is:

{
"mcpServers": {
"apify-website-visual-monitor": {
"type": "http",
"url": "https://mcp.apify.com?tools=automation-lab/website-visual-change-monitor"
}
}
}

Example prompts:

  • “Run the website visual change monitor for this public pricing page and summarize any changed regions.”
  • “Show me the latest highlighted diff for my compliance-page monitor.”
  • “Compare these three public campaign pages and list only threshold-exceeding results.”

MCP can start the Actor and inspect its dataset records; image links remain in the result.

Integrations

Google Sheets

Export capture timestamps, change percentages, threshold flags, and artifact URLs for a lightweight audit log.

Slack

Use an Apify webhook plus Make to post the page title, percentage, and highlighted diff when the threshold is exceeded.

Zapier

Trigger a Zap from completed runs and branch on thresholdExceeded before sending notifications.

GitHub or Jira

Create a visual regression issue only when a monitored production page changes beyond policy.

S3 or data warehouses

Copy dataset metadata and referenced image artifacts into long-term evidence storage according to your retention policy.

Tips for reliable visual monitoring

  • Keep viewport settings identical across baseline and comparison runs.
  • Prefer crop selectors when only one component matters.
  • Mask timestamps, ads, rotating content, live counters, and personalization.
  • Add enough render delay for fonts and client-side layout, but no more than needed.
  • Use networkidle for application pages and load for pages with perpetual analytics requests.
  • Treat font or browser-version upgrades as a reason to reset a sensitive baseline.
  • Use one scheduled task per independent monitor.
  • Choose a threshold from observed page noise instead of defaulting every page to zero.
  • Review a diff before automating a high-impact action.

Errors and troubleshooting

Why did the first run report no changes?

The first successful run initializes the baseline. Run the same scheduled task or reuse the same monitorId to compare later.

Why is every run a large change?

Check viewport consistency and inspect the page for ads, timestamps, cookie banners, animations, or personalized content. Add mask selectors or crop to a stable region.

Why did navigation time out?

Try waitUntil: "load" or "domcontentloaded", reduce the extra delay, or increase navigationTimeoutSecs within the schema limit.

Why did a crop fail?

The selector matched no elements after rendering. Verify it in browser developer tools and ensure the target is not inside a closed shadow root or authenticated page.

Why did the Actor reject the page?

The Actor detects common login redirects, CAPTCHAs, and challenge pages. It supports anonymously accessible public pages only.

Security and privacy

Do not submit private dashboard URLs, session cookies, passwords, or authenticated browser state.

monitorId should be specific and not contain secrets.

Screenshot artifacts can contain whatever is publicly rendered at the requested URL.

Apply an appropriate KVS retention policy and limit access to run storage when captures are sensitive to your organization.

The Actor does not attempt to bypass login requirements.

Only monitor pages you are legally permitted to access and process.

Respect website terms, applicable copyright, privacy rules, and reasonable request frequency.

Visual monitoring does not grant rights to republish captured content.

Use schedules that avoid unnecessary load on target websites.

This tool is designed for public-page observation, QA, compliance, and business intelligence—not access-control circumvention.

Limitations

Rendering can vary because of upstream personalization, A/B tests, fonts, geography, consent state, or browser changes.

Pixel comparison identifies visual difference; it does not explain business meaning automatically.

Changed regions are compact bounding bands, not semantic DOM elements.

The Actor does not monitor authenticated or private pages.

A maximum of 20 URLs is accepted per run to keep browser workloads controlled.

Concurrent runs use timestamp-aware baseline updates, but users should avoid overlapping schedules for the same monitor when strict ordering matters.

Use Webpage Structured Data Monitor when you need typed text or numeric field changes rather than pixel-level visual evidence.

Browse other public automation tools from automation-lab for extraction and monitoring workflows.

FAQ

Can I monitor more than one URL?

Yes. Add up to 20 URL entries. The Actor emits one result per URL.

Can I compare against a permanent design reference?

Yes. Initialize the desired baseline, then set updateBaseline to false.

Can I force a new baseline after an intentional release?

Yes. Run once with resetBaseline: true.

Can I monitor only the pricing table?

Yes. Set cropSelector to a stable selector such as #pricing or .pricing-grid.

Can I ignore a live counter?

Yes. Add its CSS selector to maskSelectors.

Does it send alerts itself?

It produces automation-ready results. Use Apify schedules and webhooks to send alerts to your chosen system.

Are screenshots downloadable?

Yes. Dataset records contain KVS URLs for current, baseline, and highlighted diff PNG files.

Does it work with single-page applications?

Usually, if the public page renders in Chromium. Adjust waitUntil and waitAfterLoadMs so the layout settles.

Does it support logins?

No. V1 intentionally supports public anonymous pages only.

What happens when one URL fails?

That URL emits an error record while other URLs continue. If all URLs fail, the run exits non-zero so monitoring systems do not mistake failure for no change.