Website Screenshot & Visual Change Monitor
Pricing
$2.00 / 1,000 successful page checks
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
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;
ignoreSelectorsfor noisy content without layout reflow;hideSelectorsfor 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
- Monitor website visual changes with screenshot diffs for a reusable baseline and highlighted visual changes;
- Monitor competitor product page visual changes for ecommerce and competitive-intelligence workflows;
- Capture full-page website screenshots in bulk for URL lists and downloadable PNG evidence;
- Capture mobile website screenshots for responsive QA with a fixed 390 × 844 viewport.
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.
7. Ignore a cookie banner or live counter
{"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
- Run a
monitorinput successfully once. - In Apify Console, choose Save as task.
- Open Schedules, create a daily schedule, and select that Task.
- 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
| Field | Default | Meaning |
|---|---|---|
urls | required | 1–500 public HTTP(S) pages, with optional externalId |
mode | snapshot | snapshot does not touch monitor state; monitor compares successful captures |
fullPage | true | Full scrollable page or viewport only |
viewport | 1440 × 900 | Deterministic CSS-pixel viewport |
waitUntil | networkidle | networkidle, load, or domcontentloaded |
delayMs | 500 | Extra render-settling time, 0–30 seconds |
navigationTimeoutMs | 45000 | Hard per-attempt navigation limit |
scrollForLazyLoad | true | Bounded scroll before full-page capture |
ignoreSelectors | [] | Preserve layout while hiding selected content |
hideSelectors | [] | Remove selected elements before capture |
changeThreshold | 0.01 | Changed-pixel fraction required for VISUAL_CHANGE |
saveDiffImage | true | Save one highlighted PNG for a meaningful change |
proxyConfiguration | direct | Optional 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 belowchangeThreshold;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 osfrom apify_client import ApifyClientclient = 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 thesepublic product-page URLs. Report only VISUAL_CHANGE rows above a 1% threshold,include changed regions and screenshot/diff URLs, and keep failed capturesseparate 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 checks | Actor 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:
| Scenario | Wall time | Peak RSS | Core compute at 2 GiB | Bounded KVS bytes |
|---|---|---|---|---|
| Viewport snapshots | 38.3 s | 824 MiB | 0.0213 CU | 8.44 MB |
| Full-page snapshots | 139.0 s | 946 MiB | 0.0772 CU | 18.00 MB |
| Unchanged monitor checks | 58.2 s | 828 MiB | 0.0324 CU | 8.62 MB |
| Changed checks with diff | 199.8 s | 877 MiB | 0.1110 CU | 23.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.
Security, legal use and retention
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_LARGEinstead 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:testdocker 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.