Self-hosted service
dgtlmoon/changedetection.io avatar
dgtlmoon/changedetection.io

changedetection.io: self-hosted page monitoring, where the real decisions are the fetcher, the filters and the LLM you plug in

Best and simplest tool for website change detection, web page monitoring, and website change alerts. Perfect for tracking content changes, price drops, restock alerts, and website defacement monitoring:all for free or enjoy our SaaS plan!

35,810 stars2,247 forksPythonApache-2.0

At a glance

What is it?
dgtlmoon/changedetection.io is an Apache-2.0 Python service that checks web pages, PDFs and JSON APIs on a schedule and sends notifications when they change. The base container fetches pages without a browser; the Visual Selector, Browser Steps and AI change rules each depend on extra pieces you have to run or pay for.
Who is it for?
changedetection.io is a strong choice for anyone who wants to watch prices, restocks, PDFs, JSON endpoints or government pages on their own server and route alerts to Discord, Slack, Telegram, email or webhooks. It is a weaker fit if you need clicks, logins or visual element picking but do not want to run a Chrome container next to it, or if page contents must never reach an outside LLM.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • dgtlmoon/changedetection.io35,810 ★ · +3,367
Star history
Repository2026-07-222026-11-14
dgtlmoon/changedetection.io32.4k35.8k

3,367 Stars in the last 115 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One container and one /datastore volume hold every watch

The repository ships a docker-compose.yml that is the shortest path to a running instance. The uncommented part is small:

code
services:
    changedetection:
      image: ghcr.io/dgtlmoon/changedetection.io
      container_name: changedetection
      hostname: changedetection
      volumes:
        - changedetection-data:/datastore

Everything the service keeps lives in /datastore, mounted from a named volume. That is also where an optional proxies.json goes, mounted as /datastore/proxies.json for the proxy list feature. The rest of the file is commented environment settings. PORT=5000 sets the default listening port inside the container, and the comment warns it is not the same thing as a compose ports: mapping. LOGGER_LEVEL accepts TRACE, DEBUG, INFO, SUCCESS, WARNING, ERROR and CRITICAL, with DEBUG as the default. EXTRA_PACKAGES installs additional Python packages into the container, such as the changedetection.io-osint-processor plugin, with several packages separated by spaces.

The practical consequence: losing that volume means losing every watch, every snapshot and the history behind the price graphs, so back it up like a database. And anything you list in EXTRA_PACKAGES is third-party Python code installed next to that data, so vet plugins the way you would vet any dependency.

The default fetcher is plain HTTP, and Chrome is a second container you add

changedetection.io lets you switch between a fast non-JavaScript fetcher and Chrome-based fetchers. The non-JS fetcher is what you get from the compose file as shipped. It does not execute JavaScript; for that, the README points to fetching through WebDriver and Playwright with an actual Chrome runner.

For a real browser, the compose file includes a commented line that points the app at a separate Chrome container over the Playwright protocol:

code
  #      - PLAYWRIGHT_DRIVER_URL=ws://browser-sockpuppet-chrome:3000

You uncomment it together with the sockpuppetbrowser service. A second setting, PLAYWRIGHT_BYPASS_CSP=false, exists for remote CDP implementations that do not support Page.setBypassCSP; the comment names Obscura browser as one of them. There is also a WEBDRIVER_URL for Selenium at http://browser-selenium-chrome:4444/wd/hub, but the compose comments call that path old and deprecated, say it does not support screenshots very well, and say it cannot handle custom headers. If you are starting fresh, the Playwright route is the one the project points you toward, and it means running and updating a Chrome container alongside the app.

Visual Selector and Browser Steps stop working without Playwright

Two of the features shown most prominently in the README depend on that browser container. The Visual Selector, which lets you click on the part of a page you care about instead of writing a selector, is available only when connected to a Playwright content fetcher. Browser Steps, which runs actions before each check, such as logging into a site, adding a product to a cart, accepting cookie prompts, entering dates or refining a search, also requires Playwright to be enabled. The intended flow is to run Browser Steps first and then use the Visual Selector tab to narrow the content.

The README notes that the Playwright fetcher is included as part of the subscription service. On a self-hosted install, you supply it yourself. So if your plan is to watch a page behind a login, or to point and click at an element, the base compose file is not enough: without the Chrome container, those tabs have nothing to drive, and you are back to writing CSS or XPath selectors by hand against what the plain fetcher returns. You can also execute JavaScript before text is extracted, which the README suggests for logging in.

Filters and selectors decide what counts as a change

A raw page changes constantly: timestamps, ads, rotating banners. Most of the work in a useful watch is telling the tool what to ignore. changedetection.io offers trigger filters named Trigger on text, Remove text by selector, Ignore text and Extract text, and all of them accept regular expressions. Elements can be targeted with XPath 1 and XPath 2 or CSS selectors, and JSON responses with JSONPath or jq. Requests can override headers and use POST or GET and other methods, which is what makes JSON API monitoring and glueing APIs together through JSON notifications possible.

PDFs get their own handling: the service can track text changes inside a PDF and also its file size and checksum. For shops, the Re-stock and Price detection for single product pages option extracts metadata from the HTML page, and you can set upper and lower price limits and a percentage change threshold, with price and restock history drawn as a graph. Diffs can be inspected by word, line or character.

