Website Visual Change Monitor
Pricing
Pay per event
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
Maintained by CommunityActor 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:
- A persistent baseline identifies what the page looked like before.
- Pixel comparison quantifies the size of the change.
- A configurable threshold separates noise from meaningful updates.
- Changed regions point reviewers toward the affected area.
- PNG artifacts preserve visual evidence.
- 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-detailsmain article[data-testid="offer-card"]
A crop selector that matches nothing produces an explicit error record instead of silently capturing the wrong area.
Input
| Field | Type | Default | Purpose |
|---|---|---|---|
startUrls | array | required | Public webpage URLs, maximum 20 |
monitorId | string | required | Stable namespace for ad-hoc baselines |
viewportWidth | integer | 1280 | Browser width in CSS pixels |
viewportHeight | integer | 800 | Browser height in CSS pixels |
fullPage | boolean | true | Capture the complete scrollable page |
waitUntil | string | networkidle | Navigation lifecycle condition |
waitAfterLoadMs | integer | 1000 | Additional render settling delay |
navigationTimeoutSecs | integer | 45 | Per-page navigation timeout |
cropSelector | string | empty | Capture one matching element |
maskSelectors | string[] | [] | Stable masks for noisy elements |
hideSelectors | string[] | [] | Elements removed before capture |
pixelThreshold | number | 0.1 | Pixel color tolerance from 0 to 1 |
changeThresholdPercent | number | 0.1 | Meaningful-change percentage |
resetBaseline | boolean | false | Start a new baseline |
updateBaseline | boolean | true | Advance baseline after success |
baselineStoreName | string | provided | Advanced named KVS override |
proxyConfiguration | object | direct | Optional 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.
| Field | Description |
|---|---|
url | Normalized requested URL |
finalUrl | URL after public redirects |
status | success or error |
title | Rendered document title |
capturedAt | Current capture timestamp |
baselineCapturedAt | Compared baseline timestamp |
baselineInitialized | Whether this was the first baseline |
baselineUpdated | Whether current capture advanced the baseline |
viewport | Rendering dimensions and full-page mode |
imageWidth, imageHeight | Actual PNG dimensions |
currentScreenshotUrl | Current PNG in KVS |
baselineScreenshotUrl | Baseline PNG in KVS |
diffScreenshotUrl | Red highlighted diff overlay |
changedPixels | Number of changed pixels |
totalPixels | Total compared canvas pixels |
changePercent | Changed pixels as a percentage |
changeThresholdPercent | Configured meaningful threshold |
thresholdExceeded | Whether the threshold was reached |
changeRegions | Up to 20 largest changed bounding regions |
error | Per-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
- Save a successful Actor run as an Apify task.
- Keep the URL, viewport, crop, mask, and wait settings stable.
- Add an hourly, daily, or weekly schedule to the task.
- Add a run-succeeded webhook.
- Filter the dataset result on
thresholdExceededdownstream. - 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 osfrom apify_client import ApifyClientclient = 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
networkidlefor application pages andloadfor 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.
Legal and ethical use
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.
Related actors
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.