May 4, 2026
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
sendMessageendpoint 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_code,response_time, andssl_not_after. That covers 90% of real outages. - Run it from a 5 €/month VPS with a
systemdtimer 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 rawssl.create_default_context()andsocket.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
A 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.pyis 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 readcert["notAfter"]. Always replace with explicitssl.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.exampleonly. 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?
A 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.py, sites.yaml, state.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.