One platform gap is spelled out in requirements.txt: jq is not available on Windows and must be installed manually. If you run the pip install on Windows and rely on jq filters, plan for that extra step.

AI rules and summaries send each diff to the model you configure

The newest layer lets you connect an LLM through LiteLLM. Two features use it. AI change detection rules take a plain-English intent, such as notify me only when the price drops below a threshold or ignore navigation and footer changes, and the model evaluates every detected diff against that intent and suppresses the rest. AI change summaries replace the raw diff in a notification with a sentence describing what changed. Both can be set globally or per watch, and you control the prompt.

Providers named include OpenAI, Gemini, Anthropic and Ollama, plus self-hosted servers such as vLLM, LM Studio and llama.cpp through the OpenAI-compatible option in the provider dropdown, pointed at your server's /v1 URL.

The consequence is about where your data goes. If you chose self-hosting because, as one of the README's use cases puts it, you have a very sensitive list of URLs to watch, a hosted model receives the diffs of those pages on every check that triggers it. Pointing the provider at a local endpoint keeps them on your hardware. The suppression also means a misjudged intent can hide a change you wanted, so keep the raw diff view in mind when an alert you expected never arrives.

Pinned dependencies exist to stop false change alerts

requirements.txt shows how sensitive a diffing tool is to its own text pipeline. The HTML-to-text converter is held to a single patch release:

code
inscriptis>=2.7.6,<2.7.7

The comment above it explains why: version 2.7.6 changed block indentation in its text output, which would show up as a whitespace-only change in every snapshot taken with include filters, so upgrades are meant to happen deliberately with a release note. If you install the package into an environment where another tool pulls a different inscriptis, you risk exactly that flood of meaningless alerts.

Other pins carry their own reasons. regex>=2026.9.29 supports a regex_replace Jinja2 filter with a thread-safe timeout for ReDoS protection. Notifications go through apprise==2.1.0, the library behind the Discord, Email, Slack, Telegram and Webhook targets. Flask is held to the 3.x line and werkzeug is pinned to 3.1.9. The pip package is published as changedetection.io, and setup.py registers a console script of the same name that calls changedetectionio:main.

ARM images pay for cryptography and litellm builds

The Dockerfile builds on python:3.11-slim-bookworm by default through ARG PYTHON_VERSION=3.11, and a large share of its comments deal with ARM. Dependencies are installed with uv~=0.9 instead of pip because, per the comment, pip made about 170 serial index round-trips. uv is installed through pip rather than from the uv container image because that image does not cover arm/v7. Environment variables point Rust at OpenSSL for the cryptography build on ARM, and litellm is installed with --only-binary because compiling its Rust extension from source under QEMU takes hours and then fails.

requirements.txt adds a warning for Raspberry Pi users: cryptography==50.0.2 needs an extra wheel for rPi, arm/v8 is not covered by piwheels, and the comment says the project will probably drop arm/v8 support because it is not worth the extra security risk and build time. If you run the image on that hardware, check the published tags for your platform before upgrading instead of assuming every release covers it.

Releases every two weeks, plus a hosted plan for those who skip the setup

The project is under active development. The repository is not archived, the last push was on 2026-11-02, and releases have been landing about every two weeks: 0.60.8 on 2026-09-28, 0.60.9 on 2026-10-12 and 0.60.10 on 2026-10-26. Version numbers are still below 1.0, so pin the image tag you deploy and read the release notes before you move.

The same code is offered as a subscription at $8.99 per month, described as the same open-source changedetection.io minus the infrastructure work. The hosted plan includes a Chrome browser, proxies and support, which are exactly the pieces a self-hosted user has to assemble: the Playwright container for Browser Steps and the Visual Selector, and a proxies.json for sites that block plain requests. That makes the choice concrete. If you only need static pages, PDFs and JSON endpoints, the single container is enough. If most of your watches need a real browser, weigh running Chrome yourself against paying for the hosted version.

Editorial conclusion

changedetection.io is a strong choice for anyone who wants to watch prices, restocks, PDFs, JSON endpoints or government pages on their own server and route alerts to Discord, Slack, Telegram, email or webhooks. It is a weaker fit if you need clicks, logins or visual element picking but do not want to run a Chrome container next to it, or if page contents must never reach an outside LLM. Before deploying, decide which fetcher you need, keep the /datastore volume backed up, and check whether your LLM endpoint is local or hosted.

Frequently asked questions

Is changedetection.io free?

The changedetection.io source is Apache-2.0 licensed and can be self-hosted with the ghcr.io/dgtlmoon/changedetection.io image. A hosted subscription costs $8.99 per month and adds a Chrome browser, proxies and support.

How do I install changedetection.io?

The repository's docker-compose.yml runs the ghcr.io/dgtlmoon/changedetection.io image with a named volume mounted at /datastore. A pip package named changedetection.io also exists and installs a changedetection.io console command.

What is changedetection.io?

changedetection.io is a Python service that checks web pages, PDFs and JSON APIs for changes and sends notifications through Discord, Email, Slack, Telegram, webhooks and other targets. It also supports price and restock tracking and optional LLM-based change rules and summaries.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dgtlmoon-changedetection-io.svg)](https://hysenlabs.com/projects/dgtlmoon-changedetection-io)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/dgtlmoon-changedetection-io.svg)](https://hysenlabs.com/projects/dgtlmoon-changedetection-io)

Was this useful?