- Docs
- Integrations
- Telegram
Build a Telegram astrology bot with RoxyAPI
Ship a working
/horoscope,/kundli, or/tarotbot on Telegram in under 20 minutes. One Bot API token from BotFather, one Roxy API key, two screens of code.
Telegram is the easiest messaging platform to ship a bot on. The official Bot API is a plain JSON-over-HTTPS surface, BotFather hands you a token in three messages, and your handler can run anywhere that can speak HTTPS. RoxyAPI provides the astrology, tarot, and numerology answers your bot replies with: 130+ endpoints across 10 domains behind one key, plus a remote MCP server per domain when you want an LLM to pick the right call on its own.
TL;DR
- You will ship a Telegram bot that replies to
/horoscope <sign>,/tarot, and/lifepath <YYYY-MM-DD>with live RoxyAPI data - You need a BotFather token (free) and a RoxyAPI key. Plans start at $39 a month for 25K requests; a free test key is available on request via contact
- Working code in three languages, ready to deploy. About 15 minutes start to finish
What you can build on Telegram
- A daily horoscope DM that fires from a cron at 8 AM for every subscriber
- A
/kundlicommand that asks for birth date, time, and place, then replies with a Vedic chart - A
/tarotcommand that draws a daily card and sends the card image assendPhoto - A
/numerologyflow that returns Life Path, Expression, and Soul Urge from a name and date - A group bot that drops the daily moon phase or hexagram into a chat at sunset
- A multi-language support bot that calls Roxy with
?lang=hior?lang=trfor non-English users - An AI agent bot where the LLM picks between horoscope, kundli, and tarot endpoints based on the question
What you need, 30 seconds
- A Telegram account and the BotFather chat open. Free.
- A RoxyAPI key. Plans start at $39 a month on the pricing page; a free test key for evaluation is available on request via contact.
- A place to run the handler. Local laptop with ngrok for testing, or any HTTPS host (Vercel, Fly.io, Railway, Render, your own server) for production.
- About 15 minutes.
Run the quickstart curl in a terminal first. A 200 response confirms the Roxy key is good before you wire it into any handler.
Step 1, register your bot and pick a transport
Open a chat with @BotFather, send /newbot, pick a name and a username ending in bot. BotFather replies with an HTTP token like 12345:ABC.... Save it. That token authenticates every call your handler makes back to Telegram.
Telegram offers two transports. Pick based on whether you have a public HTTPS URL.
Your handler loops, calling getUpdates, processes each message, replies. No webhook, no HTTPS host, no signing. Run it on your laptop, run it in a Docker container, run it during a demo.
# One-shot poll. Starts the conversation feedback loop.
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/getUpdates"
Use long polling for prototyping and bots with low message volume. Switch to webhooks once you deploy.
Telegram POSTs every incoming message to an HTTPS URL you own. No polling, lower latency, scales horizontally.
# Register a webhook. URL must be HTTPS.
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook?url=https://your-app.example.com/webhook"
For local development, run ngrok (ngrok http 3000) and use the HTTPS URL it gives you. Telegram requires a valid TLS certificate, self-signed will not work without extra setup.
Step 2, ship the /horoscope command
The handler watches for /horoscope <sign>, calls Roxy, and replies. The example below runs over the webhook transport from Step 1, but the body of each handler also works inside a long-polling loop without changes.
Verify the Roxy call is correct before wiring anything to Telegram.
curl "https://roxyapi.com/api/v2/astrology/horoscope/aries/daily" \
-H "X-API-Key: $ROXY_API_KEY"
You should get back JSON with sign, date, overview, love, career, luckyNumber, moonSign, moonPhase, and energyRating. Pick the fields you want in the bot reply.
// npm install express @roxyapi/sdk
import express from 'express';
import { createRoxy } from '@roxyapi/sdk';
const roxy = createRoxy(process.env.ROXY_API_KEY!);
const TG = `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}`;
const app = express();
app.use(express.json());
app.post('/webhook', async (req, res) => {
const msg = req.body.message;
if (!msg?.text) return res.sendStatus(200);
const [cmd, sign] = msg.text.split(/\s+/);
if (cmd === '/horoscope' && sign) {
const { data, error } = await roxy.astrology.getDailyHoroscope({
path: { sign: sign.toLowerCase() },
});
const reply = error
? `Could not read ${sign}: ${error.code}`
: `${data.sign} for ${data.date}\n\n${data.overview}\n\nLucky number: ${data.luckyNumber}\nMoon: ${data.moonSign} (${data.moonPhase})\nEnergy: ${data.energyRating}/10`;
await fetch(`${TG}/sendMessage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: msg.chat.id, text: reply }),
});
}
res.sendStatus(200);
});
app.listen(3000);
# pip install fastapi uvicorn httpx roxy-sdk
import os
import httpx
from fastapi import FastAPI, Request
from roxy_sdk import create_roxy, RoxyAPIError
roxy = create_roxy(os.environ['ROXY_API_KEY'])
TG = f"https://api.telegram.org/bot{os.environ['TELEGRAM_BOT_TOKEN']}"
app = FastAPI()
@app.post('/webhook')
async def webhook(request: Request):
update = await request.json()
msg = update.get('message')
if not msg or 'text' not in msg:
return {'ok': True}
parts = msg['text'].split()
if parts and parts[0] == '/horoscope' and len(parts) > 1:
sign = parts[1].lower()
try:
data = roxy.astrology.get_daily_horoscope(sign=sign)
reply = (
f"{data['sign']} for {data['date']}\n\n"
f"{data['overview']}\n\n"
f"Lucky number: {data['luckyNumber']}\n"
f"Moon: {data['moonSign']} ({data['moonPhase']})\n"
f"Energy: {data['energyRating']}/10"
)
except RoxyAPIError as e:
reply = f"Could not read {sign}: {e.code}"
async with httpx.AsyncClient() as client:
await client.post(
f"{TG}/sendMessage",
json={'chat_id': msg['chat']['id'], 'text': reply},
)
return {'ok': True}
# uvicorn telegram_bot:app --reload --port 3000
Send /horoscope aries to your bot. You should get back the daily reading within a second.
Bonus, a /tarot command that sends the card image
The tarot/daily response carries card.imageUrl, a public CDN URL. The Telegram sendPhoto method accepts a remote URL directly, so you can return the card image with no extra hosting.
Drop this block inside the app.post('/webhook', ...) handler from above; it reuses the same roxy, msg, and TG variables.
const { data } = await roxy.tarot.getDailyCard({ body: { seed: String(msg.from.id) } });
await fetch(`${TG}/sendPhoto`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
chat_id: msg.chat.id,
photo: data.card.imageUrl,
caption: `${data.card.name}${data.card.reversed ? ' (reversed)' : ''}\n\n${data.dailyMessage}`,
}),
});
Seeding by msg.from.id makes the daily card deterministic per user per day. Same user calls /tarot ten times before midnight, gets the same card every time. Works the same way in Python via roxy.tarot.get_daily_card(seed=str(msg['from']['id'])).
Step 3, scale to the full surface
Adding a new command is the same shape as /horoscope. Pick the endpoint, build the parameters, format the reply.
- API reference with a pre-filled test key. Browse endpoints, run a call in the browser, copy the curl
- Domain guides for endpoint ordering: Western astrology, Vedic astrology, Tarot, Numerology
- Most-used endpoints that fit Telegram bot replies:
GET /astrology/horoscope/{sign}/dailyfor daily, weekly, monthly variantsPOST /vedic-astrology/birth-chartfor kundli (callGET /location/searchfirst to geocode the city)POST /numerology/life-pathfor the Life Path number fromyear,month,dayPOST /tarot/spreads/three-cardfor past, present, future spreads
Any chart, horoscope, panchang, dasha, dosha, navamsa, KP, synastry, or compatibility endpoint needs latitude, longitude, and timezone. Always call roxy.location.searchCities (TS) or roxy.location.search_cities (Py) first and feed the result through. The server resolves IANA strings like "Asia/Kolkata" to the DST-correct offset for the chart date, so January births in New York pick EST and July births pick EDT automatically.
Add an MCP-powered AI agent (optional)
Hardcoded /horoscope and /tarot commands are fine for a focused bot. For a free-text bot ("what does my chart say about my career?"), point an LLM at the Roxy MCP server and let it pick the endpoint.
The remote MCP server runs at https://roxyapi.com/mcp/{domain} over Streamable HTTP. No stdio, no Docker, no local setup. Point Claude Code, Cursor, OpenAI Agents SDK, or any other MCP-aware client at the URL with your X-API-Key header. The agent inspects tools/list, picks the right tool, builds parameters, and calls Roxy. From inside your Telegram handler, the LLM owns the routing logic; you only forward msg.text to the agent and reply with the result.
tools/list is free. Every tools/call bills the same as the equivalent REST call. See /docs/mcp for the full setup.
Frequently asked questions
Is the test API key OK for production?
No. The shared test key on API reference is rate-limited and rotates. For your bot, mint a real key on the pricing page. The Starter plan ($39 a month) covers 25K requests, enough to run a small bot end to end. If you need more headroom for evaluation before paying, request a free test key via contact.
How do I keep the bot from burning quota in group chats?
Long polling and webhooks both deliver every message in groups your bot is added to. Filter on msg.text.startsWith('/') so only commands trigger Roxy calls. Cache daily content per user with a short-lived KV (Redis, SQLite) so the same user calling /horoscope aries ten times in a minute pays for one call.
Can the bot reply in Hindi, Spanish, or Turkish?
Yes. Most Roxy domains accept a lang query parameter (en, tr, de, es, fr, hi, pt, ru). In the SDK, pass query: { lang: 'hi' } (TS) or lang='hi' (Py). Detect the user language from msg.from.language_code (Telegram passes IETF tags) and forward it.
What happens when a user sends a malformed birth date?
Roxy returns a 400 with code: validation_error. Catch it (TS: if (error), Python: except RoxyAPIError as e), reply with a friendly message, and log the raw error for debugging. Never let the 400 bubble up as a generic Telegram crash.
Do I need to verify webhook payloads?
Telegram does not sign webhook bodies the way Slack and WhatsApp do. The standard defense is a secret_token you pass to setWebhook and check on every incoming request via the X-Telegram-Bot-Api-Secret-Token header. Add it once you go to production.
Gotchas
- Backend-only key. The Roxy key lives in your handler, never in a client message. If you proxy something to Telegram users, never echo
process.env.ROXY_API_KEY. - Telegram chat IDs are integers, not strings. Use them as numbers. JSON serialization handles both, but typed code in TS or Pydantic in Python should match.
- Webhook URL must be HTTPS. Self-signed certs work only with the
certificateform-data parameter onsetWebhook. Use ngrok for local dev, a real cert in production. textis missing on photo, sticker, or location messages. Always guardif (!msg?.text)before parsing commands.- Group bots see every message by default. BotFather,
/setprivacyto enable Privacy Mode so the bot only sees commands and replies, unless you actually want full feeds. - Send formatting matters.
sendMessagewithparse_mode: 'HTML'or'MarkdownV2'lets you bold the sign and italicize the advice. Withoutparse_modeeverything is plain text. - Telegram rate limits are 30 messages per second to different chats and 1 per second to the same chat. A bulk send to 1000 users needs throttling.
- MCP
tools/callis billable,tools/listis free. When debugging an agent, the discovery call does not count, the actual answer does.
What to build next
- The Western astrology guide and Vedic astrology guide cover endpoint ordering for chart and kundli flows.
- The WhatsApp integration and Slack integration cover the same patterns on adjacent messaging platforms.
- The AI chatbot tutorial is the reference for wiring multiple domains into an LLM-routed agent.
- Browse the API reference for every endpoint across 10 domains.