You want to know the moment a competitor drops their price, a supplier edits their terms, or your own marketing page silently breaks after a deploy. Refreshing the tab by hand does not scale, and the obvious automated fix, diffing the raw HTML, fires on every timestamp and ad slot.
By the end of this you will have a three part loop, capture the page on a schedule, diff the images, alert on change, where the only moving part is one HTTP call. I will also draw a hard line between this and visual regression testing, because they look similar and solve different problems.
What is website change detection?
Website change detection is the practice of capturing a page at intervals and comparing each capture against the previous one so you are told automatically when the page changes. The reliable version of this compares visual output, a rendered screenshot, rather than source code, because a screenshot is what a human would actually look at and notice. If two screenshots of the same URL are pixel-for-pixel identical, nothing visible changed. If they differ, something did.
The loop has exactly three jobs. Something has to render the page to an image on a schedule. Something has to compare the new image to the last known good one. Something has to tell you when the comparison crosses a threshold. Keep those three jobs separate and the whole system stays easy to reason about. The hard part is the first job, because rendering a real page reliably is where most DIY setups fall over.
That is the job a screenshot API exists to do. ScreenshotRender takes a URL and returns a hosted PNG rendered by a real Chromium, with cookie banners and ads stripped before the shot, so the only thing left to compare is the content you care about. You own the schedule and the diff; the render stops being your problem. The rest of this post builds the loop around that call.
Why does diffing raw HTML fail for change detection?
Diffing raw HTML fails because most of what changes in the markup is noise that no human would ever see. Pull the same page twice, a minute apart, and the bytes almost never match: there is a fresh CSRF token, a rotated ad slot, a build hash in an asset URL, a relative timestamp that ticked over, an analytics nonce. A naive text diff treats every one of those as a change and pages you at 3am for nothing.
Here is the bad version, so the good one feels earned. You fetch the HTML, run a string diff, and alert on any difference. Within a day you have either muted the alert because it cries wolf, or you are maintaining a growing list of regexes to scrub tokens, nonces, and timestamps out of the markup before comparing. That scrubbing list is never finished, because the next site you monitor invents a new nonce field.
Visual diffing sidesteps all of it. Render both versions to a screenshot and compare the pixels, and a rotated invisible token produces zero pixel difference, while a real price change from $49 to $59 lights up immediately. You compare what renders, not what ships over the wire. That is why every serious change detector, and the closely related field of visual regression testing, works on images rather than source.
How do you capture a stable screenshot on a schedule?
Capture the page with a single GET request and let an external scheduler decide when to fire it. The request itself is one line: https://screenshotrender.com/api/v1/screenshot?apiKey=YOUR_API_KEY&url=https://news.ycombinator.com&fullPage=true. That returns JSON with a hosted screenshot URL you download and store as the new capture. The fullPage=true parameter captures the entire scrollable document, not just the viewport, which matters when the change you are watching for sits below the fold.
Two details make the capture stable, which is the whole game for change detection. First, the same renderer every time: ScreenshotRender runs Chromium on fixed infrastructure, so the same URL produces the same baseline output run to run, instead of drifting when your laptop updates its fonts. Second, a settle window for client-rendered pages: add wait=2000 to pause two seconds after load so a single page app has finished painting before the shot. A capture that is not stable produces diffs that are really just rendering jitter.
The schedule lives outside the API, because ScreenshotRender renders, it does not run cron for you. Pick whichever scheduler you already run:
- A cron job. A line like
0 9 * * *in crontab calls your capture script every morning at 9. Cheapest option if you already have a box. - A GitHub Actions schedule. A workflow with an
on: scheduletrigger runs the capture and the diff on GitHub's runners, with no server of your own to keep alive. - An n8n workflow. If you would rather not own a cron box at all, the same loop runs as a three node n8n workflow: a Schedule Trigger, an HTTP Request node calling ScreenshotRender, and a destination node.
Whichever you pick, the output is the same: a fresh, comparable PNG sitting next to the last one, ready to diff.
Skip the Chromium build, the cron box, and the version drift.
ScreenshotRender renders every capture on a real Chromium for you, with cookie banners and ads already stripped, so the same URL produces the same comparable PNG run after run. Cached results are served from edge CDN and you pay only for successful requests, which keeps a polling loop cheap. 100 free screenshots per month, no credit card.
Get an API keyHow do you diff two screenshots to catch a change?
Run the new screenshot and the previous one through an image diff library and act on the number of pixels that differ. pixelmatch is the standard open-source choice: you call pixelmatch(imgA, imgB, diffOutput, width, height, { threshold: 0.1 }) and it returns the count of differing pixels and writes a third image highlighting exactly where they changed. That highlight image is gold for the alert, because it shows you the change at a glance instead of just telling you one happened.
There are two numbers to tune, and people confuse them. The threshold option, 0 to 1, controls per-pixel color sensitivity: how different two pixels must be before pixelmatch counts them as changed. The default of 0.1 absorbs antialiasing noise on text edges without missing real changes. Separately, you decide what share of changed pixels should actually fire the alert, and 0.1 percent of total pixels is a sane starting tolerance for a full page capture. Below that, you are almost certainly looking at rendering jitter, not a real edit.
Two screenshots have to be the same dimensions for a pixel diff to run, which is why a stable capture matters so much, and why a fixed full page render beats an ad-hoc browser. If you need raw speed on large images, odiff is a faster native alternative, and for perceptual comparisons that ignore tiny shifts there is SSIM, but pixelmatch with a tuned tolerance covers the large majority of monitoring jobs. Pick the lightest tool that gives you zero false positives over a week of known-good runs.
How do you get alerted when a website changes?
Wire the diff result to wherever you will actually see it, which is the one hop you have to build yourself because no renderer does it for you. The shape of this depends on where the loop runs. In a cron script, compare the changed-pixel count to your tolerance and, on a real change, send an email, POST to a Slack incoming webhook, or open a ticket. In a GitHub Actions job, call process.exit(1) when the diff exceeds tolerance so the workflow goes red and GitHub emails you, and attach the pixelmatch highlight image as a build artifact so the alert carries proof.
The detail that turns a noisy alert into a useful one is sending the diff image, not just a "something changed" message. A notification that shows the before, the after, and the highlighted region lets you decide in two seconds whether it matters. A notification that just says a page changed sends you back to the site to hunt for what moved, which is the manual work you were trying to delete.
After a confirmed change, promote the new screenshot to the baseline so the next run compares against current reality. Forget this step and every run after a real change keeps re-firing on the same diff. That single line, replace the baseline once you have acknowledged the change, is what keeps the loop quiet between real events.
When does website change detection produce false positives?
It produces false positives whenever the page changes visually without the underlying content changing, and the honest move is to know these cases before they wake you up. The usual suspects:
- Rotating content. Carousels, "featured" widgets, randomized testimonials, and live counters repaint on every load. Either hide them with a tighter capture region, or raise the pixel tolerance enough to absorb the moving area.
- A/B tests and personalization. The page legally differs between two loads because the site is serving variants. No diff threshold fixes this; you are watching a page that has no single canonical state.
- Animations and lazy media. A capture taken mid-animation or before lazy images load differs from one taken after. The
waitparameter and a full page render that scrolls the document both reduce this, but cannot eliminate a page that animates forever. - Login walls. A URL-only renderer captures the logged-out view. If the content you want to monitor sits behind authentication, change detection on the public URL will only ever see the login page, and you need a different approach entirely.
None of these mean the approach is broken. They mean you tune the tolerance and the capture to the specific page, then trust the loop. A monitor that fires only on changes that matter is worth far more than one that fires on everything and gets muted in a week.
Common questions about website change detection
How do I monitor a website for changes?
Capture the page on a schedule, compare each new capture against the last known good one, and alert when they differ by more than a set tolerance. The most reliable comparison is visual: render the page to a screenshot and diff the pixels, because that maps to what a human would notice. A cron job or a GitHub Actions schedule drives the timing, a screenshot API like ScreenshotRender renders each shot, and a library like pixelmatch does the diff.
Is website change detection the same as visual regression testing?
No. Visual regression testing runs in CI against your own app and is triggered by a pull request, to stop you shipping a UI break. Website change detection runs on a schedule against a live external page you do not control, to tell you when someone else changed something. The capture-and-diff mechanics are nearly identical, but the trigger, the target, and the reader differ, which is why the visual regression testing guide is a separate read.
How often should I check a page for changes?
Match the interval to how fast the page actually changes and how quickly you need to know. A competitor's pricing page or a legal document changes rarely, so once or twice a day is plenty. A stock or availability page might warrant every few minutes. Polling more often than the page changes just burns requests, so start hourly or daily and tighten only if you are missing changes that matter.
Can I detect changes on a Cloudflare protected page?
Only if your renderer gets past the bot challenge first. A stock headless browser is served a Cloudflare challenge instead of the real content, so either every capture looks identical and the diff never fires, or the challenge itself changes and you get false alerts. ScreenshotRender's Stealth Mode, bundled on the Hobby plan and above, returns the real page so the diff compares actual content. See the guide to screenshotting Cloudflare protected sites for why this happens.
What is the cheapest way to monitor a website for changes?
Self-host the schedule and the diff, which are free, and pay only for the rendering. A cron job or a GitHub Actions schedule costs nothing, pixelmatch is open source, and ScreenshotRender's free plan covers 100 screenshots per month with no credit card. One daily check of a single page is about 30 renders a month, well inside the free tier. If you monitor many pages or need Stealth Mode, the Hobby plan is $10 per month billed annually with 2,000 screenshots.
The takeaway: change detection is not a product you buy so much as a loop you assemble, and only one of its three jobs is genuinely hard. Hand the rendering to an API that returns a stable, comparable PNG every time, keep the schedule and the diff in tools you already run, and you get a monitor that pings you the moment a page changes and stays silent the rest of the time.



