Try Astrologer API

Subscribe to support and grow the project.

Zodiac Sign API: Get Sun, Moon, and Rising Signs #

“What’s my zodiac sign?” is the most common entry point into astrology, and it is the feature that drives the most traffic to astrology apps. But modern users expect more than just their Sun sign. They want their Moon sign (emotional nature), their Rising sign or Ascendant (outward persona), and increasingly, their full planetary breakdown – Mercury sign, Venus sign, Mars sign, and beyond. The Astrologer API calculates all of these from a single request, using precise ephemeris data rather than simplified date-range lookups.

This tutorial shows how to get zodiac sign data from birth information, build a “What’s My Sign” feature, and work with the extended planetary data that the API returns.

Why Date-Range Lookups Are Not Enough #

Most “what’s your zodiac sign” implementations use hardcoded date ranges: born between March 21 and April 19, you are an Aries. This works for a rough Sun sign estimate, but it fails in several important ways:

Cusp dates shift each year. The Sun does not enter Aries at exactly the same moment every year. In some years, someone born on March 20 is a Pisces; in others, they are an Aries. Only an ephemeris calculation gives the correct answer.

Moon signs require precise calculation. The Moon changes signs approximately every 2.5 days and moves about 13 degrees per day. You cannot determine a Moon sign from the date alone – you need the exact time and a proper ephemeris.

Rising signs change every 2 hours. The Ascendant (Rising sign) is the zodiac sign on the eastern horizon at the moment of birth. It cycles through all 12 signs in 24 hours. Without the birth time and location, you cannot calculate it at all.

The subject endpoint handles all of these calculations with high-precision Swiss Ephemeris data.

Getting Zodiac Signs from Birth Data #

Before you begin, get your API key on RapidAPI.

Python #

import requests

url = "https://astrologer.p.rapidapi.com/api/v5/subject"

headers = {
    "Content-Type": "application/json",
    "X-RapidAPI-Key": "YOUR_API_KEY",
    "X-RapidAPI-Host": "astrologer.p.rapidapi.com"
}

payload = {
    "subject": {
        "name": "Maria",
        "year": 1994,
        "month": 6,
        "day": 21,
        "hour": 5,
        "minute": 30,
        "city": "Barcelona",
        "nation": "ES",
        "longitude": 2.1734,
        "latitude": 41.3851,
        "timezone": "Europe/Madrid"
    }
}

response = requests.post(url, json=payload, headers=headers)
data = response.json()

subject = data["subject"]

# The Big Three
sun_sign = subject["sun"]["sign"]
moon_sign = subject["moon"]["sign"]
rising_sign = subject["ascendant"]["sign"]

print(f"Sun sign:    {sun_sign} ({subject['sun']['element']})")
print(f"Moon sign:   {moon_sign} ({subject['moon']['element']})")
print(f"Rising sign: {rising_sign} ({subject['ascendant']['element']})")

# Full planetary breakdown
planets = ["mercury", "venus", "mars", "jupiter", "saturn",
           "uranus", "neptune", "pluto"]

print("\nFull Planetary Signs:")
for planet_name in planets:
    planet = subject[planet_name]
    if planet:
        retro = " (R)" if planet["retrograde"] else ""
        print(f"  {planet['name']:>10s}: {planet['sign']} "
              f"at {planet['position']:.1f}°{retro} "
              f"in {planet['house'].replace('_', ' ')}")

JavaScript #

const url = "https://astrologer.p.rapidapi.com/api/v5/subject";

const payload = {
  subject: {
    name: "Maria",
    year: 1994,
    month: 6,
    day: 21,
    hour: 5,
    minute: 30,
    city: "Barcelona",
    nation: "ES",
    longitude: 2.1734,
    latitude: 41.3851,
    timezone: "Europe/Madrid",
  },
};

const response = await fetch(url, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-RapidAPI-Key": "YOUR_API_KEY",
    "X-RapidAPI-Host": "astrologer.p.rapidapi.com",
  },
  body: JSON.stringify(payload),
});

const data = await response.json();
const subject = data.subject;

// The Big Three
console.log(`Sun:    ${subject.sun.sign} (${subject.sun.element})`);
console.log(`Moon:   ${subject.moon.sign} (${subject.moon.element})`);
console.log(`Rising: ${subject.ascendant.sign} (${subject.ascendant.element})`);

// All planet signs
const planets = [
  "mercury",
  "venus",
  "mars",
  "jupiter",
  "saturn",
  "uranus",
  "neptune",
  "pluto",
];

console.log("\nFull Planetary Signs:");
for (const name of planets) {
  const planet = subject[name];
  if (planet) {
    const retro = planet.retrograde ? " (R)" : "";
    console.log(
      `  ${planet.name}: ${planet.sign} at ${planet.position.toFixed(1)}°${retro}`
    );
  }
}

Understanding the Response #

Each planet object in the response contains rich data. Here is what the Sun entry looks like:

