Status Page and Uptime Monitor (Python, nothing to install)
Check your sites every minute, log incidents, alert Slack or Discord, and publish a 90-day status page. Python standard library only.
Use it with Claude or any AI tool. Copy the prompt and paste it in. The link inside gives the AI the whole page as Markdown.
Overview
Build your own uptime monitor and public status page in one small Python file. It checks each of your sites on a schedule, stores every check in SQLite, opens an incident after two failures in a row (so one slow response does not wake you up), closes it when the site is back, posts to a Slack or Discord webhook, and serves a status page with 90-day uptime bars and a JSON API.
Who it is for: anyone with a side project, client sites or an API who wants to know it is down before their users tell them, without paying for a monitoring service.
What you need: Python 3.10 or newer and an AI coding tool. Nothing to install: it uses only Python’s standard library (urllib, sqlite3, http.server, threading).
How to use this plan: put CLAUDE.md (asset 1) in an empty folder, then paste each step’s prompt into your AI tool in order. Check the “done when” line before moving on. Each step builds on the last.
Build it step by step
Sign in to tick steps off-
1
Start the project and check sites once
Put CLAUDE.md in an empty folder first: it keeps the AI on the rules (one file, standard library only, UTC times). Then ask for the config file and a one-off check.
If a site that opens fine in your browser shows CERTIFICATE_VERIFY_FAILED, your Python install is missing a root certificate (common on Windows and macOS). On macOS run "Install Certificates.command" from your Python folder; on Windows update Python from python.org. Do not let the AI turn certificate checks off.
Read CLAUDE.md. Create sites.json with "interval": 60 and a "sites" list of two entries, each with a "name" and a "url" (use https://example.com and https://www.python.org). Create monitor.py with a function check(url) that sends a GET request with a 10 second timeout and returns (ok, status_code, ms, error). Any 2xx or 3xx is ok; timeouts, connection errors and 4xx/5xx are not. Add a command `python monitor.py --once` that checks every site once and prints one line per site: name, UP or DOWN, status code and response time.
Done when python monitor.py --once prints one UP/DOWN line per site with a status code and time in ms
Uses CLAUDE.md
-
2
Save every check in SQLite
Every result goes into status.db so the status page can show history later.
Store each check in SQLite (status.db next to monitor.py). Table checks: id, site, ts (UTC ISO 8601), ok (0/1), status, ms, error. Create the table on start if it is missing, turn on WAL mode, and add an index on (site, ts). --once should save its results too.
Done when after running --once twice, the checks table in status.db has 2 rows per site (open it in any SQLite viewer, or ask your AI for a one-line count)
Uses CLAUDE.md
-
3
Run the checks on a schedule
Now it runs by itself. Checks run in parallel so one slow site does not delay the others.
Make `python monitor.py` run forever: every "interval" seconds check all sites in parallel (one thread each), save the results and print them. Add an --interval N flag that overrides sites.json (useful for testing). Ctrl+C must stop it cleanly with a short goodbye, no traceback.
Done when python monitor.py --interval 5 adds a new row per site every 5 seconds, and Ctrl+C exits without a traceback
Uses CLAUDE.md
-
4
Open and close incidents
One failed check is often a blip. Two in a row is an incident.
Add an incidents table: id, site, started (UTC ISO), ended (NULL while open), reason. After a check, if a site has failed twice in a row and has no open incident, open one with the last error as the reason. On the first successful check, close the open incident. Print a clear line when an incident opens or closes. Never open two incidents for the same site at once.
Done when with a site pointed at http://127.0.0.1:9 an incident opens after the second failed check, and it closes on the first check after you point it back
Uses CLAUDE.md
-
5
Serve the status page
One command runs the checks and the page together. The page is plain server-rendered HTML with no external files, so it loads fast and works anywhere.
Add `python monitor.py --serve` (with --port, default 8080) that runs the checks in a background thread and serves a status page at / with http.server. The page shows: a banner ("All systems operational" in green, or "Partial outage" in red when any site has an open incident); for each site its name, current status, uptime % over 90 days, and a strip of 90 small bars, one per UTC day (green 100%, amber 95 to 99.99%, red below 95%, grey for no data) with a tooltip giving the date and %; and the last 10 incidents for the sites in sites.json with start, end or "ongoing", duration and reason. Escape every value you put into HTML. Work in light and dark mode (prefers-color-scheme) and on a phone.Done when http://localhost:8080 shows every site with its bars, and the banner turns red while a site has an open incident
Uses CLAUDE.md
-
6
Add a JSON API
Handy for your own dashboards, a badge, or another tool.
Add GET /api/status.json returning {"generated": ts, "operational": bool, "sites": [{"name", "url", "up", "uptime_90d", "last_check": {"ts", "status", "ms"}, "open_incident": {...} or null}]}. Send Content-Type application/json and Cache-Control: no-store. Any other path returns 404.Done when curl http://localhost:8080/api/status.json returns valid JSON with every site, and /nope returns 404
Uses CLAUDE.md
-
7
Alert Slack or Discord
Slack incoming webhooks and Discord webhooks (with /slack added to the end of the URL) both accept {"text": "..."}.
Read an optional "webhook" URL from sites.json, or the STATUS_WEBHOOK environment variable (the environment wins). When an incident opens, POST JSON {"text": "🔴 <name> is down: <reason>"}; when it closes, POST {"text": "🟢 <name> is back after <duration>"}. Use a 10 second timeout and never let a failed webhook stop the monitor: log it and carry on. Send exactly one message per open and per close.Done when a local test receiver gets exactly one "down" message when a site goes down and one "back" message when it recovers
Uses CLAUDE.md
-
8
Keep it tidy and tested
Old checks are pruned so the database stays small, and a few tests protect the incident logic.
Once a day (and on start) delete checks older than 90 days. Then add test_monitor.py using unittest only: test that one failure does not open an incident, two in a row do, a success closes it, a site name containing <script> is escaped on the status page, and the JSON API reports operational=false while an incident is open. Use a temporary database file in the tests.
Done when python -m unittest passes and the old-check cleanup runs on start without errors
Uses CLAUDE.md
Files and links
Changes
- v1.1 November 8, 2026 Clearer check for step 2
What people built
Show yoursNobody has shared a build yet. Yours could be the first.
Show what you built
Built this plan? Sign in to share your demo video, live app and screenshots.