July 29, 2026 · 9 Min Lesezeit ntfy.shTelegramPushWatch & Alert

ntfy.sh over Telegram: Push without a bot

You run a Watch & Alert setup with 12 RSS sources, an LLM classification, and a Telegram bot that pings you on important matches. It works — until it doesn't. A spike in the sources, 40 alerts in two seconds, and your bot gets HTTP 429 back. Telegram throttled you.

What if you don't need the bot at all? What if push notifications are an HTTP POST and nothing more? That's exactly ntfy.sh.

This is a known problem, and the usual answer is "just build backoff logic". But what if you don't need the bot at all? What if push notifications are an HTTP POST and nothing more?

That's exactly ntfy.sh.

Where we started

My Watch & Alert setup has been running in production since early 2026. The architecture is described in detail in the book; in short:

  • Gather data (Chapter 9): 12 RSS feeds, 3 Google News alerts, a few webhook sources
  • Understand data (Chapter 11): LLM classification via a local Ollama setup, with fallback to cloud models
  • Filter data (Chapter 12): deduplication against SQLite, threshold-based reduction to "this is new and important"
  • Send data (Chapter 13): a Telegram bot that posts the matches to a private channel

On average, 8 to 15 alerts come through per day. Harmless. But three times a week there's a spike — a major CVE, an industry event, a security advisory — and suddenly it's 30 alerts in 90 seconds. The Telegram bot gets 429, my script logs the errors, and I miss the important ones because they get lost in the retry loop.

The root cause analysis was clear: not the pipeline, but the push channel.

Requirements

What I expect from a push channel for Watch & Alert setups:

  • No account setup. I don't want to talk to BotFather first, generate a token, and store it in a .env.
  • No token rotation. Telegram tokens are security-critical, rotate regularly, and every rotation is a maintenance step that can go wrong.
  • No rate limits in normal operation. On a spike, all alerts should arrive, not 80% of them.
  • Self-hosting option. I don't want to depend on a third party that changes prices tomorrow or shuts down its service.
  • Simple HTTP API. My Watch & Alert script already speaks HTTP — it shouldn't have to learn to talk to an SDK.
  • Push to phone. I want to see alerts on the go, not just in the server log.

Telegram fulfills some of these, but not all. ntfy.sh fulfills all of them.

Three options compared

Option 1: Telegram bot (as before)

Telegram bots are powerful. Commands, inline keyboards, interactive workflows — if you want to build a chatbot, the Telegram Bot API is the best ecosystem available. My setup uses python-telegram-bot.

But for pure push alerts, this is overkill:

  • You need a Telegram account.
  • You need a dialogue with @BotFather to create the bot.
  • You need a token that you store securely and rotate regularly.
  • You need a server URL if you use webhooks (or long polling, which keeps your script open).
  • You're limited to 30 messages per second globally (core.telegram.org/bots/faq) — beyond that, you get 429.
  • Messages flow through Telegram's infrastructure — a question of trust.

For my Watch & Alert setup, the balance is negative. I'm not using Telegram as a chatbot, but as a pure push channel. That's too expensive in effort.

Option 2: ntfy.sh cloud

ntfy.sh is an HTTP-based pub/sub service for push notifications. The name stands for "notify". The concept:

  1. Pick a topic name — for example stephan-watch-alert-7k9x.
  2. Subscribe to the topic in the ntfy app on your phone (Android, iOS, desktop).
  3. Send messages via POST or PUT to https://ntfy.sh/<topic>.
  4. Done. The message arrives as a push notification.

No account, no token, no server. Topics don't need to be created explicitly — the first POST creates them. The app stores a history of the most recent messages.

Advantages:

  • Two-minute setup. Pick a topic name, install the app, send the first POST.
  • No rate limits in normal operation. Spikes don't trigger 429s. ntfy.sh scales on the cloud side.
  • No server requirement. If you use the cloud variant, everything runs on ntfy.sh's infrastructure.
  • End-to-end encryption possible. Topics can be encrypted with a topic password.

Disadvantages:

  • Cloud dependency. If ntfy.sh goes down, you don't get alerts. For non-time-critical Watch & Alert setups, that's fine.
  • Topic names are public. If you pick stephan-alerts as a topic, anyone with that name can POST. The fix: long, random topic names like stephan-watch-alert-7k9x-mn4p-q2vr.
  • No commands. Pure push notifications, no interactive buttons.

Option 3: ntfy.sh self-hosted

If you don't trust the cloud variant (or can't use it for compliance reasons): ntfy.sh can be fully self-hosted. A single Docker container image or a Go binary is enough.

docker run -d \
  --name ntfy \
  -p 2586:80 \
  -v /var/cache/ntfy:/var/cache/ntfy \
  -v /etc/ntfy:/etc/ntfy \
  binwiederhier/ntfy serve

Configuration is a single server.yml with theme, authentication, rate limits, and retention policies. For Watch & Alert setups, the default configuration is enough; ACLs only come into play when multiple users share the same server.

Self-hosting advantages:

  • Full data control. Alerts never leave your network.
  • No dependency on ntfy.sh cloud.
  • Extensible — end-to-end encryption is optional, attachments work, webhooks for source integration.