{
  "name": "Sun",
  "quality": "Mutable",
  "element": "Air",
  "sign": "Gem",
  "sign_num": 2,
  "position": 29.87,
  "abs_pos": 89.87,
  "emoji": "♊️",
  "house": "First_House",
  "retrograde": false,
  "speed": 0.9537,
  "declination": 23.44,
  "magnitude": null
}

Key fields for zodiac sign features:

Field Description
sign Three-letter sign abbreviation: Ari, Tau, Gem, Can, Leo, Vir, Lib, Sco, Sag, Cap, Aqu, Pis
sign_num Zero-indexed sign number (Aries = 0, Pisces = 11)
position Degrees within the sign (0-30)
element Fire, Earth, Air, or Water
quality Cardinal, Fixed, or Mutable
house Which house this planet occupies (e.g., "First_House", "Tenth_House")
retrograde true if the planet appears to move backward

Notice the position field: a Sun at 29.87 degrees of Gemini means this person was born very close to the Gemini-Cancer cusp. With a date-range lookup, you might misclassify them. The API’s ephemeris calculation gives the definitive answer.

Building a “What’s My Sign” Feature #

Here is a complete function that takes birth data and returns a user-friendly sign profile:

Python #

import requests

SIGN_NAMES = {
    "Ari": "Aries", "Tau": "Taurus", "Gem": "Gemini",
    "Can": "Cancer", "Leo": "Leo", "Vir": "Virgo",
    "Lib": "Libra", "Sco": "Scorpio", "Sag": "Sagittarius",
    "Cap": "Capricorn", "Aqu": "Aquarius", "Pis": "Pisces"
}

SIGN_EMOJIS = {
    "Ari": "", "Tau": "", "Gem": "",
    "Can": "", "Leo": "", "Vir": "",
    "Lib": "", "Sco": "", "Sag": "",
    "Cap": "", "Aqu": "", "Pis": ""
}

def get_sign_profile(birth_data):
    """Get a complete zodiac sign profile from birth data."""
    url = "https://astrologer.p.rapidapi.com/api/v5/subject"
    headers = {
        "Content-Type": "application/json",
        "X-RapidAPI-Key": "YOUR_API_KEY",
        "X-RapidAPI-Host": "astrologer.p.rapidapi.com"
    }

    response = requests.post(
        url,
        json={"subject": birth_data},
        headers=headers
    )
    subject = response.json()["subject"]

    sun = subject["sun"]
    moon = subject["moon"]
    rising = subject["ascendant"]

    profile = {
        "sun_sign": {
            "sign": SIGN_NAMES[sun["sign"]],
            "emoji": SIGN_EMOJIS[sun["sign"]],
            "element": sun["element"],
            "quality": sun["quality"],
            "degree": round(sun["position"], 1),
            "house": sun["house"].replace("_", " "),
        },
        "moon_sign": {
            "sign": SIGN_NAMES[moon["sign"]],
            "emoji": SIGN_EMOJIS[moon["sign"]],
            "element": moon["element"],
            "quality": moon["quality"],
            "degree": round(moon["position"], 1),
            "house": moon["house"].replace("_", " "),
        },
        "rising_sign": {
            "sign": SIGN_NAMES[rising["sign"]],
            "emoji": SIGN_EMOJIS[rising["sign"]],
            "element": rising["element"],
            "quality": rising["quality"],
            "degree": round(rising["position"], 1),
        },
        "all_planets": {}
    }

    # Add all personal planets
    for key in ["mercury", "venus", "mars", "jupiter", "saturn"]:
        planet = subject[key]
        if planet:
            profile["all_planets"][planet["name"]] = {
                "sign": SIGN_NAMES[planet["sign"]],
                "degree": round(planet["position"], 1),
                "retrograde": planet["retrograde"],
                "house": planet["house"].replace("_", " "),
            }

    return profile


# Example
birth = {
    "name": "Maria",
    "year": 1994, "month": 6, "day": 21,
    "hour": 5, "minute": 30,
    "city": "Barcelona", "nation": "ES",
    "longitude": 2.1734, "latitude": 41.3851,
    "timezone": "Europe/Madrid"
}

profile = get_sign_profile(birth)
print(f"{profile['sun_sign']['emoji']} Sun in {profile['sun_sign']['sign']} "
      f"({profile['sun_sign']['degree']}°)")
print(f"{profile['moon_sign']['emoji']} Moon in {profile['moon_sign']['sign']} "
      f"({profile['moon_sign']['degree']}°)")
print(f"{profile['rising_sign']['emoji']} Rising in {profile['rising_sign']['sign']} "
      f"({profile['rising_sign']['degree']}°)")

JavaScript #

const SIGN_NAMES = {
  Ari: "Aries",
  Tau: "Taurus",
  Gem: "Gemini",
  Can: "Cancer",
  Leo: "Leo",
  Vir: "Virgo",
  Lib: "Libra",
  Sco: "Scorpio",
  Sag: "Sagittarius",
  Cap: "Capricorn",
  Aqu: "Aquarius",
  Pis: "Pisces",
};

