Try Astrologer API

Subscribe to support and grow the project.

Horoscope API: Build a Personalized Horoscope Generator #

Generic horoscopes based only on Sun signs are a relic of newspaper columns. Modern users expect personalized readings that account for their full birth chart, current planetary transits, and the interplay between the two. This tutorial shows you how to build a horoscope generator that does exactly that, using the Astrologer API as your astrological backend.

By the end, you will have working code in both Python and JavaScript that fetches natal data, retrieves current transits, and combines them with AI-generated context to produce unique, personalized horoscopes for any user.

How Personalized Horoscopes Work #

A meaningful horoscope requires three layers of data:

  1. Natal chart: The fixed map of planetary positions at the moment of birth. This defines the person’s astrological blueprint, including their Sun, Moon, and rising signs, house placements, and natal aspects.

  2. Current transits: The positions of planets right now (or at a target date). When transiting planets form aspects to natal positions, astrologers interpret these as activating specific themes in the person’s life.

  3. Interpretation: Combining natal placements with transit aspects to produce human-readable guidance. This is where AI context endpoints shine, generating structured interpretations that you can use directly or feed into your own LLM pipeline.

The Astrologer API provides endpoints for all three layers.

Step 1: Get the User’s Natal Data #

Start by fetching the user’s birth chart data. The subject endpoint returns all calculated positions.

Python #

import requests

API_URL = "https://astrologer.p.rapidapi.com/api/v5"
HEADERS = {
    "Content-Type": "application/json",
    "X-RapidAPI-Key": "YOUR_API_KEY",
    "X-RapidAPI-Host": "astrologer.p.rapidapi.com"
}

def get_natal_data(name, year, month, day, hour, minute, city, nation, lng, lat, tz):
    """Fetch natal chart data for a user."""
    response = requests.post(
        f"{API_URL}/subject",
        json={
            "subject": {
                "name": name,
                "year": year,
                "month": month,
                "day": day,
                "hour": hour,
                "minute": minute,
                "city": city,
                "nation": nation,
                "longitude": lng,
                "latitude": lat,
                "timezone": tz
            }
        },
        headers=HEADERS
    )
    return response.json()

natal = get_natal_data(
    "Alice", 1990, 3, 21, 14, 30,
    "New York", "US", -74.006, 40.7128, "America/New_York"
)

subject = natal["subject"]
print(f"Sun: {subject['sun']['sign']} (House {subject['sun']['house']})")
print(f"Moon: {subject['moon']['sign']} (House {subject['moon']['house']})")
print(f"Rising: {subject['ascendant']['sign']}")

Output:

Sun: Ari (House Tenth_House)
Moon: Sco (House Fifth_House)
Rising: Can

JavaScript #

const API_URL = "https://astrologer.p.rapidapi.com/api/v5";
const HEADERS = {
  "Content-Type": "application/json",
  "X-RapidAPI-Key": "YOUR_API_KEY",
  "X-RapidAPI-Host": "astrologer.p.rapidapi.com"
};

async function getNatalData(subject) {
  const response = await fetch(`${API_URL}/subject`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ subject })
  });
  return response.json();
}

const natal = await getNatalData({
  name: "Alice",
  year: 1990,
  month: 3,
  day: 21,
  hour: 14,
  minute: 30,
  city: "New York",
  nation: "US",
  longitude: -74.006,
  latitude: 40.7128,
  timezone: "America/New_York"
});

const { sun, moon, ascendant } = natal.subject;
console.log(`Sun: ${sun.sign}, Moon: ${moon.sign}, Rising: ${ascendant.sign}`);

Step 2: Fetch Transit Data #

To see how current planetary positions interact with the natal chart, use the transit chart data endpoint. This computes aspects between the natal chart and a transit moment.

Python #

