- Docs
- What To Build
- AI Astrology Chatbot
Build an AI astrology chatbot
Ship a chatbot that answers "what is my Life Path", "draw me a tarot card", "what is my kundli" using real Roxy data. Time to ship: 30 to 45 minutes.
The flagship Roxy build. A user types a natural-language question. An LLM picks the right Roxy tool, calls it, and replies in plain English. The LLM does endpoint selection, field extraction, and interpretation synthesis. You write no question-to-endpoint mapping.
MCP (Model Context Protocol) is the fast path. Register Roxy once, every Roxy endpoint becomes a tool the model can auto-discover. The slower path is raw function calling, where you hand-define each tool. Both are covered below.
What you can build with this
- Daily horoscope bot ("what is the Leo horoscope today")
- Natal chart bot ("I was born July 15 1990 at 2:30 PM in New York, what is my chart")
- Tarot reader ("pull me a card", "do a three-card spread")
- Numerology assistant ("what is my Life Path for July 15 1990")
- Vedic kundli bot ("generate the kundli for someone born July 15 1990 in Mumbai")
- Multi-domain wellness companion covering all ten Roxy domains
What you need, 30 seconds
- A Roxy API key. Get one on the pricing page.
- An LLM key from Anthropic, OpenAI, or Google AI Studio.
- Node.js 18+ or Bun (for the from-scratch path) or any MCP-compatible client (for the MCP path).
Option A, clone the flagship starter
The fastest path. The MIT-licensed starter the Roxy team maintains as a reference integration.
git clone https://github.com/RoxyAPI/astrology-ai-chatbot.git
cd astrology-ai-chatbot
npm install
Create .env:
ROXY_API_KEY=your_roxy_key
ANTHROPIC_API_KEY=your_claude_key
# or OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY
Run:
npm run dev
Open http://localhost:3000. The starter ships with a web UI, conversation history, multi-provider LLM support, and auto-discovery of every Roxy MCP server (astrology, vedic-astrology, numerology, tarot, biorhythm, iching, crystals, dreams, angel-numbers). You do not define tools. The Vercel AI SDK picks them up from the remote MCP manifest.
Option B, build it from scratch with function calling
If you want to see the machinery, here is a single-file chatbot that hand-defines four tools and hand-calls Roxy. Good for teaching, good for LLMs without MCP client support.
Setup:
mkdir astro-chat && cd astro-chat
npm init -y
npm install @anthropic-ai/sdk
Create chat.js:
# For a quick sanity check that the underlying endpoints respond
curl "https://roxyapi.com/api/v2/astrology/horoscope/aries/daily" \
-H "X-API-Key: $ROXY_API_KEY"
// chat.ts, drop-in replacement for the JavaScript version below
import Anthropic from '@anthropic-ai/sdk';
import { createRoxy } from '@roxyapi/sdk';
import { createInterface } from 'node:readline';
const client = new Anthropic();
const roxy = createRoxy(process.env.ROXY_API_KEY!);
const tools = [
{
name: 'get_daily_horoscope',
description: 'Daily horoscope for a zodiac sign. Returns overview, love, career, health, finance.',
input_schema: {
type: 'object',
properties: { sign: { type: 'string', description: 'Lowercase sign, aries through pisces' } },
required: ['sign'],
},
},
{
name: 'calculate_life_path',
description: 'Numerology Life Path number from birth date.',
input_schema: {
type: 'object',
properties: {
year: { type: 'number' },
month: { type: 'number' },
day: { type: 'number' },
},
required: ['year', 'month', 'day'],
},
},
];
async function callRoxy(name: string, input: any) {
if (name === 'get_daily_horoscope') {
const { data } = await roxy.astrology.getDailyHoroscope({ path: { sign: input.sign } });
return data;
}
if (name === 'calculate_life_path') {
const { data } = await roxy.numerology.calculateLifePath({ body: input });
return data;
}
throw new Error('unknown tool');
}
# chat.py, same idea, with the Python SDK
import os
import anthropic
from roxy_sdk import create_roxy
client = anthropic.Anthropic()
roxy = create_roxy(os.environ['ROXY_API_KEY'])
tools = [
{
'name': 'get_daily_horoscope',
'description': 'Daily horoscope for a zodiac sign.',
'input_schema': {
'type': 'object',
'properties': {'sign': {'type': 'string'}},
'required': ['sign'],
},
},
{
'name': 'calculate_life_path',
'description': 'Numerology Life Path number from birth date.',
'input_schema': {
'type': 'object',
'properties': {
'year': {'type': 'number'},
'month': {'type': 'number'},
'day': {'type': 'number'},
},
'required': ['year', 'month', 'day'],
},
},
]
def call_roxy(name, input):
if name == 'get_daily_horoscope':
return roxy.astrology.get_daily_horoscope(sign=input['sign'])
if name == 'calculate_life_path':
return roxy.numerology.calculate_life_path(**input)
raise ValueError('unknown tool')
# Skip the hand-defined tools entirely with Option C below.
For the raw JavaScript version (no SDK), drop this into chat.js:
import Anthropic from '@anthropic-ai/sdk';
import { createInterface } from 'readline';
const client = new Anthropic();
const ROXY_KEY = process.env.ROXY_API_KEY;
const tools = [
{
name: 'get_daily_horoscope',
description: 'Daily horoscope for a zodiac sign. Returns overview, love, career, health, finance.',
input_schema: {
type: 'object',
properties: { sign: { type: 'string', description: 'Lowercase sign, aries through pisces' } },
required: ['sign'],
},
},
{
name: 'generate_natal_chart',
description: 'Western natal chart from birth data. Returns planets with interpretation, houses, aspects.',
input_schema: {
type: 'object',
properties: {
date: { type: 'string', description: 'YYYY-MM-DD' },
time: { type: 'string', description: 'HH:MM:SS' },
latitude: { type: 'number' },
longitude: { type: 'number' },
timezone: { type: 'string', description: 'IANA id like America/New_York' },
},
required: ['date', 'time', 'latitude', 'longitude', 'timezone'],
},
},
{
name: 'get_daily_tarot_card',
description: 'Draw a single tarot card with interpretation.',
input_schema: { type: 'object', properties: { seed: { type: 'string' } } },
},
{
name: 'calculate_life_path',
description: 'Numerology Life Path from birth date.',
input_schema: {
type: 'object',
properties: { year: { type: 'number' }, month: { type: 'number' }, day: { type: 'number' } },
required: ['year', 'month', 'day'],
},
},
];
async function callRoxy(name, input) {
const routes = {
get_daily_horoscope: { method: 'GET', url: `https://roxyapi.com/api/v2/astrology/horoscope/${input.sign}/daily` },
generate_natal_chart: { method: 'POST', url: 'https://roxyapi.com/api/v2/astrology/natal-chart', body: input },
get_daily_tarot_card: { method: 'POST', url: 'https://roxyapi.com/api/v2/tarot/daily', body: input },
calculate_life_path: { method: 'POST', url: 'https://roxyapi.com/api/v2/numerology/life-path', body: input },
};
const r = routes[name];
const res = await fetch(r.url, {
method: r.method,
headers: { 'X-API-Key': ROXY_KEY, 'Content-Type': 'application/json' },
body: r.body ? JSON.stringify(r.body) : undefined,
});
return res.json();
}
async function chat(history) {
let response = await client.messages.create({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 1024,
system: 'You are a friendly spiritual assistant. Always call a tool when the user asks for horoscope, birth chart, tarot, or numerology data. Never guess.',
tools,
messages: history,
});
while (response.stop_reason === 'tool_use') {
const tu = response.content.find(b => b.type === 'tool_use');
const result = await callRoxy(tu.name, tu.input);
history.push({ role: 'assistant', content: response.content });
history.push({ role: 'user', content: [{ type: 'tool_result', tool_use_id: tu.id, content: JSON.stringify(result) }] });
response = await client.messages.create({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 1024,
tools,
messages: history,
});
}
const text = response.content.find(b => b.type === 'text')?.text ?? '';
history.push({ role: 'assistant', content: response.content });
return text;
}
const rl = createInterface({ input: process.stdin, output: process.stdout });
const history = [];
console.log('Roxy chatbot. Type quit to exit.\n');
const loop = () => rl.question('You: ', async line => {
if (line.trim().toLowerCase() === 'quit') return rl.close();
history.push({ role: 'user', content: line });
const reply = await chat(history);
console.log('\nBot: ' + reply + '\n');
loop();
});
loop();
Run:
ROXY_API_KEY=your_roxy_key ANTHROPIC_API_KEY=your_claude_key node chat.js
Try:
- "What is the Leo horoscope today?"
- "I was born July 15 1990 at 2:30 PM in New York, what is my chart?"
- "Draw me a tarot card."
- "What is my Life Path? Birthday July 15 1990."
Option C, skip tool definitions with MCP
Neither of the above scales. The moment you want "all ten domains, every endpoint" you stop hand-writing tool schemas and point the LLM at MCP.
Register all Roxy MCP servers with Claude Code in one go:
claude mcp add-json --scope user roxy-astrology '{"type":"http","url":"https://roxyapi.com/mcp/astrology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-vedic '{"type":"http","url":"https://roxyapi.com/mcp/vedic-astrology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-numerology '{"type":"http","url":"https://roxyapi.com/mcp/numerology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-tarot '{"type":"http","url":"https://roxyapi.com/mcp/tarot","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-iching '{"type":"http","url":"https://roxyapi.com/mcp/iching","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-location '{"type":"http","url":"https://roxyapi.com/mcp/location","headers":{"X-API-Key":"YOUR_KEY"}}'
Same pattern for Claude Desktop, Cursor, Antigravity, and Vercel AI SDK. See the MCP setup docs for full client instructions.
Gotchas
- Do not embed the Roxy API key in the browser. The chatbot backend holds the key and proxies calls. Anyone can view page source.
- Timezone accepts IANA strings. The LLM naturally fills
"America/New_York"from "New York", let it. The server resolves DST for the birth date automatically. Decimal offsets like-5also work but do not DST-correct. - Tell the model to geocode for chart calls. Add a line to the system prompt: "For any birth chart question, call location search first to get latitude, longitude, and timezone." Without that, the LLM sometimes invents coordinates.
- Seeded endpoints are deterministic. Pass
seed: userIdto daily tarot and the same user gets the same card all day. That is a feature, not a cache staleness bug. - Gemini tool schemas use Type enums. See the function-calling guide for the Gemini variant.
What to build next
- The MCP setup docs cover Claude Desktop, Cursor, Antigravity, and custom Streamable HTTP clients.
- The function-calling guide shows the raw tool-calling pattern for OpenAI, Anthropic, and Gemini.
- The starters page lists the full catalog of clone-and-ship apps.
- The Vedic astrology guide and Western astrology guide list every endpoint your bot can cover.