Skip to content
OwnlyPlans

Plan

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.

Remix

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.

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. 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.

    Prompt
    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. 2

    Save every check in SQLite

    Every result goes into status.db so the status page can show history later.

    Prompt
    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. 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.

    Prompt
    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. 4

    Open and close incidents

    One failed check is often a blip. Two in a row is an incident.

    Prompt
    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. 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.

    Prompt
    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. 6

    Add a JSON API

    Handy for your own dashboards, a badge, or another tool.

    Prompt
    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. 7

    Alert Slack or Discord

    Slack incoming webhooks and Discord webhooks (with /slack added to the end of the URL) both accept {"text": "..."}.

    Prompt
    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. 8

    Keep it tidy and tested

    Old checks are pruned so the database stays small, and a few tests protect the incident logic.

    Prompt
    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

  • CLAUDE.md 1 KB Project rules for your AI tool. Put it in the empty project folder before step 1.

Changes

  • v1.1 November 8, 2026 Clearer check for step 2

What people built

Show yours

Nobody 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.

Your AI tool
Starter prompt

		

Download the plan