How to Build a Telegram Bot with Claude API in Python (2026)

In this tutorial you will build a Telegram bot powered by the Claude API — from BotFather setup to a fully deployed AI assistant that remembers conversation history. Everything runs in Python with python-telegram-bot v20+.

This is a practical telegram bot Python AI guide — by the end you will have a working bot that handles messages, keeps per-user context, and runs as a background service on a VPS.


Prerequisites

  • Python 3.10+
  • Anthropic API key — get one here
  • Telegram account
  • VPS (optional, for deployment)

Step 1: Create a Telegram Bot via BotFather

Open Telegram, find @BotFather, send /newbot and follow the prompts. Copy the bot token (format: 7123456789:AAF...).


Step 2: Install Dependencies

pip install python-telegram-bot anthropic

Step 3: Basic Echo Bot (Test First)

Before adding Claude, verify your bot token works:

from telegram import Update
from telegram.ext import Application, MessageHandler, filters, ContextTypes

BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"

async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    text = update.message.text
    await update.message.reply_text(f"You said: {text}")

app = Application.builder().token(BOT_TOKEN).build()
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
app.run_polling()

Send your bot a message in Telegram — it should echo it back. Once it works, move on.


Step 4: Add Claude API

Now replace the echo handler with a Claude-powered one. It keeps per-chat conversation history (last 20 messages) so Claude has context:

from telegram import Update
from telegram.ext import Application, MessageHandler, CommandHandler, filters, ContextTypes
import anthropic

BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"
claude = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from env

conversations: dict[int, list] = {}

async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    chat_id = update.effective_chat.id
    user_text = update.message.text

    if chat_id not in conversations:
        conversations[chat_id] = []
    conversations[chat_id].append({"role": "user", "content": user_text})

    history = conversations[chat_id][-20:]

    response = claude.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system="You are a helpful assistant. Be concise — this is a Telegram chat.",
        messages=history,
    )

    reply = response.content[0].text
    conversations[chat_id].append({"role": "assistant", "content": reply})
    await update.message.reply_text(reply)

async def reset_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    conversations.pop(update.effective_chat.id, None)
    await update.message.reply_text("Conversation reset.")

app = Application.builder().token(BOT_TOKEN).build()
app.add_handler(CommandHandler("reset", reset_command))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
app.run_polling()

The ANTHROPIC_API_KEY is read from environment automatically. See the Claude API Python tutorial for full setup.


Step 5: Add Typing Indicator

Claude takes 1-3 seconds to respond. Show a typing indicator while waiting:

async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_chat_action(
        chat_id=update.effective_chat.id,
        action="typing"
    )
    # ... rest of the handler unchanged

Step 6: Handle Long Responses

Telegram caps messages at 4096 characters. Split long replies:

def split_message(text: str, limit: int = 4000) -> list[str]:
    if len(text) <= limit:
        return [text]
    parts = []
    while text:
        parts.append(text[:limit])
        text = text[limit:]
    return parts

# In your handler, replace reply_text with:
for part in split_message(reply):
    await update.message.reply_text(part)

Step 7: Deploy on a VPS

Run the bot as a persistent systemd service. Create /etc/systemd/system/claude-bot.service:

[Unit]
Description=Claude Telegram Bot
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/claude-bot
ExecStart=/usr/bin/python3 bot.py
Restart=always
Environment=ANTHROPIC_API_KEY=your-key
Environment=BOT_TOKEN=your-token

[Install]
WantedBy=multi-user.target
sudo systemctl enable claude-bot
sudo systemctl start claude-bot
sudo systemctl status claude-bot

Cost Estimate

With 100 daily messages (avg 200 input + 150 output tokens each) using Claude Sonnet:

  • Input: ~/usr/bin/zsh.06/day
  • Output: ~/usr/bin/zsh.23/day
  • Total: ~/month

Add prompt caching on the system prompt to cut input costs by ~90%. See the prompt engineering guide for caching tips.


What to Build Next

  • /summarize command — paste a URL, get a summary
  • Image analysis — handle filters.PHOTO and pass to Claude vision
  • Scheduled reminders — use job_queue from python-telegram-bot
  • Multi-bot workflows — check n8n + Claude automation for ideas

FAQ

What Python library should I use for Telegram bots?

python-telegram-bot (v20+) is the most popular choice — async, well-documented, actively maintained. aiogram is a good alternative for a lower-level API.

Does the Claude API work with Telegram bots?

Yes. The Anthropic Python SDK works in any async Python context. You call claude.messages.create() inside your Telegram message handler just like in any other Python script.

How much does a Claude-powered Telegram bot cost?

For a personal bot with ~100 messages/day on Claude Sonnet, expect ~$9/month. With prompt caching on the system prompt that drops to under $1/month.

Can I run a Telegram bot without a VPS?

Yes — run_polling() works on your local machine for development. For production, a $4/month VPS or a free-tier cloud function keeps the bot online 24/7.

Subscribe