def get_transit_data(natal_subject, transit_year, transit_month, transit_day,
                     transit_hour, transit_minute, transit_city, transit_nation,
                     transit_lng, transit_lat, transit_tz):
    """Get transit aspects relative to a natal chart."""
    response = requests.post(
        f"{API_URL}/chart-data/transit",
        json={
            "first_subject": natal_subject,
            "transit_subject": {
                "name": "Transit",
                "year": transit_year,
                "month": transit_month,
                "day": transit_day,
                "hour": transit_hour,
                "minute": transit_minute,
                "city": transit_city,
                "nation": transit_nation,
                "longitude": transit_lng,
                "latitude": transit_lat,
                "timezone": transit_tz
            }
        },
        headers=HEADERS
    )
    return response.json()

transit_data = get_transit_data(
    {
        "name": "Alice",
        "year": 1990, "month": 3, "day": 21,
        "hour": 14, "minute": 30,
        "city": "New York", "nation": "US",
        "longitude": -74.006, "latitude": 40.7128,
        "timezone": "America/New_York"
    },
    2026, 4, 21, 12, 0,
    "New York", "US", -74.006, 40.7128, "America/New_York"
)

# List the transit aspects
aspects = transit_data["chart_data"]["aspects"]
for aspect in aspects[:5]:
    p1 = aspect["p1_name"]
    p2 = aspect["p2_name"]
    asp_type = aspect["aspect"]
    orb = aspect["orbit"]
    print(f"  {p1} {asp_type} {p2} (orb: {orb:.2f})")

Example output:

  Sun trine Sun (orb: 0.85)
  Jupiter conjunction Moon (orb: 2.31)
  Saturn square Venus (orb: 1.44)
  Mars opposition Mercury (orb: 3.12)
  Neptune sextile Jupiter (orb: 0.67)

Step 3: Use AI Context for Interpretations #

The natal context endpoint generates XML-structured interpretations that are optimized for LLM consumption. This is the key ingredient for horoscope generation – it provides rich, structured astrological analysis that you can feed to an LLM or display directly.

Python #

def get_natal_context(subject):
    """Fetch AI-generated natal chart interpretation."""
    response = requests.post(
        f"{API_URL}/context/birth-chart",
        json={"subject": subject},
        headers=HEADERS
    )
    return response.json()

context_data = get_natal_context({
    "name": "Alice",
    "year": 1990, "month": 3, "day": 21,
    "hour": 14, "minute": 30,
    "city": "New York", "nation": "US",
    "longitude": -74.006, "latitude": 40.7128,
    "timezone": "America/New_York"
})

# The context field contains XML-structured interpretation
xml_context = context_data["context"]
print(xml_context[:500])

The returned XML includes planetary placements, house positions, aspects, element distributions, and lunar phase data – all structured for easy parsing by language models.

JavaScript #

async function getNatalContext(subject) {
  const response = await fetch(`${API_URL}/context/birth-chart`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({ subject })
  });
  return response.json();
}

const contextData = await getNatalContext({
  name: "Alice",
  year: 1990,
  month: 3,
  day: 21,
  hour: 14,
  minute: 30,
  city: "New York",
  nation: "US",
  longitude: -74.006,
  latitude: 40.7128,
  timezone: "America/New_York"
});

// Use the XML context with your LLM of choice
const xmlContext = contextData.context;
console.log(`Context length: ${xmlContext.length} characters`);

Step 4: Combine Transit Context for Daily Horoscopes #

For a complete daily horoscope, the transit context endpoint is the most powerful option. It produces an AI-optimized interpretation of how current transits affect the natal chart.

Python #

def get_transit_context(first_subject, transit_subject):
    """Get AI-generated transit interpretation."""
    response = requests.post(
        f"{API_URL}/context/transit",
        json={
            "first_subject": first_subject,
            "transit_subject": transit_subject
        },
        headers=HEADERS
    )
    return response.json()

