Build an Insight App in Python: Astrology, Vedic, Tarot APIs (2026)

10 min read
Brett Calloway
developerPythonAstrology APIVedic AstrologyTarot APISDKroxy-sdk

Python tutorial to build an astrology app covering Western charts, Vedic kundli, panchang, and tarot with the Roxy Python SDK. Typed, async-ready, by RoxyAPI.

TL;DR

  • One Python SDK covers Western astrology, Vedic kundli, panchang, tarot, numerology, biorhythm, I Ching, crystals, dreams, and angel numbers.
  • Install with pip install roxy-sdk or uv add roxy-sdk. Every method has a matching _async variant for FastAPI and asyncio.
  • Geocode the birth city with roxy.location.search_cities first. Pass the returned utcOffset (a decimal) as the timezone kwarg on chart endpoints.
  • Build a daily horoscope, a Vedic birth chart, and a tarot spread in under 30 minutes with roxy-sdk.

About the author: Brett Calloway is a Developer Advocate and AI Integration Specialist with 12 years of experience building APIs and developer tooling. He has led developer relations at two Series B SaaS companies and spoken at PyCon and JSConf on building context-rich AI agents using Model Context Protocol.

Why Python developers stall when building insight apps

Every astrology, tarot, or numerology side project starts with the same blocker: the math. Natal chart calculations need an ephemeris verified against authoritative sources. Vedic panchang needs sunrise and tithi math. KP needs sub-lord theory. Tarot needs a seeded RNG that produces the same card per user per day. Most Python developers spend a weekend piecing together single-domain libraries, a tarot deck JSON, and three timezone workarounds, then ship a broken V1. The problem is not Python, the problem is stitching four unrelated sources together and reinventing every interpretation string yourself. The Python RoxyAPI SDK collapses ten spiritual-data domains behind one import. Calculations are verified against NASA JPL Horizons and cross-referenced against authoritative panchang sources. This post walks through the three patterns that matter: geocode, compute, interpret.

Install and authenticate in two minutes

Pick your package manager. The SDK ships wheels for Python 3.10 and up and depends only on httpx, so it installs in seconds on any platform.

# pip
pip install roxy-sdk

# or uv (faster, recommended)
uv add roxy-sdk

Get an API key at pricing. Starter is $39 a month for 5,000 requests across every domain with one key. Every endpoint uses the same X-API-Key header, and create_roxy injects it automatically.

import os
from roxy_sdk import create_roxy

roxy = create_roxy(os.environ["ROXY_API_KEY"])

# A first call. No boilerplate, no JSON parsing.
horoscope = roxy.astrology.get_daily_horoscope(sign="aries")
print(horoscope["overview"])

The SDK sends X-SDK-Client: roxy-sdk-python/0.1.3 on every request so you can track SDK usage in your own logs alongside the API response.

Geocode the birth city before any chart call

Chart endpoints (natal, Vedic, synastry, panchang, KP, transits, compatibility) need latitude, longitude, and timezone as a decimal number. Never ask the user to type coordinates. Geocode the city first, then pass the result into the chart method.

The location response is a paginated envelope. Each city has two timezone fields: timezone is the IANA identifier (Asia/Kolkata) for your date library, and utcOffset is the DST-adjusted decimal (5.5 for IST, -5 for EST, 9 for JST). Pass utcOffset to chart endpoints.

result = roxy.location.search_cities(q="Mumbai, India")
city = result["cities"][0]
# city = { "city": "Mumbai", "latitude": 19.017, "longitude": 72.857,
#          "timezone": "Asia/Kolkata", "utcOffset": 5.5, ... }

natal = roxy.astrology.generate_natal_chart(
    date="1990-01-15",
    time="14:30:00",
    latitude=city["latitude"],
    longitude=city["longitude"],
    timezone=city["utcOffset"],
)

Passing the IANA string instead of the decimal returns validation_error: expected number, received string. This is the single most common mistake and the SDK AGENTS.md flags it in red for AI coding agents.

Build a daily horoscope in three lines

The daily horoscope endpoint is the cheapest hook in any consumer app. It drives daily active users, push notifications, and streaks. One GET call, no body, no coordinates.

h = roxy.astrology.get_daily_horoscope(sign="scorpio")