async function getSignProfile(birthData) {
  const url = "https://astrologer.p.rapidapi.com/api/v5/subject";

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-RapidAPI-Key": "YOUR_API_KEY",
      "X-RapidAPI-Host": "astrologer.p.rapidapi.com",
    },
    body: JSON.stringify({ subject: birthData }),
  });

  const data = await response.json();
  const subject = data.subject;

  return {
    sunSign: {
      sign: SIGN_NAMES[subject.sun.sign],
      element: subject.sun.element,
      degree: subject.sun.position.toFixed(1),
      house: subject.sun.house.replace(/_/g, " "),
    },
    moonSign: {
      sign: SIGN_NAMES[subject.moon.sign],
      element: subject.moon.element,
      degree: subject.moon.position.toFixed(1),
      house: subject.moon.house.replace(/_/g, " "),
    },
    risingSign: {
      sign: SIGN_NAMES[subject.ascendant.sign],
      element: subject.ascendant.element,
      degree: subject.ascendant.position.toFixed(1),
    },
  };
}

// Usage
const profile = await getSignProfile({
  name: "Maria",
  year: 1994,
  month: 6,
  day: 21,
  hour: 5,
  minute: 30,
  city: "Barcelona",
  nation: "ES",
  longitude: 2.1734,
  latitude: 41.3851,
  timezone: "Europe/Madrid",
});

console.log(`Sun in ${profile.sunSign.sign} (${profile.sunSign.degree}°)`);
console.log(`Moon in ${profile.moonSign.sign} (${profile.moonSign.degree}°)`);
console.log(`Rising in ${profile.risingSign.sign} (${profile.risingSign.degree}°)`);

Element and Quality Distribution #

Beyond individual signs, the element and quality distribution across the entire chart reveals important personality patterns. Use the natal chart data endpoint to get this analysis:

url = "https://astrologer.p.rapidapi.com/api/v5/chart-data/natal"

response = requests.post(url, json={"subject": birth_data}, headers=headers)
chart = response.json()["chart_data"]

elements = chart["element_distribution"]
qualities = chart["quality_distribution"]

print(f"Fire:  {elements['fire_percentage']}%")
print(f"Earth: {elements['earth_percentage']}%")
print(f"Air:   {elements['air_percentage']}%")
print(f"Water: {elements['water_percentage']}%")

print(f"\nCardinal: {qualities['cardinal_percentage']}%")
print(f"Fixed:    {qualities['fixed_percentage']}%")
print(f"Mutable:  {qualities['mutable_percentage']}%")

A chart heavily weighted toward Fire and Cardinal energy suggests a very different personality than one dominated by Water and Mutable energy, even if they share the same Sun sign. This is the kind of depth that sets a quality astrology app apart.

Handling Edge Cases #

Unknown birth time. If a user does not know their birth time, default to 12:00 noon. This maximizes the chance of getting the correct Moon sign (though it is not guaranteed) and provides reasonable house placements. Clearly indicate to the user that the Rising sign and house placements require an exact birth time.

Cusp births. When the position field is very close to 0 or 30 degrees, the person was born near a sign cusp. You can flag this in your UI: “Sun at 29.9° Gemini (very close to Cancer)”. The API’s calculation is definitive – they are whichever sign the ephemeris says – but users appreciate knowing they are on a cusp.

Retrograde planets. The retrograde field is important for user-facing features. Mercury retrograde is culturally well-known, but Venus and Mars retrogrades also carry astrological significance. Display the retrograde status prominently.

Sidereal calculations. By default, the API uses the Tropical zodiac (the Western standard). If your app serves users who follow Vedic or sidereal astrology, set zodiac_type to "Sidereal" and provide a sidereal_mode (e.g., "LAHIRI"). The sign results will differ, often by about one sign.

vedic_payload = {
    "subject": {
        "name": "Maria",
        "year": 1994, "month": 6, "day": 21,
        "hour": 5, "minute": 30,
        "city": "Barcelona", "nation": "ES",
        "longitude": 2.1734, "latitude": 41.3851,
        "timezone": "Europe/Madrid",
        "zodiac_type": "Sidereal",
        "sidereal_mode": "LAHIRI"
    }
}

Going Deeper: From Signs to Full Chart Data #

Once users know their basic signs, many want to explore further. The natural progression is:

  1. Signs (this tutorial) – Sun, Moon, Rising via /api/v5/subject
  2. Full chart data – aspects, house positions, distributions via /api/v5/chart-data/birth-chart (natal chart data docs)
  3. Visual charts – SVG natal chart via /api/v5/chart/birth-chart (natal chart docs)
  4. AI interpretations – natural language readings via /api/v5/context/birth-chart (natal context docs)

Each step deepens engagement and increases the value your app provides. Start with the simple sign lookup described here, and layer on complexity as users demonstrate interest.

For the full API reference, visit the Astrologer API landing page. Get your API key on RapidAPI to start building.

Build With Astrology Data

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

Try Astrologer API