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
- Render the page and take the visible text of
selector(defaultbody). - Remove noise: relative times ("5 minutes ago", "just now"), ISO timestamps and clock times, view and visitor counters, extra whitespace, plus your
ignorepatterns. - 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 keepscreenshot = trueand review them when text changes. hideapplies to the SnapRender renderer's screenshots only; scope its text withselector.
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)
| File | Size | Uploaded | |
|---|---|---|---|
| pagequiet-0.1.0.tar.gz | 15.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|