proxykey API KEY VAULT

Telegram bot tokens leak more than any other API key: how to hide yours

A regular API key at least has a chance to hide in a request header. A Telegram bot token has none: it lives right in the URL — and settles into every log along the request's path. Here's what that costs and how to fix it without rewriting your bot.

2026-07-06 · proxykey

Why the bot token is the worst case

The Bot API puts the token into the address itself:

https://api.telegram.org/bot123456:AAEhBOweik6ad.../sendMessage

The URL is the leakiest part of an HTTP request. A bot token automatically ends up:

With an OpenAI key you have to try hard to log it. With a bot token you have to try hard not to.

What the finder gets

Everything the bot can do, no exceptions:

The cruel part A bot-token leak is usually noticed not on a bill (the Bot API is free) but through user complaints about spam — that is, after the reputation damage is done.

Standard measures and their limits

  1. Revoke via @BotFather — the mandatory first step after a leak. But it treats the symptom: the new token lands in the same logs and configs.
  2. Webhook secret_token — protects your endpoint from foreign POSTs, but does nothing for the token itself.
  3. Environment variables — hide the token from code, but not from the URL: at request time it still expands into the address line.

The root problem isn't storage — it's that the real token participates in every single request.

The proxy approach: the real token never leaves the vault

The credential proxy scheme fits bots especially well. The real token is encrypted once in the vault; the bot gets a pass — a virtual token — and exactly two things change: the host and the token.

# before
https://api.telegram.org/bot123456:AAEh.../sendMessage
# after
https://api.proxykey.org/p/telegram-bot/123456:vlt_telegrambot_.../sendMessage

The proxy extracts the pass from the path, checks status, IP and limits, injects the real token on its side and forwards the request to Telegram unchanged. Now every log, config and shell history holds only a revocable pass: a leak is one click of "revoke", not a fire drill.

The format-validation trick

aiogram, telebot and grammY validate the <digits>:<hash> token format before the first request — a bare vlt_… pass fails their check. So the proxy accepts a composite form: prefix the pass with your bot id and a colon — the digits are ignored, and the libraries' validation passes.

# aiogram
bot = Bot(
    token="123456:vlt_telegrambot_XXXX",
    session=AiohttpSession(api=TelegramAPIServer.from_base(
        "https://api.proxykey.org/p/telegram-bot"
    )),
)

Long polling is covered too: getUpdates with timeout=50 doesn't get cut off — the proxy holds long requests, and file downloads work through the same /file/bot… path.

What you get in practice

How we moved two production bots to this scheme — including the rakes we stepped on — in a separate write-up.

FAQ

Does this work with webhooks?

Yes: the webhook is set with the same request through the proxy, and secret_token keeps protecting your endpoint. The real token no longer appears in the webhook config — only the pass does.

Will the bot get slower?

One extra hop — single to low double-digit milliseconds. Against the network latency to Telegram it's invisible, and long polling doesn't care at all.

What if the pass itself leaks?

Wrong IP — 403, over the limits — 429, revocation — one click without reissuing the real token. Compare that with the cleanup after the original leaks.

Hide your bot's token in a minute

Store the token in proxykey — get a pass with IP binding, rate limits and a live log. Free, no card required.

Protect your bot →