Skip to main content

pagequiet

Website change detection that stays quiet until something real changes.

Most change monitors compare HTML, so they alert on every rotating ad slot, CSRF token and "updated 3 minutes ago". After a week nobody reads the alerts. pagequiet compares what a reader sees instead: the rendered text of one element on the page, with relative times, counters and whitespace stripped. When that changes, you get the changed lines and a full-page screenshot of the new version.

$ pagequiet check
CHANGED example-com-pricing https://example.com/pricing
  - Pro $49 / month
  + Pro $59 / month
  screenshot: .pagequiet/captures/example-com-pricing-20261005T080012Z.png
1 changed, 11 unchanged, 0 new, 0 errors

It is a single command with a TOML file, built for cron and CI: exit code 1 means something changed.

Install

pip install "pagequiet[playwright]"
playwright install chromium

Python 3.11 or later. The Playwright extra renders pages with a local Chromium and costs nothing. If you would rather not run a browser (CI runners, small servers), see Renderers.

Quick start

pagequiet init        # writes pagequiet.toml
# edit the [[page]] entries
pagequiet check       # first run records a baseline
pagequiet check       # later runs report changes

Configuration

[settings]
renderer = "playwright"           # or "snaprender"
state = ".pagequiet/state.json"   # last text and fingerprint per page
captures = ".pagequiet/captures"  # full-page screenshot of each new version
notify = ["stdout", "slack"]      # stdout, slack, webhook
timeout = 45                      # seconds per page
use_default_noise = true          # strip relative times, timestamps, view counters

[[page]]
url = "https://example.com/pricing"
selector = "main"                          # watch one element, not the whole page
ignore = ['Offer ends in \d+ days']        # extra regex patterns removed before comparing
hide = [".testimonial-carousel"]           # hidden before text and screenshot

[[page]]
url = "https://example.com/legal/terms"
selector = "article"
screenshot = false                         # text only

Notifiers read their targets from the environment:

Notifier Variable Sends
stdout none the changed lines and screenshot path
slack PAGEQUIET_SLACK_WEBHOOK a message to a Slack incoming webhook
webhook PAGEQUIET_WEBHOOK_URL JSON: {"event": "page.changed", "name", "url", "changes", "screenshot"}

Exit codes: 0 nothing changed, 1 at least one page changed, 2 errors and no changes.

What counts as a change

  1. Render the page and take the visible text of selector (default body).
  2. Remove noise: relative times ("5 minutes ago", "just now"), ISO timestamps and clock times, view and visitor counters, extra whitespace, plus your ignore patterns.
  3. Hash the result. Same hash as last run: quiet. Different: report the changed lines and take a screenshot.

The defaults are deliberately few. Every pattern you strip is a kind of change you will never be told about, so add ignore patterns only after you have seen one cause a false alarm. If timestamps on a page are meaningful to you, set use_default_noise = false.

The biggest single improvement is selector. Watching main or the pricing table instead of the whole page ignores navigation, footers and "latest posts" widgets that change for reasons you do not care about.

A failed render or an empty selector is reported as an error and never as a change, and the previous text is kept, so a flaky site does not produce a false alarm the next time it loads.

Renderers

playwright (default) runs Chromium locally through Playwright. Free, private, and fine for most pages. Cookie consent banners are part of the page, so if one varies between visits, scope selector to the content or add the banner to hide.

snaprender uses the hosted SnapRender API to render pages, so no browser runs on your machine. Cookie banners and ads are removed before the text and screenshot are taken. It needs SNAPRENDER_API_KEY; the free plan covers 200 renders a month. Each check is one render, plus one when a page changed. Disclosure: I build SnapRender. pagequiet works fully without it and always will.

Run it on a schedule

Cron, every six hours:

0 */6 * * * cd /opt/watch && pagequiet check -q >> pagequiet.log 2>&1

GitHub Actions, keeping state in the repository: see examples/github-actions.yml. Scheduled workflows run at most every five minutes, can start late at busy times, and are paused in public repositories after 60 days without activity; committing the state file each run counts as activity.

Limits

  • Pages behind a login are not supported.
  • Text comparison misses purely visual changes (a new image, a broken layout). Compare the screenshots in captures/ for those, or keep screenshot = true and review them when text changes.
  • hide applies to the SnapRender renderer's screenshots only; scope its text with selector.

Development

python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev,playwright]"
pytest

MIT licensed.

Metadata

Release files for pagequiet 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pagequiet 0.1.0
File Size Uploaded
pagequiet-0.1.0.tar.gz 15.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pagequiet 0.1.0
File Interpreter ABI Platform
pagequiet-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.0 kB

Release files / pagequiet-0.1.0.tar.gz

Download URL pagequiet-0.1.0.tar.gz
Size 15.2 kB
Tags Source
SHA-256 checksum
How to use checksums
04ec832c726ce564a1bbc31cbab261afdaaafc345bb4ac3f72bad3f066308339
BLAKE2b-256 checksum
How to use checksums
3cf927904a07c4de2c64195d74e8ecb256d8a2709dbc95c94c74afd4cfeb1796
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / pagequiet-0.1.0-py3-none-any.whl

Download URL pagequiet-0.1.0-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ac2729594a3a41ffa3d1d836730f2448ab6b4c87c8d2fc47b476ad16a897e122
BLAKE2b-256 checksum
How to use checksums
343c8eeedcd1c525b394b755a95b5eac2da7b06f4f2200131eb1ca79b1396377
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page