# h is a plain dict, ready to serialize to your app
print(h["overview"])        # General daily forecast, ~230 chars
print(h["love"])             # Love and relationship forecast
print(h["career"])           # Career outlook
print(h["luckyNumber"])      # Integer
print(h["luckyColor"])       # String
print(h["compatibleSigns"])  # List of compatible signs for today
print(h["moonPhase"])        # Current moon phase
print(h["energyRating"])     # 1 to 10

Weekly (get_weekly_horoscope) and monthly (get_monthly_horoscope) variants return the same shape plus luckyDays[] and keyDates[]. For caching, the horoscope changes at midnight UTC, so a 1-day TTL works. See the Western astrology reference for every field.

Ready to ship? Get your API key and drop a daily-horoscope card into your app this afternoon.

Generate a Vedic kundli and panchang

Vedic is where the depth shows. The kundli endpoint returns all twelve rashi houses with the planets placed in each, a meta dict keyed by planet name, house interpretations, and combustion / planetary-war metadata.

kundli = roxy.vedic_astrology.generate_birth_chart(
    date="1990-01-15", time="14:30:00",
    latitude=28.6139, longitude=77.209, timezone=5.5,
)

# Iterate rashis for the 12-sign chart view
for rashi in ["aries", "taurus", "gemini", "cancer", "leo", "virgo",
              "libra", "scorpio", "sagittarius", "capricorn", "aquarius", "pisces"]:
    signs = kundli[rashi]["signs"]  # planets in this rashi (can be empty)
    if signs:
        print(rashi, [p["graha"] for p in signs])

# Or look up any planet directly
print(kundli["meta"]["Sun"]["rashi"])       # e.g. "Capricorn"
print(kundli["meta"]["Moon"]["nakshatra"]["name"])  # e.g. "Uttara Ashadha"

Panchang is the other India-market staple. One call returns tithi, nakshatra, yoga, karana, rahu kaal, abhijit muhurta, gulika, and brahma muhurta for any date and location. See the Vedic astrology API product page for the full endpoint list.

Cast a tarot spread with a deterministic seed

Tarot readings need a seeded RNG so the same user gets the same card on the same day. The SDK handles the seed automatically. Pass a user ID or session ID and the card is stable until tomorrow.

# Daily card, same answer for this user until midnight
card = roxy.tarot.get_daily_card(seed="user-42")
print(card["card"]["name"])          # e.g. "The Star"
print(card["card"]["imageUrl"])      # Full-res card art URL
print(card["card"]["reversed"])      # bool, reversed meaning applies
print(card["dailyMessage"])           # Contextual message for today

# Ten-position Celtic Cross for a specific question
reading = roxy.tarot.cast_celtic_cross(
    question="What should I focus on this quarter?",
    seed="user-42-q1",
)
for p in reading["positions"]:
    print(p["position"], p["name"], p["card"]["name"], "-", p["interpretation"])

# Lightweight yes / no for an impulse feature
answer = roxy.tarot.cast_yes_no(question="Should I take the offer?")
print(answer["answer"])     # "Yes" | "No" | "Maybe"
print(answer["strength"])    # "Strong" | "Qualified"
print(answer["card"]["name"])

Card art ships as public HTTPS URLs, no signed-URL dance, safe to embed directly. The full tarot API reference has 10 endpoints including three-card, love spread, and the 78-card catalog.

Handle errors and rate limits without noise

The SDK raises RoxyAPIError on any non-2xx response. The code field is machine-readable and stable, safe to switch on. The error field is human-readable and may change wording between releases.

from roxy_sdk import create_roxy, RoxyAPIError

try:
    result = roxy.astrology.get_daily_horoscope(sign="scorpio")
except RoxyAPIError as e:
    if e.code == "validation_error":
        # e.error includes the failing field
        print(f"Bad input: {e.error}")
    elif e.code == "rate_limit_exceeded":
        # Monthly quota hit. Upgrade or wait until the 1st.
        print("Quota reached, retry next month")
    elif e.code in ("subscription_inactive", "invalid_api_key"):
        print(f"Auth problem: {e.code}")
    else:
        print(f"API error {e.status_code}: {e.code} - {e.error}")

Validation errors include an issues[] array with the specific field path that failed. No more guessing which parameter your integration sent wrong. The canonical error code list lives on the SDK docs page.

Deploy behind FastAPI with async

Every sync method has an _async variant for asyncio. Drop the SDK into FastAPI with zero glue code.

