Change monitoring

Know the moment
a page changes

Check the text of any page on your own schedule, compare it with the last run, and keep a full-page screenshot of every version that differs. Cookie banners and ads are removed before capture, so the diff is about the page, not the overlay.

200 renders a month free, no card. Every feature on every plan.

Try it on your URL

Live capture, no signup. You get the screenshot and the exact API call that produced it.

Why this is harder than it looks

Change monitors that compare raw HTML fire on every rotating ad slot, CSRF token and timestamp in the markup. After a week of false alarms nobody reads the alerts. Comparing the rendered text of the page, after scripts have run and consent pop-ups are gone, removes most of that noise.

Self-hosting the browser that renders those pages is the part that breaks. A headless Chromium on a cron box leaks memory, fails silently on sites that need a real user agent, and captures the consent dialog instead of the content. The job keeps running and the archive fills with screenshots of cookie banners.

Hosted monitoring tools solve that, and they charge per monitored page. That is the right trade for a marketing team watching ten pages. It gets expensive for a developer who wants to watch a few hundred URLs and route changes into their own systems.

Who does what

SnapRender is the capture layer. The schedule and the history stay in your systems, which keeps your data where your policies say it should be.

SnapRender does

  • Render the page in real Chromium with ads and cookie banners removed
  • Return the visible text from GET /v1/extract?type=text, ready to hash and diff
  • Capture a full-page PNG of the changed version, fresh every time (caching is off unless you turn it on)
  • Run batches of up to 50 URLs and call your webhook when the batch finishes

You wire up

  • Pick the schedule: cron, GitHub Actions, a cloud scheduler or n8n
  • Keep the previous hash or image so each run has something to compare against
  • Decide what counts as a change and where the alert goes

The code

Two approaches cover most monitoring. A text hash is cheap and ignores rendering noise, so it is the right first signal. A pixel diff catches layout and image changes the text misses. Start with text, add pixels where they matter.

monitor.py: text hash with a screenshot on change
Python
import hashlib, json, os, pathlib, datetime, requests

API = "https://app.snap-render.com"
HEADERS = {"X-API-Key": os.environ["SNAPRENDER_API_KEY"]}
URLS = ["https://example.com/pricing", "https://example.com/terms"]
STATE = pathlib.Path("state.json")
ARCHIVE = pathlib.Path("captures")

state = json.loads(STATE.read_text()) if STATE.exists() else {}
ARCHIVE.mkdir(exist_ok=True)

for url in URLS:
    r = requests.get(f"{API}/v1/extract", headers=HEADERS, timeout=60,
                     params={"url": url, "type": "text"})
    r.raise_for_status()
    text = r.json()["content"]
    digest = hashlib.sha256(text.encode()).hexdigest()

    if state.get(url) == digest:
        continue  # unchanged since the last run

    stamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    shot = requests.get(f"{API}/v1/screenshot", headers=HEADERS, timeout=90,
                        params={"url": url, "format": "png", "full_page": "true"})
    shot.raise_for_status()
    name = ARCHIVE / f"{url.split('//')[1].replace('/', '_')}-{stamp}.png"
    name.write_bytes(shot.content)

    if url in state:  # first run records a baseline, later runs alert
        print(f"CHANGED {url} -> {name}")
    state[url] = digest

STATE.write_text(json.dumps(state, indent=2))

Each URL costs one extract call per run, plus one screenshot only when the text changed. The first run records a baseline without alerting.

pixel-diff.mjs: visual change percentage
Node.js
// npm install pngjs pixelmatch
import fs from 'node:fs';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';

const url = 'https://example.com';
const params = new URLSearchParams({ url, format: 'png', width: '1280', height: '800' });
const res = await fetch(`https://app.snap-render.com/v1/screenshot?${params}`, {
  headers: { 'X-API-Key': process.env.SNAPRENDER_API_KEY },
});
if (!res.ok) throw new Error(`capture failed: ${res.status}`);
const current = PNG.sync.read(Buffer.from(await res.arrayBuffer()));

if (fs.existsSync('previous.png')) {
  const previous = PNG.sync.read(fs.readFileSync('previous.png'));
  const { width, height } = current;
  const diff = new PNG({ width, height });
  const changed = pixelmatch(previous.data, current.data, diff.data, width, height, { threshold: 0.1 });
  const percent = (changed / (width * height)) * 100;
  console.log(`${percent.toFixed(2)}% of pixels changed`);
  if (percent > 1) fs.writeFileSync('diff.png', PNG.sync.write(diff));
}
fs.writeFileSync('previous.png', PNG.sync.write(current));

Viewport captures at a fixed 1280 x 800 keep both images the same size, which pixel diffing needs; full-page heights change from run to run.

.github/workflows/monitor.yml: run it every six hours
YAML
name: page-monitor
on:
  schedule:
    - cron: "0 */6 * * *"
  workflow_dispatch:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install requests
      - run: python monitor.py
        env:
          SNAPRENDER_API_KEY: ${{ secrets.SNAPRENDER_API_KEY }}
      - name: Keep state and captures in the repo
        run: |
          git config user.name "page-monitor"
          git config user.email "[email protected]"
          git add state.json captures
          git commit -m "monitor run" || echo "no changes"
          git push

GitHub Actions is a free scheduler for this. Committing state.json between runs gives the next run its baseline and keeps a dated history of every change.

Every parameter is in the API reference and the OpenAPI spec. Errors come back as { "error": { "code", "message", "status" } }; a 429 with QUOTA_EXCEEDED means the monthly allowance is used.

What it costs at your volume

A text check is one call per page per run, and screenshots only happen on change. Fifty pages checked daily is about 1,500 calls a month, which fits Starter at $9. Hourly checks on the same 50 pages are about 36,000 a month, which is Business territory.

About 0 renders a month

Free

Start free

A month counts as 30 days. Failed captures are refunded. Yearly billing on paid plans is ten months for twelve. All plans.

Get a key and run the first recipe today

Every script on this page works with a free key: 200 renders a month, every feature included, no card. Your first capture takes about two minutes.

Paid plans from $9 for 2,000 renders. Yearly billing is ten months for twelve. Failed captures are refunded.

Frequently asked questions

Should I compare text or screenshots to detect changes?+

Start with text. GET /v1/extract?type=text returns what a reader sees after scripts run, so hashing it ignores markup noise such as tokens and ad slots. Add a pixel diff for pages where layout, images or colors are what you care about.

Does SnapRender run the schedule for me?+

Not today. SnapRender is the capture layer: your cron job, GitHub Actions workflow, cloud scheduler or n8n flow calls the API on the schedule you choose, and you keep the history where you want it.

Will I get stale results from a cache?+

No. Caching is off by default, so every request renders the page fresh. Batch jobs always render fresh too. Turn caching on with cache=true only for repeat requests where an older copy is acceptable.

How do I avoid false alarms from cookie banners and ads?+

Both are removed before capture by default (block_cookie_banners and block_ads are on). For other dynamic areas, such as a rotating testimonial, pass hide_selectors with a CSS selector and that element is hidden before the text or image is taken.

How many pages can I monitor on the free plan?+

The free plan includes 200 renders a month. That covers six pages checked daily with text hashes, or about forty pages checked weekly, before you need Starter at $9 for 2,000.

Related

Other use cases

Your first capture takes two minutes

200 renders a month free, no card. The API key is on the next screen.