Self-hosting disadvantages:

  • You're responsible for uptime. If your home server goes down at night, you won't get alerts about the down service — directly.
  • TLS certificates are still required for the mobile app to work (Let's Encrypt via Caddy or nginx).

The migration

My migration path from Telegram bot to ntfy.sh was remarkably short. Here are the steps:

Step 1 — Pick a topic and install the app

The topic name should be long, random, and unguessable. I use:

stephan-watch-alert-7k9x-mn4p-q2vr

In the ntfy app (Android, iOS, or the web app), click "Subscribe topic", enter the name, done.

Step 2 — Send a test message

curl -d "Watch & Alert test: pipeline is running" \
  https://ntfy.sh/stephan-watch-alert-7k9x-mn4p-q2vr

When the push notification arrives on the phone, the connection is live.

Step 3 — Python integration in the Watch & Alert script

My old Telegram dispatch looked like this (shortened, from Chapter 13 of the book):

import telegram

bot = telegram.Bot(token=os.environ["TELEGRAM_BOT_TOKEN"])

def send_alert(message: str) -> None:
    bot.send_message(
        chat_id=os.environ["TELEGRAM_CHAT_ID"],
        text=message,
        parse_mode=telegram.ParseMode.MARKDOWN,
    )

The ntfy integration replaces that with a single HTTP POST:

import requests

def send_alert(message: str, *, priority: int = 3) -> None:
    requests.post(
        f"https://ntfy.sh/{os.environ['NTFY_TOPIC']}",
        data=message.encode("utf-8"),
        headers={
            "Title": "Watch & Alert",
            "Priority": str(priority),
            "Tags": "warning",
        },
        timeout=5,
    )

No SDK dependency, no authentication beyond the secret topic name. The requests library is already in the project.

Step 4 — Use priorities

ntfy.sh supports five priority levels:

Priority Meaning When I use it
1 (min) Silent notification Heartbeat "all OK" messages
3 (default) Normal notification Standard alerts
4 (high) Sound + vibration Important alerts to be seen immediately
5 (urgent) Sound + vibration + fullscreen Critical alerts (CVE affects own infrastructure)

In my Watch & Alert setup, security-relevant matches get priority=5, industry news priority=3, daily heartbeats priority=1. The Telegram variant didn't have this — there, all alerts were equally visible.

Step 5 — Keep Telegram as fallback

ntfy.sh is my primary push channel. But for interactive use cases (a watch bot that can be muted via /pause), Telegram stays in the game. My Watch & Alert setup checks both channels:

def send_alert(message: str, *, priority: int = 3) -> None:
    send_via_ntfy(message, priority=priority)
    if priority >= 4:
        send_via_telegram(message)

Critical alerts go to both channels — ntfy for speed, Telegram for commands.

What we learned

Three insights from three months of production use:

Insight 1: Topic names are API keys.

Anyone who knows your topic name can POST. So: not stephan-alerts, but stephan-watch-alert-7k9x-mn4p-q2vr. Treat long, random names like a password.

Insight 2: Push is not push.

Telegram bots are built for chat workflows. ntfy.sh is built for machine-to-human push. If your use case is "the server should notify me", ntfy.sh is the better tool — not because Telegram is bad, but because you've been using a hammer where a screwdriver would do.

Insight 3: Self-hosting is cheaper than expected.

A single ntfy container on a Raspberry Pi 4 is enough for a dozen data sources. The only real complexity is TLS — but that's done in ten minutes with Caddy.

Reference to the book

The book Watch & Alert covers three push channels for watch systems in Chapter 13: Telegram bots, ntfy.sh, and email via SMTP/IMAP. The Telegram section is detailed because the SDK is powerful and many examples exist. The ntfy section is shorter because the API is trivial — one POST request, done.

This article complements the book at a point that's only sketched in the chapter: the concrete migration path from a production setup. If you want to use ntfy.sh productively after reading the book, the architecture shown here (hybrid: ntfy for push, Telegram for commands) is the path I'd recommend.

More on the Watch & Alert pipeline architecture can be found in Chapter 12 (filtering data) and Chapter 13 (sending data) of the book.

When to replicate this — and when not

ntfy.sh over Telegram is right for you if:

  • you have multiple data sources and spikes are possible
  • you don't need an interactive chatbot, just push
  • you can store a secret topic name securely (in a .env or password safe)
  • you're willing to fall back to a cloud variant in a worst-case scenario

Stay with Telegram if:

  • you need commands (/pause, /status, /mute-source xyz)
  • you use inline keyboards for interactive workflows
  • your alerts need to flow into an existing Telegram channel shared with others
  • you already have a Telegram bot infrastructure that works

For a pure Watch & Alert setup without interaction, ntfy.sh is the more honest solution: less code, less infrastructure, fewer limits.


Next steps for you:

  1. Generate a topic name and subscribe in the app.
  2. Send a test curl.
  3. Add requests.post() to your Watch & Alert script.
  4. Run in parallel with Telegram for one week.
  5. When no more 429s — switch off Telegram.

The complete code and test strategy for watch systems are described in Chapter 13 and Chapter 16 of the book.