from fastapi import FastAPI
from roxy_sdk import create_roxy, RoxyAPIError
import os

app = FastAPI()
roxy = create_roxy(os.environ["ROXY_API_KEY"])

@app.get("/horoscope/{sign}")
async def horoscope(sign: str):
    try:
        return await roxy.astrology.get_daily_horoscope_async(sign=sign)
    except RoxyAPIError as e:
        return {"error": e.code, "message": e.error}, e.status_code

@app.post("/natal")
async def natal(city: str, date: str, time: str):
    locations = await roxy.location.search_cities_async(q=city)
    c = locations["cities"][0]
    return await roxy.astrology.generate_natal_chart_async(
        date=date, time=time,
        latitude=c["latitude"], longitude=c["longitude"],
        timezone=c["utcOffset"],
    )

The SDK shares one httpx.AsyncClient across calls, so connection pooling is automatic. Benchmarks on a Hetzner CX23 show median end-to-end latency of 87 ms for the horoscope endpoint and 180 ms for a full natal chart, including the round trip from Frankfurt to the API.

Give your AI agents the same SDK your code uses

Python insight apps are a natural fit for Claude Code, Cursor, Windsurf, and the OpenAI Agents SDK because the data they return is structured JSON, not prose. The Roxy Python SDK ships an AGENTS.md inside the wheel at site-packages/roxy_sdk/AGENTS.md, so any AI coding agent that reads AGENTS.md (Claude Code, Cursor, Windsurf, Copilot, Codex, Gemini CLI) picks up the critical patterns automatically: geocode first, timezone is a decimal, location returns an envelope, method names come from operationId, not URL paths. Separately, each domain exposes a remote MCP server at https://roxyapi.com/mcp/{domain} (Streamable HTTP, no stdio, no self-hosting) so agents can call the API directly without glue code. Read the MCP setup guide to wire up Claude Desktop or Cursor in under five minutes.

Troubleshooting checklist for the first hour

These are the five issues every new integrator hits. Work through them once and you will not hit them again.

First, the timezone parameter must be a decimal number. Asia/Kolkata fails. Use city["utcOffset"], not city["timezone"]. Second, time must include seconds in HH:MM:SS format, 14:30 fails. Third, numerology takes year, month, day as integers, not a single date string. Fourth, location responses are paginated envelopes, always index result["cities"][0], never result[0]. Fifth, do not wrap the API key in quotes inside the env file, export it raw. If you hit invalid_api_key with a correct key, check for a trailing newline or stray whitespace. The SDK strips neither, deliberately, so upstream errors surface quickly.

Frequently Asked Questions

Q: What does the Roxy Python SDK cover? A: Ten spiritual-data domains behind one key. Western astrology, Vedic or Jyotish, numerology, tarot, biorhythm, I Ching, crystals, dreams, angel numbers, and location. Plus usage stats. A single pip install roxy-sdk gets every domain, no stitching needed.

Q: Is the SDK typed and does it support async? A: Every method is a typed Python function with keyword arguments. Every sync method has a matching _async variant that uses httpx.AsyncClient for FastAPI, asyncio, and any event loop. Response shapes follow the OpenAPI spec exactly.

Q: Why does the timezone field on a city return Asia Kolkata instead of a number? A: The city timezone field is the IANA identifier for your date library. Chart endpoints need a decimal like 5.5. Use the utcOffset field on the same city (already DST-adjusted) and pass it as the timezone kwarg.

Q: Can I use the SDK from a Jupyter notebook or a CLI script? A: Yes. The SDK has no framework dependency. Works in notebooks, scripts, FastAPI, Flask, Django, Celery workers, and background jobs. Shared httpx client is safe across threads and asyncio tasks.

Q: How do I avoid hitting the monthly quota during development? A: Keep the API key out of browser-exposed code and cache deterministic responses locally. Daily horoscopes, tarot daily cards, and angel number lookups are stable within a day, so a 1-day cache is safe. Use the free get_usage_stats call to monitor quota.

Conclusion

Python developers stall on insight apps because the math and the content are both hard. The Roxy Python SDK removes both layers: one import, ten domains, typed responses, async by default. Start with the Python SDK quickstart, then pick the domain your users want first. Pricing starts at $39 a month for 5,000 requests across every endpoint, so your V1 costs less than a domain name.