Blog

We wrote about the bot that watches 50+ Bitsens sites and pings our Telegram group when one breaks. Several founders asked the same thing: how do you actually build that thing? Here is the build, end to end, in the form we wish we had read first. You will leave with a working telegram monitoring bot, real code, and a deployment that costs less than a coffee per month.

TL;DR

  • A useful telegram monitoring bot is one Python file, ~250 lines, talking to two HTTP APIs.
  • BotFather gives you a token in 60 seconds. Telegram’s sendMessage endpoint is the alert pipe.
  • Vibecoding with Claude Code turns a weekend project into two evenings — but only if you ship a thin first version and add one rule per iteration.
  • Check status_coderesponse_time, and ssl_not_after. That covers 90% of real outages.
  • Run it from a 5 €/month VPS with a systemd timer or cron. No queues, no Kubernetes.
  • De-duplicate alerts with a tiny JSON state file so the bot does not spam during long incidents.
  • Recovery pings matter as much as down alerts — the team wants to know when to stop debugging.

Why this matters

Founders and small CTOs hate two things: a customer telling them the site is down, and paying 200 €/month for a 14-tab observability stack they never log into. A focused telegram monitoring bot sits exactly between “we’ll know if something breaks” and “we’ll pay Datadog when we have a real ops team.” It scales with the business: you start with one site at the kitchen table; six months later it is watching every microsite, your Stripe webhook receiver, and three SSL certificates that quietly expire on Saturdays.

Vibecoding makes this build economically obvious. Five years ago, “build your own monitoring” was a multi-day yak shave. With Claude Code and a single Python file, it is a Saturday afternoon — if you keep the scope honest.

Step-by-step: ship a working bot in two evenings

1. Create the bot and grab your chat ID

In Telegram, open a chat with @BotFather, send /newbot, give it a display name, give it a unique username ending in bot, and copy the token BotFather returns. That string is your TELEGRAM_BOT_TOKEN. Treat it like a password.

You also need the chat ID where alerts should land. Add the bot to your team group, send any message in that group, then call:

curl "https://api.telegram.org/bot<TOKEN>/getUpdates"

Look for "chat":{"id":-1001234567890,"title":"Ops"...}. That negative number is your TELEGRAM_CHAT_ID. Save both in a .env file.

2. Define what “down” actually means

Before writing code, write the alert rules in plain English. We use four for every site:

  • HTTP status is not 200–399 ? DOWN.
  • HTTP request takes longer than 8 seconds ? SLOW.
  • SSL certificate expires in fewer than 14 days ? SSL_WARN.
  • Three consecutive failures ? escalate from WARN to DOWN.

Skip everything else for v1. Visual-regression checks, content-keyword diffs, response-body assertions — they can come after the bot has paid for itself.

3. Vibecode the checker with Claude Code

Open Claude Code in your project folder and prompt it with the rules from step 2. The trick is to front-load constraints: language, file count, dependencies. A prompt that worked for us:

Build a single-file Python 3.11 monitoring script called bot.py.
Inputs: a sites.yaml file with a list of {url, name} entries and a
.env file with TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID.

For each site:
- HTTP GET with a 8s timeout, follow redirects.
- If status not in 200..399, set state DOWN.
- If status 2xx but elapsed > 4s, set state SLOW.
- Check SSL certificate not_after; if < 14 days, set SSL_WARN.

Persist last state to state.json. Only send a Telegram message
when the state for a site changes (UP?DOWN, DOWN?UP, OK?SSL_WARN).

Dependencies: requests, pyyaml. No frameworks, no async.
Exit cleanly. Print one line per site to stdout.

Claude Code will produce something close to runnable. The vibecoding discipline is to read the diff, not just trust it. Two things you should always patch by hand:

  • The SSL check. LLMs love urllib3.util.ssl_ shortcuts that silently skip validation. Replace with raw ssl.create_default_context() and socket.create_connection.
  • The Telegram payload. Make sure parse_mode="HTML" is set and that the URL is HTML-escaped — otherwise an & in a query string blows up the message.

Here is the core check that ships in our bot:

import socket, ssl
from datetime import datetime, timezone
from urllib.parse import urlparse

def ssl_days_left(url: str) -> int:
    host = urlparse(url).hostname
    ctx = ssl.create_default_context()
    with socket.create_connection((host, 443), timeout=5) as sock:
        with ctx.wrap_socket(sock, server_hostname=host) as ssock:
            cert = ssock.getpeercert()
    not_after = datetime.strptime(
        cert["notAfter"], "%b %d %H:%M:%S %Y %Z"
    ).replace(tzinfo=timezone.utc)
    return (not_after - datetime.now(timezone.utc)).days

4. Send the alert

Telegram’s Bot API is a single HTTPS POST per message. No SDK needed:

import os, html, requests

TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
CHAT  = os.environ["TELEGRAM_CHAT_ID"]

def notify(name: str, url: str, status: str, detail: str) -> None:
    icon = {"DOWN": "?", "SLOW": "?", "SSL_WARN": "?", "UP": "?"}[status]
    text = (
        f"{icon} <b>{html.escape(name)}</b> — {status}\n"
        f"{html.escape(url)}\n"
        f"<i>{html.escape(detail)}</i>"
    )
    r = requests.post(
        f"https://api.telegram.org/bot{TOKEN}/sendMessage",
        json={"chat_id": CHAT, "text": text, "parse_mode": "HTML",
              "disable_web_page_preview": True},
        timeout=10,
    )
    r.raise_for_status()

That is the entire delivery layer. No queue, no retry exchange, no broker. If Telegram returns a 5xx the next run will catch up — your monitor is its own retry mechanism.

