Updated 9 May 2026 · ~3 min read · built from each project's reference docs
webchanges vs urlwatch: a comparison
Both tools watch URLs, browser-rendered pages, and shell-command output for changes and notify you. webchanges
does everything urlwatch does — same jobs, filters, and reporter model — and four high-value things
urlwatch can't.
Bottom line: Pick webchanges if you need any of:
TLS/JA3 fingerprint evasion for anti-bot sites, AI-summarized diffs of long
documents, image change detection, or structural JSON/XML diffs without false
positives. Or one of the many other additions, and a tool that is actively developed.
Release activity in the last 12 months
Source: GitHub releases on
mborsetti/webchanges and tags on
thp/urlwatch, as of 9 May 2026.
The 4 high-value wins
If any of these match what you actually monitor, urlwatch can't keep up. Each is a webchanges feature with no
native urlwatch equivalent.
1Get past TLS/JA3 fingerprinting curl_cffi
Why it matters: Cloudflare, Akamai, DataDome and friends fingerprint the TLS handshake
and HTTP/2 settings — not just User-Agent. urlwatch's stock requests client gets you a
403 with no recourse. webchanges routes through curl_cffi with a real browser TLS profile.
How: Set http_client: curl_cffi and impersonate: chrome (or
safari / firefox / edge). Override fingerprints.ja3,
fingerprints.akamai, or pin http_version as far as v3only.
url: https://hard-to-scrape.example/
http_client: curl_cffi
impersonate: chrome
http_version: v2tls
2Long diffs → one paragraph ai_google
Why it matters: Watching a 50-page terms-of-service, an RFC draft, a regulatory filing,
or a long policy page? webchanges sends you a plain-English summary of the meaningful changes instead of a
2,000-line unified diff. urlwatch ships the raw patch and leaves you to read it.
How: differ: ai_google calls Google Gemini with a prompt template that
exposes {old_text}, {new_text}, {unified_diff}. Configurable
model, system_instructions, temperature, top_p,
thinking_budget. Bring your own free Gemini API key.
differ:
name: ai_google
prompt: 'Summarize material changes by section.\n\n{unified_diff}'
3Watch images, not just text image
Why it matters: Logo swaps, product-photo updates, dashboard screenshots, status-page
banners, captcha pages. urlwatch is text-first; if the page is a chart or an image, you're out. webchanges
pixel-diffs and ships a yellow overlay showing what moved.
How: differ: image with data_type: url (or
filename / ascii85 / base64) and
mse_threshold (default 2.5) to tolerate compression noise.
url: https://example.com/logo.png
differ:
name: image
data_type: url
mse_threshold: 5.0
4JSON/XML diffs that don't cry wolf deepdiff
Why it matters: APIs reorder keys. Numeric precision drifts. Whitespace flips. urlwatch
text-diffs the serialized form and pings you for noise; you end up chaining format-json +
jq + a custom diff_tool and still missing nested changes. webchanges compares
the parsed structure element by element.
How: differ: deepdiff with data_type: json (or
yaml / xml), ignore_order, ignore_string_case,
significant_digits, compact.
differ:
name: deepdiff
data_type: json
ignore_order: true
significant_digits: 2
Use webchanges if…
- Any of the four wins above apply.
-
You need granular Playwright control:
block_elements, evaluate,
chained wait_for_*, persistent user_data_dir, switches,
http_credentials.
-
You want reporters that earn their keep:
github_issue turns each change into a
tracked ticket with labels/assignees/milestones; mailgun for transactional email;
browser opens the HTML report locally; run_command exposes
{count}/{jobs}/{text} plus WEBCHANGES_*_JSON env vars.
-
You want Telegram with formatting: bold/italics/strikethrough/links via Markdown, not plain
text plus a
monospace flag.
-
You want snapshot retention controls:
max_snapshots caps storage growth and
--rollback-database recovers from a bad filter change. urlwatch keeps every snapshot.
-
You want safer external commands: the
execute filter runs a program without
shell interpretation. shellpipe is still available but flagged as a security risk.
-
You want extra filters at the edges:
ascii85, base64,
format-xml, jsontoyaml, pypdf, remove_repeated,
absolute_links; renamed keep_lines_containing /
delete_lines_containing with text+re modes.
- You want a JSON Schema (
config.schema.json) for editor autocomplete.
- You need HTTP/2 to access sites.
Migration is mostly drop-in: jobs.yaml / config.yaml are largely compatible, and the
database auto-migrates. Original files are retained so you can go back if you want.
Full reference comparison
Source: each project's official reference docs.
Jobs & universal directives
| Directive |
webchanges |
urlwatch |
Notes |
name, enabled, kind, user_visible_url,
compared_versions, max_tries
|
✓ |
✓ |
Common ground. |
filter / filters, diff_filter / diff_filters |
✓ |
✓ |
Plural form in webchanges. |
differ — typed differ selector with sub-directives |
✓ |
× |
urlwatch only has diff_tool (a custom command). |
note, monospace, is_markdown |
✓ |
× |
Per-job rendering hints. webchanges-only |
additions_only, deletions_only at job level |
✓ |
filter-only |
webchanges promotes them to first-class job options. |
tags, treat_new_as_changed |
× |
✓ |
urlwatch-only |
suppress_errors / suppress_repeated_errors / suppress_error_ended
|
✓ |
~ |
Finer-grained muting in webchanges. |
same_site_delay |
✓ |
× |
Staggers jobs hitting the same site to dodge rate limits. webchanges-only |
Shell job stderr handling
(ignore/fail/stdout…)
|
× |
✓ |
urlwatch-only |
HTTP client & fingerprinting
| Capability |
webchanges |
urlwatch |
Selectable client (httpx (HTTP/2), requests, curl_cffi) |
✓ |
× |
http_version: v1, v2, v2tls,
v2_prior_knowledge, v3, v3only
|
✓ |
× |
impersonate (chrome / safari / firefox / edge) |
✓ |
× |
fingerprints.ja3, fingerprints.akamai, extra_fp |
✓ |
× |
params, data_as_json, retries, no_redirects,
no_conditional_request, empty_as_transient
|
✓ |
~ |
Common: headers, cookies, data, method,
encoding, timeout, ssl_no_verify,
http_proxy/https_proxy, ignore_*_errors
|
✓ |
✓ |
Browser jobs (Playwright)
Both projects use Playwright and support the same engines (chromium, chrome, firefox, webkit, msedge). The
difference is the directive surface, which is much broader for webchanges.
webchanges — use_browser: true
block_elements (skip stylesheets/images/fonts/media/scripts)
connect_over_cdp (attach to an already-running browser over the DevTools Protocol)
evaluate (run JS, return result)
-
init_script, initialization_url, initialization_js (initialize
page before going to the URL with the data)
-
switches, ignore_default_args, user_data_dir(control browser
launch)
http_credentials (log in, including a proxy)
-
ignore_https_errors,
referer
-
wait_for_function, wait_for_selector (state / strict / timeout, list-able),
wait_for_url, wait_for_timeout, wait_until
urlwatch — navigate:
-
navigate, wait_until, wait_for, useragent,
browser
Same engine, much smaller knob set.
Filters
Both share the core (CSS/XPath, html2text, jq, pdf2text, OCR, regex, sort/reverse). Differences is the additional filters offered by webchanges.
-
webchanges-only:
execute (safer than shellpipe),
ascii85, base64, between, format-xml, jsontoyaml,
pypdf, remove_repeated, absolute_links, enhanced
strip (chars/side/splitlines),
format-json with sort_keys, pdf2text with
raw/physical.
-
Renamed in webchanges:
grep → keep_lines_containing,
grepi → delete_lines_containing (both accept text or
re in webchanges).
Differs
| Differ |
What it produces |
webchanges |
urlwatch |
unified |
Standard line diff (context_lines, range_info) |
✓ |
implicit |
table |
HTML side-by-side table (tabsize) |
✓ |
× |
wdiff |
Word-level diff with line breaks preserved |
✓ |
× |
command |
Pipe to external program (is_html) |
✓ |
diff_tool |
deepdiff |
Structural JSON/YAML/XML diff |
✓ |
× |
image |
Pixel diff with overlay |
✓ |
× |
ai_google |
LLM summary via Gemini |
✓ |
× |
Reporters
| Reporter |
webchanges |
urlwatch |
Notes |
Common: stdout, email (sendmail/SMTP), mailgun, telegram,
matrix, gotify, ntfy, pushover,
pushbullet, prowl, ifttt, xmpp, discord
|
✓ |
✓ |
Both broad. |
github_issue |
✓ |
× |
Token, owner, repo, labels, assignees, milestone. webchanges-only |
browser |
✓ |
× |
Open HTML report locally. webchanges-only |
run_command |
✓ |
~ |
webchanges substitutes {count}/{jobs}/{text} +
WEBCHANGES_*_JSON; urlwatch has a simpler shell reporter.
|
webhook (unified Slack / Mattermost / custom) |
✓ |
slack + mattermost |
webchanges merges them with markdown, max_message_length,
rich_text.
|
Command-line interface
Webchanges
adds operational tooling for testing, retention, recovery, observability, and packaging.
| Flag |
What it does |
webchanges |
urlwatch |
--errors [REPORTER] |
Test-run every enabled job and list ones that errored or captured no data; optionally pipe the result
to a reporter.
|
✓ |
× |
--list-jobs [REGEX] |
List jobs and their indexes, filtered by an optional regex. urlwatch's --list has no
filter.
|
✓ |
no regex |
--test [JOB] |
Run one job and show filtered output. With no JOB, validates the syntax of every config, jobs, and
hooks file.
|
✓ |
JOB required |
--test-differ JOB |
Replay the differ against existing saved snapshots; combines with --test-reporter to
preview the rendered output.
|
✓ |
--test-diff-filter |
--no-headless |
Show the actual browser window for Playwright jobs (debug captchas, redirects, login walls). |
✓ |
× |
--max-workers N |
Cap the parallel thread pool used to fetch jobs. |
✓ |
× |
--clean-database [N] |
Trim snapshot history to the latest N per job (default 1). |
✓ |
× |
--rollback-database TIMESTAMP |
Recovery undo: drop every changed snapshot newer than TIMESTAMP — the antidote to a bad filter
that triggered a wave of false changes.
|
✓ |
× |
--delete-snapshot JOB |
Delete only the most recent snapshot for a job, e.g. to force a re-fetch. |
✓ |
× |
--gc-database [N] |
Drop snapshots for jobs no longer in the jobs file. |
✓ |
--gc-cache |
--database-engine, --max-snapshots N |
Override the database engine and retention from the CLI without editing config.yaml. |
✓ |
× |
--footnote "TEXT" |
Append a custom footer to every rendered report. |
✓ |
× |
--log-file FILE |
Redirect logs to a file (defaults to verbose when set). |
✓ |
× |
--check-new |
Query PyPI to see if a newer release of webchanges is available. |
✓ |
× |
--detailed-versions |
Dump version info for webchanges, Python, SQLite, every dependency, the Playwright browser, and (on
Linux) related apt packages — useful for bug reports.
|
✓ |
× |
--install-chrome |
Install or update the Chrome browser used by Playwright jobs. |
✓ |
× |
--tags |
Select jobs to run by tag instead of by index. |
× |
✓ |
Configuration & database
-
webchanges exposes
differ_defaults (per-differ defaults),
database (engine + max_snapshots), global tz, and
footnote. Ships config.schema.json for editor autocompletion. Underscore-prefixed
keys (_note:) act as inline comments.
-
Database backends — webchanges allows to set
max_snapshots to define retention.
-
Default config locations — webchanges: Linux
~/.config/webchanges, macOS
~/Library/Preferences/webchanges, Windows %USERPROFILE%\Documents\webchanges.
FAQ
What's the single biggest reason to choose webchanges?
The differ system ai_google turns a 2,000-line legal diff into three bullets;
deepdiff kills false positives on JSON/XML; image watches pictures. urlwatch has
none of these natively.
Can webchanges scrape sites that block urlwatch?
Often, yes. Use http_client: curl_cffi with impersonate: chrome (or
safari/firefox/edge) and override fingerprints.ja3 /
fingerprints.akamai if needed. urlwatch ships with no fingerprinting controls.
Is migrating from urlwatch hard?
Usually not. Job and config files are mostly compatible; the database auto-migrates. Original files are retained so you can go back if you want.
Does webchanges send my pages to Google?
Only if you opt in via the ai_google differ, which needs your own Gemini API key. Otherwise
change detection is local.