Claude API Telegram Bot Setup Guide
Building a Telegram bot that answers with Claude takes three pieces: a Telegram bot token from BotFather, a way to call Claude's API, and a small server or script that passes messages between the two. This guide walks through all three, with working Node.js code you can deploy in under an hour.
The short version: register a bot with @BotFather, get an API key for Claude, write a message handler that forwards Telegram text to Claude and sends the reply back, then run it with polling (simplest) or a webhook (better for production). Everything below fills in the details.
Step 1: Create the Telegram Bot
Open Telegram, search for @BotFather, and send /newbot. Follow the prompts to pick a name and a username ending in bot. BotFather returns a token that looks like 123456789:AAFn.... Save it somewhere safe — this is how your code authenticates with Telegram's Bot API.
Optionally set a description and profile picture with /setdescription and /setuserpic, and disable group privacy mode with /setprivacy if you want the bot to read every message in a group rather than only commands.
Step 2: Get Access to Claude's API
You need an API key that can call Claude's messages endpoint. If you already have direct Anthropic API access, you can use that. If you want to skip provisioning and billing setup, or you're building this on a Claude.ai subscription you already pay for, SubToAPI turns that access into a standard HTTPS API with a sub_live_... key — same request shape, streaming, and tool use, just without a separate Anthropic account. Either way, the integration code below is nearly identical; only the base URL and key differ.
Step 3: Set Up the Project
mkdir claude-telegram-bot && cd claude-telegram-bot
npm init -y
npm install node-telegram-bot-api node-fetch dotenv
Create a .env file:
TELEGRAM_BOT_TOKEN=123456789:AAFn...
SUBTOAPI_KEY=sub_live_xxxxxxxxxxxx
Step 4: Write the Message Handler
Create bot.js:
require('dotenv').config();
const TelegramBot = require('node-telegram-bot-api');
const fetch = require('node-fetch');
const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: true });
async function askClaude(userMessage) {
const response = await fetch('https://api.subtoapi.app/v1/messages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SUBTOAPI_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages: [{ role: 'user', content: userMessage }]
})
});
const data = await response.json();
return data.content[0].text;
}
bot.on('message', async (msg) => {
const chatId = msg.chat.id;
const text = msg.text;
if (!text) return;
bot.sendChatAction(chatId, 'typing');
try {
const reply = await askClaude(text);
bot.sendMessage(chatId, reply);
} catch (err) {
console.error(err);
bot.sendMessage(chatId, 'Something went wrong talking to Claude. Try again in a moment.');
}
});
console.log('Bot is running...');
Run it with node bot.js. Message your bot on Telegram and you should get a Claude reply. The full request/response shape for the messages endpoint is documented at /docs/messages if you want to add system prompts, temperature settings, or multi-turn history.
Step 5: Keep Conversation Context
Telegram sends each message independently — Claude has no memory of earlier turns unless you build it yourself. A simple in-memory map works for testing:
const history = new Map();
bot.on('message', async (msg) => {
const chatId = msg.chat.id;
const text = msg.text;
if (!text) return;
const prior = history.get(chatId) || [];
const messages = [...prior, { role: 'user', content: text }];
const reply = await askClaude(messages);
history.set(chatId, [
...messages,
{ role: 'assistant', content: reply }
].slice(-20)); // cap history length
bot.sendMessage(chatId, reply);
});
Pass messages directly in the request body instead of a single string. For anything beyond a demo, swap the in-memory map for Redis or a database — process restarts will otherwise wipe every conversation.
Step 6: Stream Replies for Long Answers
Telegram messages have a length limit and users notice lag on longer completions. If you want to edit a message incrementally as tokens arrive instead of waiting for the full response, Claude supports server-sent event streaming — see /docs/streaming for the request format. In practice, editing a Telegram message every 500ms with bot.editMessageText as chunks arrive gives a responsive feel without hitting Telegram's rate limits on message edits.
Step 7: Add Commands and Tools
For a bot that does more than chat — looking up data, calling a weather API, querying a database — use Claude's tool use feature so the model decides when to call a function and you execute it server-side before sending the result back. The request format is covered in /docs/tools. A common pattern: Telegram command like /weather Paris triggers a tool-enabled request, Claude requests the get_weather tool, your code calls the real API, and the final reply goes back to the user.
Step 8: Deploy
Polling works fine for personal projects and runs anywhere a Node process can stay alive — a VPS, a Raspberry Pi, a Docker container. For production bots, switch to a webhook: register your server's HTTPS endpoint with bot.setWebHook(url) and Telegram pushes updates to you instead of you polling theirs. This scales better and avoids the "409 conflict" error you get from running two polling instances of the same bot at once.
Whichever route you pick, keep the Claude API key out of your repo. Environment variables plus a secrets manager (or your hosting platform's built-in env config) is enough for most setups.
Common Pitfalls
- Double responses: running two instances of the bot with polling enabled causes Telegram to deliver updates to both, producing duplicate replies.
- Markdown formatting errors: Claude sometimes returns markdown that Telegram's
parse_modecan't render. Strip or convert it, or just send as plain text. - Rate limits: a busy group chat can generate more requests than your Claude plan allows. Queue messages or debounce rapid-fire input from the same chat.
- Long messages: Telegram caps messages at 4096 characters. Split longer Claude replies across multiple
sendMessagecalls.
FAQ
Do I need a paid Telegram developer account?
No. Telegram bots are free to create via BotFather with no account verification or billing required. The only cost is your Claude API usage.
Can I run this without managing my own server?
Yes — host the Node process on any platform that keeps long-running processes alive (a small VPS or a container service), or use a webhook with a serverless function if you prefer not to manage infrastructure continuously.
How do I avoid sharing my Anthropic account directly for a bot like this?
If you don't want to set up separate Anthropic billing just for a side project, SubToAPI gives you an API key tied to your existing Claude access. Sign up at /signup and check /pricing for plan details, or start with /docs/quickstart to get the first request working.