5. De-duplicate with a state file

The number-one mistake on day three is the bot waking everyone every five minutes during a real outage. Persist last state to disk:

import json, pathlib

STATE = pathlib.Path("state.json")

def load_state() -> dict:
    if STATE.exists():
        return json.loads(STATE.read_text())
    return {}

def save_state(s: dict) -> None:
    STATE.write_text(json.dumps(s, indent=2, sort_keys=True))

Compare the new state against the old; only call notify() when a site’s status changes. This single check cuts message volume by ~95% and is the difference between a useful bot monitoring workflow and one your team mutes by Wednesday.

6. Schedule the run

systemd timer is the cleanest way to run the bot every five minutes on a small VPS. Two files:

# /etc/systemd/system/sitebot.service
[Unit]
Description=Bitsens site monitor

[Service]
Type=oneshot
WorkingDirectory=/opt/sitebot
EnvironmentFile=/opt/sitebot/.env
ExecStart=/opt/sitebot/.venv/bin/python /opt/sitebot/bot.py
# /etc/systemd/system/sitebot.timer
[Unit]
Description=Run sitebot every 5 minutes

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
Unit=sitebot.service

[Install]
WantedBy=timers.target

Enable with systemctl enable --now sitebot.timer. The bot now runs every five minutes, costs nothing in idle CPU between runs, and survives reboots without a process supervisor.

7. Send the recovery ping

Half of monitoring’s value is telling the team when to stop worrying. When a site flips back to UP, send a green message and the duration of the incident:

if previous == "DOWN" and current == "UP":
    duration = now - down_since[site]
    notify(name, url, "UP", f"recovered after {duration}")

This is the difference your team will notice the most. It is also the feature most copy-paste tutorials skip.

A concrete example: 14 sites, two evenings, 0 € recurring

This is the stack we shipped for a Lithuanian B2B client running 14 microsites and one Stripe webhook receiver:

  • 14 entries in sites.yaml — names + URLs.
  • Hetzner CX11 VPS at 4.51 €/month, Ubuntu 24.04, single Python venv.
  • bot.py is 247 lines, generated and refined across 11 Claude Code turns.
  • Total build time: 4 hours Saturday, 90 minutes Sunday for SSL + recovery pings.
  • Total monthly run cost: 4.51 € infra, 0 € for SaaS.
  • First catch: an SSL certificate two weeks from expiry on a marketing subdomain that no one had checked since 2023. The monitoring bot found it 13 days before customers would have.
  • Second catch: a Cloudflare DNS edit that flipped a redirect to 301 ? /404. Detected in eight minutes, fixed in twelve.

The bot has run continuously for the last 70 days. It has sent 18 incident messages and 18 recovery messages — exactly twice the noise floor we were willing to accept.

Common pitfalls

  • Sending a message every run, not on state change. If you skip the state file, you ship a spammer, not a telegram monitoring bot. Build it on day one, not after the team mutes the channel.
  • Trusting the LLM-generated SSL check. Most generated snippets call requests.get(verify=False) or never read cert["notAfter"]. Always replace with explicit ssl.create_default_context() against port 443.
  • No timeout on the HTTP call. Without timeout=8, one stuck site holds up the whole run. Then the run takes longer than the timer interval, and you start overlapping processes. Use a flock or set a hard timeout.
  • Hardcoding the chat ID. Treat tokens and chat IDs as secrets in .env; commit a .env.example only. Keep the bot’s git repo private until you are sure it does not leak names.
  • Watching too many things. A bot that checks every page on the site catches the wrong signal — it alerts on cache-warm misses and rate-limited paths. Pick the canonical URL per service. One per app is usually enough.
  • No alert routing. When the bot grows beyond five services, route by topic: payments ? #ops-pay, marketing pages ? #ops-mkt. Telegram supports multiple chat IDs. Three lines of YAML, zero new infrastructure.

FAQ

What is a telegram monitoring bot?

telegram monitoring bot is a small program that periodically checks the health of a website, API, or service and sends a message to a Telegram chat when something changes. It uses Telegram’s Bot API as the alert delivery channel, which means alerts land in the same place your team already chats — no extra app, no extra login.

Is a telegram monitoring tool better than UptimeRobot?

For a single site, UptimeRobot is faster to set up. For 10+ sites, a custom telegram monitoring tool is usually cheaper and more flexible: you control the alert thresholds, the message format, and what counts as a “different enough” change to ping the team. The break-even point is roughly five monitored services or any need for a custom rule like “ignore 503 between 02:00–02:10 UTC.”

Can I build a monitoring bot without writing code?

Yes — but you trade flexibility. n8n and Make.com can wire an HTTP check to a Telegram message in 20 minutes. They struggle with SSL expiry checks, custom de-duplication, and response-body matching. For founders who want it to just work and never grow, no-code is fine; for teams that already vibecode their internal tools, a 250-line Python file is faster to extend.

What to do next

If you want a telegram monitoring bot of your own, fork the structure above — bot.pysites.yamlstate.json, a systemd timer — and ship the smallest possible v1 tonight. If you would rather hand the build off, that is exactly what we do at Bitsens: we build small, sharp internal tools for founders who want fewer SaaS bills and more control. Tell us about your project — most of these bots are scoped, built, and deployed inside two weeks.

More posts

SaaS vs AI Agent: How to Decide Without Burning a Quarter

AI Prototyping Tools in 2026: a Practical Guide to Validating Product Ideas

Build a Mobile App in 2 Weeks: A Founder’s Decision Guide

View all posts