# Pattern 22 — Telegram bot as monitoring transport, €0/mo alternative to PagerDuty
## The pain
Your service is in production. Something will eventually go wrong: an exchange API will return garbage, a disk will fill up, a cron job will silently stop running. You need *someone* (you) to find out within minutes, not hours, not the next morning when a customer emails you.
PagerDuty exists for this. It costs €19/user/month, has a configuration UI deeper than IKEA assembly instructions, and is wildly overkill if you're a solo dev with one production service.
The boring answer: **Telegram bot**. Free. The Bot API has been stable since 2015. You create a bot in 60 seconds via `@BotFather`, get a token, and from then on you can `curl` your bot a message and your phone buzzes within ~2 seconds. There is nothing else to configure.
## When to use it
- Solo dev or small team where the alert recipient is one or two people.
- You already use Telegram on your phone (or are willing to install it).
- You want push notifications for: service down, scheduled job missed, cron failed, disk usage high, deploy succeeded, anything.
- You don't need on-call rotations, escalation policies, or audit trails.
## The setup (90 seconds)
1. Open Telegram, search for `@BotFather`, send `/newbot`. Follow the prompts. Get a token like `1234567890:ABCdEFghIJklMNOpqrSTUvwxyz`. Save it as `TELEGRAM_BOT_TOKEN`.
2. Send any message to your new bot (you have to message it first, before it can message you).
3. Get your `chat_id` by visiting `https://api.telegram.org/bot<TOKEN>/getUpdates` in a browser. Look for `"chat":{"id":12345678}` in the response. Save that as `TELEGRAM_CHAT_ID`.
That's it. You're done.
## The code
A 10-line helper module that any other module can import:
```python
import os
import requests
TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID")
def send_alert(message: str, parse_mode: str = "Markdown") -> bool:
"""Send a message to the configured Telegram chat. Returns True on success.
Silent no-op if creds are missing (so dev environments don't spam).
"""
if not (TOKEN and CHAT_ID):
return False
try:
r = requests.post(
f"https://api.telegram.org/bot{TOKEN}/sendMessage",
data={"chat_id": CHAT_ID, "text": message, "parse_mode": parse_mode},
timeout=10,
)
return r.json().get("ok", False)
except Exception:
return False
```
Use it from anywhere:
```python
from alerts import send_alert
if disk_usage > 0.9:
send_alert(f"⚠️ disk at {disk_usage:.0%} on `{hostname}`")
if collector_age > 600:
send_alert(f"❌ collector stale: last fetch {collector_age}s ago")
# Or for deploys:
send_alert(f"✅ deploy of `{commit_sha[:7]}` complete")
```
Your phone buzzes within 2 seconds. No SaaS account, no SDK, no webhooks UI, no pager rotation.
## Group chats and channels
The same pattern works for:
- **A group chat** with you + a co-founder. Add the bot to the group, get the (negative) chat_id, use it. Both phones buzz.
- **A public broadcast channel** where you post deploy logs / status updates that users can subscribe to. Make the bot an admin of the channel, use the channel's `@username` as the chat_id (no token needed).
- **A separate "noisy" channel** for non-urgent stuff (deploy logs, daily summaries) and a "quiet" channel for actual alerts. Just two different `chat_id` env vars.
## When NOT to use it
- **You need on-call rotation.** Telegram has no concept of "the alert goes to whoever is on duty this week." Use Opsgenie or PagerDuty.
- **You need acknowledgment + escalation.** "If no one acks within 5 min, page the next person." Same answer.
- **Compliance requires an audit trail.** Telegram is not a logging system. Use a real ticketing tool that records who acknowledged what and when.
- **You're sending more than ~30 messages/second.** Telegram rate limits bots to ~30 msg/sec across all chats. For high-frequency events (a metrics fire-hose), batch them or use Prometheus.
For the solo-dev / small-team / "I just want my phone to buzz when X happens" case, none of these apply. Telegram wins.
## Some practical tricks
**Markdown formatting**. Use `parse_mode=Markdown` to get bold (`*text*`), italic (`_text_`), inline code (`` `text` ``), and code blocks (triple backticks). It makes alerts much easier to scan.
**Don't include sensitive secrets**. Treat the bot like a webhook with no auth: anyone who has the token can read every message you've ever sent through it. Don't include API keys, customer data, or session tokens in alerts.
**Rate-limit your own alerts**. If your service catches an exception in a loop, you don't want it to spam Telegram 10 times/sec. Add a deduplication window:
```python
import time
_last_alert: dict[str, float] = {}
def send_alert_once(key: str, message: str, cooldown: int = 600) -> bool:
"""Send an alert at most once per `cooldown` seconds per `key`."""
now = time.time()
if key in _last_alert and now - _last_alert[key] < cooldown:
return False
_last_alert[key] = now
return send_alert(message)
```
Now you can call `send_alert_once("disk_full", "...")` from a hot path without flooding.
## A note on cost
The Telegram Bot API is free. Forever. No "freemium" rug-pull, no surprise tiers, no token costs after $X. The bot has worked the same way since 2015 and has not become a product they're trying to monetize. It's the closest thing in 2026 to "infrastructure that just works for free, indefinitely."
## Further reading
- Official Telegram Bot API reference — https://core.telegram.org/bots/api
- `@BotFather` documentation — https://core.telegram.org/bots/features#botfather
- Limits on message rate — https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
## The summary
You don't need PagerDuty for a side project. You need a Telegram bot, a 10-line helper, and two env vars. Your phone will buzz within 2 seconds when something breaks, and you'll have spent €0 to set it up. I've used this pattern for every service I've shipped in the last three years.