transit_context = get_transit_context(
    {
        "name": "Alice",
        "year": 1990, "month": 3, "day": 21,
        "hour": 14, "minute": 30,
        "city": "New York", "nation": "US",
        "longitude": -74.006, "latitude": 40.7128,
        "timezone": "America/New_York"
    },
    {
        "name": "Transit",
        "year": 2026, "month": 4, "day": 21,
        "hour": 12, "minute": 0,
        "city": "New York", "nation": "US",
        "longitude": -74.006, "latitude": 40.7128,
        "timezone": "America/New_York"
    }
)

# The context includes natal positions, transit positions, and inter-chart aspects
print(f"Context preview: {transit_context['context'][:300]}...")

Step 5: Build the Horoscope Generator #

Now combine everything into a single function that generates a personalized horoscope. This example uses the transit context as input for your own LLM pipeline.

Python (Complete Horoscope Generator) #

import requests
from datetime import datetime

API_URL = "https://astrologer.p.rapidapi.com/api/v5"
HEADERS = {
    "Content-Type": "application/json",
    "X-RapidAPI-Key": "YOUR_API_KEY",
    "X-RapidAPI-Host": "astrologer.p.rapidapi.com"
}

def generate_horoscope(user_birth_data, target_date=None):
    """
    Generate a personalized horoscope for a user.

    Args:
        user_birth_data: dict with name, year, month, day, hour, minute,
                         city, nation, longitude, latitude, timezone
        target_date: dict with year, month, day, hour, minute (defaults to now)

    Returns:
        dict with natal_summary, transit_context, and chart_data
    """
    if target_date is None:
        now = datetime.utcnow()
        target_date = {
            "year": now.year, "month": now.month, "day": now.day,
            "hour": now.hour, "minute": now.minute
        }

    # 1. Fetch natal context for the base personality reading
    natal_resp = requests.post(
        f"{API_URL}/context/birth-chart",
        json={"subject": user_birth_data},
        headers=HEADERS
    )
    natal_context = natal_resp.json()

    # 2. Fetch transit context for today's influences
    transit_subject = {
        "name": "Transit",
        **target_date,
        "city": user_birth_data["city"],
        "nation": user_birth_data["nation"],
        "longitude": user_birth_data["longitude"],
        "latitude": user_birth_data["latitude"],
        "timezone": user_birth_data["timezone"]
    }

    transit_resp = requests.post(
        f"{API_URL}/context/transit",
        json={
            "first_subject": user_birth_data,
            "transit_subject": transit_subject
        },
        headers=HEADERS
    )
    transit_context = transit_resp.json()

    # 3. Extract key natal info for the summary
    subject = natal_context["chart_data"]["subject"]
    natal_summary = {
        "sun_sign": subject["sun"]["sign"],
        "moon_sign": subject["moon"]["sign"],
        "rising_sign": subject["ascendant"]["sign"],
        "sun_house": subject["sun"]["house"],
        "moon_house": subject["moon"]["house"]
    }

    return {
        "natal_summary": natal_summary,
        "natal_xml": natal_context["context"],
        "transit_xml": transit_context["context"],
        "transit_aspects": transit_context["chart_data"]["aspects"]
    }


# Usage
horoscope = generate_horoscope({
    "name": "Alice",
    "year": 1990, "month": 3, "day": 21,
    "hour": 14, "minute": 30,
    "city": "New York", "nation": "US",
    "longitude": -74.006, "latitude": 40.7128,
    "timezone": "America/New_York"
})

print(f"Sun: {horoscope['natal_summary']['sun_sign']}")
print(f"Moon: {horoscope['natal_summary']['moon_sign']}")
print(f"Rising: {horoscope['natal_summary']['rising_sign']}")
print(f"Transit aspects found: {len(horoscope['transit_aspects'])}")
print(f"Transit XML length: {len(horoscope['transit_xml'])} chars")

# Feed natal_xml and transit_xml to your LLM to generate the final reading

JavaScript (Complete Horoscope Generator) #

const API_URL = "https://astrologer.p.rapidapi.com/api/v5";
const HEADERS = {
  "Content-Type": "application/json",
  "X-RapidAPI-Key": "YOUR_API_KEY",
  "X-RapidAPI-Host": "astrologer.p.rapidapi.com"
};

async function generateHoroscope(userBirthData, targetDate = null) {
  if (!targetDate) {
    const now = new Date();
    targetDate = {
      year: now.getUTCFullYear(),
      month: now.getUTCMonth() + 1,
      day: now.getUTCDate(),
      hour: now.getUTCHours(),
      minute: now.getUTCMinutes()
    };
  }

  // Fetch natal context and transit context in parallel
  const [natalResp, transitResp] = await Promise.all([
    fetch(`${API_URL}/context/birth-chart`, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify({ subject: userBirthData })
    }),
    fetch(`${API_URL}/context/transit`, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify({
        first_subject: userBirthData,
        transit_subject: {
          name: "Transit",
          ...targetDate,
          city: userBirthData.city,
          nation: userBirthData.nation,
          longitude: userBirthData.longitude,
          latitude: userBirthData.latitude,
          timezone: userBirthData.timezone
        }
      })
    })
  ]);

  const [natalContext, transitContext] = await Promise.all([
    natalResp.json(),
    transitResp.json()
  ]);

  const subject = natalContext.chart_data.subject;

  return {
    natalSummary: {
      sunSign: subject.sun.sign,
      moonSign: subject.moon.sign,
      risingSign: subject.ascendant.sign
    },
    natalXml: natalContext.context,
    transitXml: transitContext.context,
    transitAspects: transitContext.chart_data.aspects
  };
}

// Usage
const horoscope = await generateHoroscope({
  name: "Alice",
  year: 1990,
  month: 3,
  day: 21,
  hour: 14,
  minute: 30,
  city: "New York",
  nation: "US",
  longitude: -74.006,
  latitude: 40.7128,
  timezone: "America/New_York"
});

console.log(`Sun: ${horoscope.natalSummary.sunSign}`);
console.log(`Transit aspects: ${horoscope.transitAspects.length}`);
// Feed natalXml + transitXml to your LLM

Adding Solar and Lunar Return Forecasts #

For weekly or monthly horoscopes, combine transit data with return charts.

The solar return context endpoint generates a yearly forecast based on the moment the Sun returns to its exact natal position. The lunar return context endpoint does the same for the Moon, producing a monthly snapshot.

# Fetch the solar return for the current year
solar_return = requests.post(
    f"{API_URL}/context/solar-return",
    json={
        "subject": user_birth_data,
        "year": 2026
    },
    headers=HEADERS
).json()

# Fetch the lunar return for the current month
lunar_return = requests.post(
    f"{API_URL}/context/lunar-return",
    json={
        "subject": user_birth_data,
        "year": 2026,
        "month": 4
    },
    headers=HEADERS
).json()

# Combine all three contexts for a rich horoscope
# natal_xml + transit_xml + solar_return["context"] + lunar_return["context"]

Integrating Moon Phase Data #

Add lunar awareness to your horoscopes with the moon phase endpoint.

moon = requests.post(
    f"{API_URL}/moon-phase",
    json={
        "year": 2026, "month": 4, "day": 21,
        "hour": 12, "minute": 0,
        "latitude": 40.7128, "longitude": -74.006,
        "timezone": "America/New_York"
    },
    headers=HEADERS
).json()

overview = moon["moon_phase_overview"]["moon"]
print(f"Phase: {overview['phase_name']}")
print(f"Illumination: {overview['illumination']}")
print(f"Moon sign: {overview['zodiac']['moon_sign']}")

Including moon phase information adds a natural rhythm to your horoscope content that users intuitively connect with.

Next Steps #

Build With Astrology Data

Natal charts, synastry, transits & AI interpretations — all via REST API.

Try Astrologer API