Log in/Sign up

Vedic Astrology REST API

A JSON HTTP API over the Jagannatha Hora calculation engine. Send a birth date, time and place; get back the full Vedic chart — 23 divisional charts, dashas, yogas, doshas, ashtakavarga, transits, panchang, muhurta and compatibility. Seventeen endpoints, sixteen of them open with no API key.

Last verified against the service source on 2026-08-31.

Start here

The API is live at https://jagannatha-hora-359167915530.europe-west1.run.app. There is nothing to sign up for. Send a POST with a JSON body and read the JSON that comes back. This one command returns a complete Vedic birth chart:

The whole thing, in one call
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/horoscope" \
  -H "content-type: application/json" \
  -d '{"date":"1993-07-27","time":"13:51:00","place":"Hassan",
       "latitude":13.0,"longitude":76.1,"timezone":5.5}'

If you do not know the coordinates for a birth place, resolve it first with GET /location/search, which searches a local index of 162,000+ cities and returns latitude, longitude and the timezone offset. That is the normal two-step flow: one lookup, then one calculation.

If you are wiring this into an AI agent rather than an application, use the MCP server instead. It exposes the same engine as 32 individually callable tools, so a model can ask for just the dashas or just the ashtakavarga and get kilobytes back rather than the megabytes /horoscope returns.

Machine-readable versions of this page

This page exists in three more formats. They carry the same facts and are kept in sync by a test that fails the build when they drift.

Conventions that apply everywhere

No API key
Every endpoint on this list except POST /horoscope/pdf is open — no key, no token, no signup. CORS is wide open, so browser-side calls work too. The one gated endpoint uses a shared secret in the X-PDF-Secret header and is reserved for the site's own backend.
Rate limits are real
Limits are enforced per client IP by the service itself. The app-wide default is 200 requests/minute per IP; several endpoints set a tighter one, listed with each endpoint below. Exceeding a limit returns 429.
Everything is deterministic
The same inputs always produce the same output — these are astronomical calculations, not model inferences. Cache aggressively on your side. /panchang, /gochar and /gochar/events additionally send Cache-Control: public, max-age=21600.
Dates and times
Dates are YYYY-MM-DD and times are HH:MM:SS on a 24-hour clock, always as strings, never epoch numbers. Timezones are a decimal hour offset from UTC (5.5, not "Asia/Kolkata"). For birth moments the service re-resolves the historically correct, DST-aware offset from the coordinates and date, so a slightly wrong offset is usually corrected for you.
Responses are gzipped
Responses over 1 KB are gzip-compressed. This matters most for /horoscope, where it turns megabytes into hundreds of kilobytes. Send Accept-Encoding: gzip — every HTTP client does by default.
Validation happens twice
FastAPI rejects a structurally wrong body with 422 before the handler runs; the field validators then reject out-of-range values with 400 and a message naming the field and the bound. Read the detail string — it tells you exactly which field failed and why.

The shared birth-details block

Six endpoints take a birth chart as their subject: /horoscope, /horoscope/pdf, /gochara, /gochara/range, /muhurta and /saturn-timeline. They all start from the same block of fields, listed once here rather than repeated six times below. Only the first six are required; everything after elevation is a calculation setting with a sensible default, and you can ignore all of them until you have a reason not to.

One caveat on scope: /horoscope and /horoscope/pdf accept every field in this table. The transit and muhurta endpoints take the first eight — through ayanamsa_mode — plus their own parameters, and /saturn-timeline accepts a narrower ayanamsa_mode than the rest. Each endpoint below says what it adds.

Birth details and calculation settings, from BirthDetailsRequest in api_server.py
ParameterTypeRequiredDefaultNotes
datestringYesBirth date, YYYY-MM-DD. Year must be 1900-2100, month 1-12, day 1-31.
timestringYesBirth time, HH:MM:SS (24-hour). Hour 0-23, minute 0-59, second 0-59.
placestringYesBirth place name. Cosmetic on the transit endpoints, where it defaults to an empty string; required on /horoscope.
latitudenumberYesDecimal degrees, -90 to 90.
longitudenumberYesDecimal degrees, -180 to 180.
timezonenumberYesUTC offset in hours, -12 to 14 (5.5 for IST). The service re-resolves the historical, DST-correct offset for the birth moment, so this is a hint rather than the last word.
elevationnumberNo0.0Metres above sea level.
ayanamsa_modestringNo"LAHIRI"One of 21 modes (see the ayanamsa list below). Case-insensitive; uppercased server-side.
ayanamsa_valuenumberNonullCustom ayanamsa in degrees, -30.0 to 30.0. Only meaningful with SIDM_USER.
calculation_typestringNo"drik""drik" (modern) or "ss" (Surya Siddhanta). Case-insensitive.
node_typestringNo"mean"Rahu/Ketu node: "mean" or "true".
bhava_madhya_methodintegerNo1House system, 1-5: 1=Equal-Lagna-Middle, 2=Equal-Lagna-Start, 3=Sripati, 4=KP, 5=Whole-Sign.
languagestringNo"en"One of en, hi, ka, ta, te.
pravesha_typeintegerNo00=Natal, 1=Annual, 2=Tithi-Pravesha.
yearsintegerNo1Year number for annual/progressed charts, 1-120.
monthsintegerNo1Month number for monthly charts, 1-12.
sixty_hoursintegerNo160-hour period for hora progressions. The validator enforces 1-1000.
divisional_chart_factorintegerNo11-300. 1=D-1 (Rasi), 2=D-2 (Hora), 9=D-9 (Navamsa), and so on.
chart_methodintegerNo11-6. Which classical construction to use for the divisional chart.
base_rasiintegerNonull0-11 (0=Aries, 11=Pisces), for custom charts.
count_from_end_of_signbooleanNonullCount divisions backward in even signs.
varnada_methodintegerNo1Positive integer.
tamil_month_methodintegerNo30=Ravi Annasamy, 1=V4.3.5, 2=10AM Start, 3=Midday UTC.
kali_start_yearintegerNo131=Prabhava (South Indian) or 13=Pramaadhi (North Indian).

Ayanamsa modes

The ayanamsa_mode field accepts these 21 values. Lahiri is the default and by far the most widely used in Indian practice; the choice shifts every sidereal longitude in the chart, so pick one and stay with it rather than comparing across modes.

LAHIRI, TRUE_CITRA, TRUE_LAHIRI, KP, FAGAN, RAMAN, USHASHASHI, YUKTESHWAR, SURYASIDDHANTA, SURYASIDDHANTA_MSUN, ARYABHATA, ARYABHATA_MSUN, SS_CITRA, TRUE_REVATI, SS_REVATI, TRUE_PUSHYA, TRUE_MULA, KP-SENTHIL, SENTHIL, SUNDAR_SS, SIDM_USER

All 17 endpoints

Listed most useful first. Every one of them returns JSON except POST /horoscope/pdf, which returns a PDF.

POST/horoscope

The complete Vedic birth chart for a birth moment.

The workhorse endpoint. One call returns every calculation the engine performs for a chart: 23 divisional charts, nakshatras and padas, yogas, doshas, chara karakas, sahams, upagrahas, special lagnas, sphutas, arudhas, house relationships, planetary states, five kinds of bala, ashtakavarga, graha and rasi dashas, Saturn transits and a longevity band. The response is large — on the order of 3.5 MB of JSON — and gzip-compressed on the wire. Computed fresh every time; the same inputs always produce the same output, so cache it on your side.

Rate limit
30 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
birth_details, horoscope

Takes the shared birth-details block.

Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/horoscope" \
  -H "content-type: application/json" \
  -d '{
    "date": "1993-07-27",
    "time": "13:51:00",
    "place": "Hassan",
    "latitude": 13.0,
    "longitude": 76.1,
    "timezone": 5.5
  }'
Example response — TRUNCATED excerpt, see the full key list below
{
  "birth_details": {
    "date": "1993-07-27", "time": "13:51:00", "place": "Hassan",
    "latitude": 13.0, "longitude": 76.1, "timezone": 5.5
  },
  "horoscope": {
    "ayanamsa_value": 23.8117,
    "julian_day": 2449196.0770833,
    "nakshatra_pada": {
      "Ascendant": { "nakshatra": "Swati", "nakshatra_number": 15,
                     "pada": 2, "nakshatra_lord": "Rahu",
                     "degrees_in_nakshatra": 4.1832 }
    },
    "divisional_charts": {
      "D-1_rasi": { "Ascendant": { "sign": "Libra", "longitude": 10.8483 } }
    }
    /* ...29 more top-level keys, listed in full above... */
  }
}
  • Ask for a divisional chart with divisional_chart_factor, but note that divisional_charts already contains all 23 vargas regardless.
  • If you only need one slice of this — the dashas, the ashtakavarga, the yogas — the MCP server exposes each as its own tool and returns kilobytes instead of megabytes.

POST/horoscope/pdf

The same chart rendered as a downloadable PDF report.

Runs the identical calculation path as POST /horoscope — there is no second computation and no divergence — then typesets it as a designed report and returns application/pdf with a Content-Disposition of inline; filename="janma-kundali.pdf". Dosha presence and the birth panchanga are computed additionally for the cover; the /horoscope JSON response is unaffected. This is the one gated endpoint on the service.

Rate limit
10 requests/minute per IP
Authentication
X-PDF-Secret request header, compared against the server's HOROSCOPE_PDF_SECRET with a constant-time comparison. Fails closed — no secret configured on the server means every call is rejected. This endpoint is for the site's own backend, not for third-party integration.
Response keys
(binary application/pdf)

Takes the shared birth-details block, plus:

Body fields for /horoscope/pdf
ParameterTypeRequiredDefaultNotes
namestringNonullName printed on the cover. Maximum 80 characters.
chart_stylestringNo"north""north" or "south". Anything else is rejected.
photostringNonullBase64 cover photo; validated and re-encoded server-side.
online_urlstringNonullMaximum 300 characters. Renders a "View online" button at the top of the PDF.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/horoscope/pdf" \
  -H "content-type: application/json" \
  -H "x-pdf-secret: $HOROSCOPE_PDF_SECRET" \
  -d '{
    "date": "1993-07-27", "time": "13:51:00", "place": "Hassan",
    "latitude": 13.0, "longitude": 76.1, "timezone": 5.5,
    "name": "Asha R", "chart_style": "south"
  }' --output janma-kundali.pdf
Example response
HTTP/1.1 200 OK
content-type: application/pdf
content-disposition: inline; filename="janma-kundali.pdf"

%PDF-1.4 ...binary...

POST/marriage-match

Ashtakoota (North Indian) and 10 Porutham (South Indian) compatibility for two charts.

Takes two birth charts and scores them under both classical systems at once, then adds the Manglik-family dosha load for each person and the Beeja/Kshetra sphutas. The North Indian block scores the eight kutas out of 36; the South Indian block reports each of the ten poruthams as matched or not, with the minimum-porutham flag. It returns the computation, not a verdict.

Rate limit
Inherits the app default (200 requests/minute per IP) — this endpoint declares no limiter of its own
Authentication
None. Open endpoint, no key required.
Response keys
boy, girl, north_indian, south_indian, boy_dosha_data, girl_dosha_data, beeja_rasi, kshetra_rasi
Body fields for /marriage-match
ParameterTypeRequiredDefaultNotes
boy_birth_detailsobjectYesdate, time, latitude, longitude, timezone (all required) and elevation (default 0.0). Note this nested model has NO place field and takes no calculation settings.
girl_birth_detailsobjectYesSame shape as boy_birth_details.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/marriage-match" \
  -H "content-type: application/json" \
  -d '{
    "boy_birth_details": {
      "date": "1993-07-27", "time": "13:51:00",
      "latitude": 13.0, "longitude": 76.1, "timezone": 5.5
    },
    "girl_birth_details": {
      "date": "1995-11-02", "time": "06:20:00",
      "latitude": 12.9716, "longitude": 77.5946, "timezone": 5.5
    }
  }'
Example response
{
  "north_indian": {
    "match_system": "North Indian (Ashtakoota)",
    "overall_compatibility": {
      "total_score": 24.5, "max_possible_score": 36,
      "percentage": 68.06, "rating": "Good"
    }
  },
  "south_indian": {
    "match_system": "South Indian (10 Porutham)",
    "ten_porutham": { "dina_porutham": true, "gana_porutham": true,
                      "rajju_porutham": false },
    "overall_compatibility": {
      "matched_count": 7, "total_porutham": 10,
      "percentage": 70.0, "rating": "Good",
      "minimum_porutham_satisfied": true
    }
  },
  "boy_dosha_data": { "mars_dosha": 0.0, "sun_dosha": 0.0,
                      "saturn_dosha": 0.5, "rahu_ketu_dosha": 0.0,
                      "total_dosha": 0.5 },
  "beeja_rasi": "Scorpio",
  "kshetra_rasi": "Cancer"
}
  • The two nested birth-detail objects are a different, smaller model than the one /horoscope takes — no place, no ayanamsa_mode, no chart settings.

POST/panchang

The full daily panchang for a date and place. No birth chart needed.

The five limbs — vara, tithi, nakshatra, yoga, karana — plus sun and moon rise/set, day length, the auspicious and inauspicious muhurta windows, day and night Choghadiya, the lunar month in both Amanta and Purnimanta reckoning, ritu, ayana, Shaka and Vikram Samvat with the sixty-year cycle name, and the Moon and Sun signs. Deterministic for a given date and place; the response carries Cache-Control: public, max-age=21600.

Rate limit
60 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
panchang
Body fields for /panchang
ParameterTypeRequiredDefaultNotes
datestringYesYYYY-MM-DD. Rejects dates that do not exist on the calendar, e.g. 2026-02-31.
latitudenumberYes-90 to 90, and additionally |latitude| must be 66 or less — panchang is sunrise-anchored and undefined beyond the polar circles.
longitudenumberYes-180 to 180.
timezonenumberYesUTC offset, -12 to 14.
placestringNo""Cosmetic.
ayanamsa_modestringNo"LAHIRI"Any allowed ayanamsa mode.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/panchang" \
  -H "content-type: application/json" \
  -d '{
    "date": "2026-07-13",
    "latitude": 28.6139, "longitude": 77.2090,
    "timezone": 5.5, "place": "New Delhi"
  }'
Example response
{
  "panchang": {
    "date": "2026-07-13",
    "vara": "Monday",
    "tithi": { "name": "Krishna Ekadashi", "ends": "2026-07-13 21:44" },
    "nakshatra": { "name": "Rohini", "pada": 3 },
    "yoga": { "name": "Vyaghata" },
    "karana": { "name": "Balava" },
    "sunrise": "05:32", "sunset": "19:19",
    "moonrise": "01:12", "moonset": "15:48",
    "rahu_kalam": { "start": "07:15", "end": "08:58" },
    "abhijit": { "start": "11:58", "end": "12:53" },
    "lunar_month": { "amanta": "Ashadha", "purnimanta": "Shravana" }
  }
}
  • The polar restriction is a hard 400, not a degraded result: beyond ±66° the solar-crossing search would not terminate.

POST/gochar

Live planetary transits for a date and place. Birthless.

Where the natal chart is irrelevant: the nine grahas with sign and degree, nakshatra and pada, direct or retrograde motion, combustion flag, classical dignity (exalted, debilitated, moolatrikona, own), daily speed, and the date each next changes sign, nakshatra and pada — plus the Moon and Sun signs and the set of planets currently retrograde. Positions are geocentric and therefore identical worldwide at an instant; the place's timezone governs only what counts as "today" and how the dates are displayed. Cached 6 hours.

Rate limit
60 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
gochar
Body fields for /gochar
ParameterTypeRequiredDefaultNotes
datestringYesYYYY-MM-DD; must be a real calendar date.
latitudenumberYes-90 to 90. No polar restriction — gochar is sunrise-free and well defined at every latitude.
longitudenumberYes-180 to 180.
timezonenumberYesUTC offset, -12 to 14.
placestringNo""Cosmetic.
ayanamsa_modestringNo"LAHIRI"Any allowed ayanamsa mode.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/gochar" \
  -H "content-type: application/json" \
  -d '{
    "date": "2026-07-14",
    "latitude": 28.6139, "longitude": 77.2090,
    "timezone": 5.5, "place": "New Delhi"
  }'
Example response
{
  "gochar": {
    "date": "2026-07-14",
    "planets": [
      { "name": "Saturn", "sign": "Pisces", "degree": 8.42,
        "nakshatra": "Uttara Bhadrapada", "pada": 1,
        "retrograde": true, "combust": false,
        "dignity": "own", "daily_speed": -0.019,
        "next_sign_change": "2027-03-29" }
    ],
    "moon_sign": "Taurus",
    "sun_sign": "Gemini",
    "retrograde_now": ["Saturn", "Rahu", "Ketu"]
  }
}

POST/gochar/events

Upcoming transit events over a date range. Birthless.

One merged, date-sorted list of sign changes for every requested planet, nakshatra changes for all of them except the Moon (which changes nakshatra roughly daily and would drown the list), and retrograde stations for Mars, Mercury, Jupiter, Venus and Saturn. Event dates are expressed in the requested timezone. Computed fresh.

Rate limit
60 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
gochar_events
Body fields for /gochar/events
ParameterTypeRequiredDefaultNotes
from_datestringYesYYYY-MM-DD, a real calendar date.
to_datestringYesYYYY-MM-DD. The range is capped at 90 days.
latitudenumberYes-90 to 90. Used for the display timezone.
longitudenumberYes-180 to 180.
timezonenumberYesUTC offset used to express event dates, -12 to 14.
placestringNo""Cosmetic.
planetsstring[]NonullPlanet names. Defaults to all nine: Sun, Moon, Mars, Mercury, Jupiter, Venus, Saturn, Rahu, Ketu.
ayanamsa_modestringNo"LAHIRI"Any allowed ayanamsa mode.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/gochar/events" \
  -H "content-type: application/json" \
  -d '{
    "from_date": "2026-07-14", "to_date": "2026-08-13",
    "latitude": 28.6139, "longitude": 77.2090, "timezone": 5.5,
    "planets": ["Jupiter", "Saturn", "Mercury"]
  }'
Example response
{
  "gochar_events": [
    { "date": "2026-07-18", "planet": "Mercury",
      "type": "nakshatra_change", "to": "Pushya" },
    { "date": "2026-07-26", "planet": "Mercury",
      "type": "retrograde_station", "direction": "retrograde" },
    { "date": "2026-08-04", "planet": "Jupiter",
      "type": "sign_change", "from": "Cancer", "to": "Leo" }
  ]
}

POST/gochara

A transit snapshot read against a natal chart, for one target date.

The natal counterpart to /gochar. Returns the panchanga for the target moment; the muhurta windows (sunrise, sunset, solar noon, Abhijit, and the Rahu, Gulika and Yamaganda periods to avoid); all nine planets with sign, degree, nakshatra and pada, retrograde and combust flags, and their house from both the natal Moon and the natal Lagna; and the person-relative strengths — Tara Bala from Moon, Sun and Lagna, Chandra Bala, and Sade Sati state. The event location is optional and defaults to the birth location. Computed fresh, no cache.

Rate limit
60 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
birth_details, gochara

Takes the shared birth-details block, plus:

Body fields for /gochara
ParameterTypeRequiredDefaultNotes
target_datestringYesYYYY-MM-DD — the transit moment.
target_timestringNo"12:00:00"HH:MM:SS. Defaults to local noon.
event_placestringNonullFalls back to the birth place.
event_latitudenumberNonull-90 to 90. Falls back to the birth latitude.
event_longitudenumberNonull-180 to 180. Falls back to the birth longitude.
event_timezonenumberNonull-12 to 14. Falls back to the birth timezone.
event_elevationnumberNonullMetres. Falls back to the birth elevation.
includestring[]NonullEnrichment sections. "ashtakavarga" is the recognised value; omit the field for the default, which includes it.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/gochara" \
  -H "content-type: application/json" \
  -d '{
    "date": "1991-08-18", "time": "11:13:00", "place": "Hassan",
    "latitude": 13.0071, "longitude": 76.0962, "timezone": 5.5,
    "target_date": "2026-06-25",
    "event_place": "Bangalore",
    "event_latitude": 12.9716, "event_longitude": 77.5946
  }'
Example response
{
  "birth_details": { "date": "1991-08-18", "time": "11:13:00",
                     "place": "Hassan", "latitude": 13.0071,
                     "longitude": 76.0962, "timezone": 5.5 },
  "gochara": {
    "panchanga": { "vara": "Thursday", "tithi": "Shukla Dasami",
                   "nakshatra": "Hasta", "yoga": "Siddha",
                   "karana": "Taitila", "paksha": "Shukla",
                   "rikta": false },
    "muhurta": { "sunrise": "05:58", "sunset": "18:49",
                 "abhijit": { "start": "12:01", "end": "12:53" },
                 "rahu_kalam": { "start": "13:47", "end": "15:20" } },
    "planets": [
      { "name": "Jupiter", "sign": "Cancer", "degree": 14.7,
        "nakshatra": "Aslesha", "pada": 1, "retrograde": false,
        "combust": false, "house_from_moon": 11, "house_from_lagna": 9 }
    ],
    "person": { "tara_bala": { "from_moon": "Sadhaka" },
                "chandra_bala": 6,
                "sade_sati": { "active": false } }
  }
}

POST/gochara/range

The same natal transit snapshot for every day in a range, in one call.

Built for the case where an AI agent should do the deciding. It returns the full per-day gochara detail — panchanga, muhurta windows including a clean Abhijit, planets, person-relative strength, ashtakavarga, gochara-phala and vedha — with no scoring and no "best date" verdict, so the caller reasons over the raw data itself. The natal reference is computed once and reused across the range. Computed fresh; the range is capped at 60 days.

Rate limit
30 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
gochara_range

Takes the shared birth-details block, plus:

Body fields for /gochara/range
ParameterTypeRequiredDefaultNotes
from_datestringYesYYYY-MM-DD.
to_datestringYesYYYY-MM-DD. Maximum 60 days from from_date.
time_of_daystringNo"12:00:00"HH:MM:SS used for each day. Defaults to local noon.
event_placestringNonullFalls back to the birth place.
event_latitudenumberNonullFalls back to the birth latitude.
event_longitudenumberNonullFalls back to the birth longitude.
event_timezonenumberNonullFalls back to the birth timezone.
event_elevationnumberNonullFalls back to the birth elevation.
includestring[]NonullEnrichment sections; the documented values are "ashtakavarga" and "gochara_phala". Omit for all of them, or pass [] for the lean snapshot.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/gochara/range" \
  -H "content-type: application/json" \
  -d '{
    "date": "1991-08-18", "time": "11:13:00", "place": "Hassan",
    "latitude": 13.0071, "longitude": 76.0962, "timezone": 5.5,
    "from_date": "2026-06-24", "to_date": "2026-06-30",
    "include": []
  }'
Example response
{
  "gochara_range": {
    "from_date": "2026-06-24",
    "to_date": "2026-06-30",
    "days": [
      { "date": "2026-06-24",
        "panchanga": { "vara": "Wednesday", "tithi": "Shukla Navami" },
        "planets": [ { "name": "Moon", "sign": "Virgo", "degree": 2.9 } ],
        "person": { "chandra_bala": 4 } }
    ]
  }
}
  • Pass include: [] when you want the lean snapshot — omitting the field is not the same thing, and gives you everything.

POST/saturn-timeline

Sade Sati and Dhaiyya timing, with month-by-month pressure and relief bands.

Returns the sign-based Sade Sati cycles for a chart, whether one is running right now and which of the three phases it is in, how many months remain, and — derived from Saturn's kakshya in the Ashtakavarga — the classical relief share. detail="full" adds the month-by-month bands, the interruptions and the segments. This is computation, not interpretation: dates and supported/unsupported flags, nothing else.

Rate limit
60 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
saturn_timeline
Body fields for /saturn-timeline
ParameterTypeRequiredDefaultNotes
datestringYesBirth date, YYYY-MM-DD.
timestringYesBirth time, HH:MM:SS.
placestringNo""Cosmetic.
latitudenumberYes-90 to 90.
longitudenumberYes-180 to 180.
timezonenumberYesUTC offset, -12 to 14.
ayanamsa_modestringNo"LAHIRI"ONLY "LAHIRI" or "RAMAN" here — narrower than every other endpoint. The two subsystems this endpoint drives honour only those two labels identically; anything else would silently compute a mismatched chart, so it is rejected with a 400 instead.
detailstringNo"summary""summary" or "full".
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/saturn-timeline" \
  -H "content-type: application/json" \
  -d '{
    "date": "1990-05-15", "time": "14:30:00", "place": "Bangalore",
    "latitude": 12.9716, "longitude": 77.5946, "timezone": 5.5,
    "detail": "summary"
  }'
Example response
{
  "saturn_timeline": {
    "moonSign": 7,
    "moonDegree": 18.44,
    "sadeSati": {
      "active": true,
      "currentPhase": "1st",
      "cycle": {
        "startDate": "2025-03-29", "endDate": "2032-06-03",
        "phases": [
          { "house": "12th", "startDate": "2025-03-29", "endDate": "2027-06-12" },
          { "house": "1st",  "startDate": "2027-06-12", "endDate": "2029-09-01" },
          { "house": "2nd",  "startDate": "2029-09-01", "endDate": "2032-06-03" }
        ]
      },
      "monthsRemaining": 70,
      "reliefShare": 0.38
    },
    "dhaiyya": { "active": false, "kind": null,
                 "startDate": null, "endDate": null },
    "nextCycle": null
  }
}

POST/planet-ingress

Every sidereal sign change for a set of planets across a date range.

Includes retrograde back-outs and re-entries, each crossing flagged so you can tell a temporary ingress from the one that sticks: a final_settling flag marks the last crossing of each sign boundary inside the window. Data only, computed fresh.

Rate limit
60 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
planet_ingress
Body fields for /planet-ingress
ParameterTypeRequiredDefaultNotes
planetsstring[]NonullPlanet names. Defaults to the slow movers: Saturn, Jupiter, Rahu, Ketu.
from_datestringYesYYYY-MM-DD.
to_datestringYesYYYY-MM-DD. The field description states a maximum of about 5 years.
timezonenumberNo0.0UTC offset used to express the crossing dates, -12 to 14. Defaults to UTC, not to IST.
ayanamsa_modestringNo"LAHIRI"Any allowed ayanamsa mode.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/planet-ingress" \
  -H "content-type: application/json" \
  -d '{
    "planets": ["Saturn", "Jupiter"],
    "from_date": "2025-01-01", "to_date": "2027-12-31",
    "timezone": 5.5
  }'
Example response
{
  "planet_ingress": [
    { "planet": "Saturn", "from_sign": "Aquarius", "to_sign": "Pisces",
      "date": "2025-03-29", "retrograde": false, "final_settling": true },
    { "planet": "Jupiter", "from_sign": "Gemini", "to_sign": "Cancer",
      "date": "2025-10-18", "retrograde": false, "final_settling": false },
    { "planet": "Jupiter", "from_sign": "Cancer", "to_sign": "Gemini",
      "date": "2025-12-05", "retrograde": true, "final_settling": false }
  ]
}
  • timezone defaults to 0.0 (UTC). A crossing near local midnight will look like it is on the previous day unless you pass your own offset.

POST/muhurta

Score every day in a range for an event, against classical muhurta rules and the native's own chart.

Scores each day by the classical factors — vara, nakshatra, tithi, paksha — and by the person's strength on that day: Tara Bala, Chandra Bala and Sade Sati state, per the rule table for the chosen event type. It returns the per-date factor data, a deterministic score with its breakdown and the weights used, and the auspicious Abhijit window plus the Rahu, Gulika and Yamaganda windows to avoid. It does not pick a date; the caller composes the recommendation. Computed fresh.

Rate limit
30 requests/minute per IP
Authentication
None. Open endpoint, no key required.
Response keys
muhurta

Takes the shared birth-details block, plus:

Body fields for /muhurta
ParameterTypeRequiredDefaultNotes
event_typestringYesMust be one of the tier-1 event types. Fetch the list from GET /muhurta/events; an unknown value is a 400 that names the valid set.
from_datestringYesScan start, YYYY-MM-DD.
to_datestringYesScan end, YYYY-MM-DD. Maximum 90 days.
event_placestringNonullFalls back to the birth place.
event_latitudenumberNonullFalls back to the birth latitude.
event_longitudenumberNonullFalls back to the birth longitude.
event_timezonenumberNonullFalls back to the birth timezone.
event_elevationnumberNonullFalls back to the birth elevation.
day_part_resolution_minutesintegerNo15Time-of-day granularity, 5 to 60 minutes.
respect_inauspicious_windowsbooleanNotrueReport the Rahu, Gulika and Yamaganda windows to avoid.
Example request
curl -X POST "https://jagannatha-hora-359167915530.europe-west1.run.app/muhurta" \
  -H "content-type: application/json" \
  -d '{
    "date": "1991-08-18", "time": "11:13:00", "place": "Hassan",
    "latitude": 13.0071, "longitude": 76.0962, "timezone": 5.5,
    "event_type": "griha_pravesha_rented",
    "from_date": "2026-06-24", "to_date": "2026-06-30",
    "event_place": "Bangalore",
    "event_latitude": 12.9716, "event_longitude": 77.5946
  }'
Example response
{
  "muhurta": {
    "event_type": "griha_pravesha_rented",
    "weights": { "vara": 2, "nakshatra": 3, "tithi": 2,
                 "tara_bala": 3, "chandra_bala": 2 },
    "dates": [
      { "date": "2026-06-26",
        "score": 11,
        "breakdown": { "vara": 2, "nakshatra": 3, "tithi": 2,
                       "tara_bala": 3, "chandra_bala": 1 },
        "factors": { "vara": "Friday", "nakshatra": "Uttara Phalguni",
                     "tithi": "Shukla Ekadashi", "paksha": "Shukla" },
        "windows": { "abhijit": { "start": "12:00", "end": "12:52" },
                     "avoid": [ { "name": "Rahu Kalam",
                                  "start": "10:40", "end": "12:16" } ] } }
    ]
  }
}

GET/muhurta/events

The event types POST /muhurta accepts, with their rule sources.

Call this first. It is the authoritative list of event_type values; sending anything not in it is a 400. Each entry carries a human label and the classical source the rule table came from.

Rate limit
Inherits the app default (200 requests/minute per IP)
Authentication
None. Open endpoint, no key required.
Response keys
events
Example request
curl "https://jagannatha-hora-359167915530.europe-west1.run.app/muhurta/events"
Example response
{
  "events": [
    { "event_type": "griha_pravesha_rented",
      "label": "Griha Pravesha (rented home)",
      "source": "Muhurta Chintamani" }
  ]
}
  • The page does not reproduce the event list because it is defined in the service's rule table (gochara_rules.EVENT_RULES) and changes there, not here. Fetch it.

Resolve a place name to coordinates and a timezone offset.

Resolves against a local index of 162,000+ world cities — instant, free, offline, and deterministic. No external geocoder is contacted. The single best match is returned. For "City, Country" inputs that do not match directly, the trailing qualifier is dropped and retried, so "Springfield, IL, US" falls back to "Springfield, IL". Use this to turn a user's typed birth place into the latitude, longitude and timezone the calculation endpoints need.

Rate limit
Inherits the app default (200 requests/minute per IP)
Authentication
None. Open endpoint, no key required.
Response keys
place, latitude, longitude, timezone, source, country, displayName, state (when known)
Query parameters for /location/search
ParameterTypeRequiredDefaultNotes
querystringYesThe place name. Minimum 2 characters after trimming, or 400.
qstringNonullAlias for query, accepted because external callers commonly send ?q=. If both are present, query wins.
Example request
curl "https://jagannatha-hora-359167915530.europe-west1.run.app/location/search?query=Chennai,%20India"
Example response
{
  "place": "Chennai",
  "latitude": 13.0475,
  "longitude": 80.2087,
  "timezone": 5.5,
  "source": "database",
  "country": "India",
  "displayName": "Chennai, Tamil Nadu, India",
  "state": "Tamil Nadu"
}
  • Comma handling is bounded at 6 segments, so a hostile many-comma query cannot trigger unbounded index scans on this public endpoint.
  • A miss is a 404 whose message suggests adding a country name or ISO code.

GET/location/autocomplete

Type-ahead suggestions from the same 162,000-city index.

Substring search over the local city index, for populating a birth-place picker as the user types. Much faster than an external geocoding API and free.

Rate limit
Inherits the app default (200 requests/minute per IP)
Authentication
None. Open endpoint, no key required.
Response keys
query, count, results
Query parameters for /location/autocomplete
ParameterTypeRequiredDefaultNotes
querystringYesPartial place name. Minimum 2 characters, or 400.
qstringNonullAlias for query.
limitintegerNo10Maximum results. Values above 50 are clamped to 50 rather than rejected.
Example request
curl "https://jagannatha-hora-359167915530.europe-west1.run.app/location/autocomplete?query=Chenn&limit=3"
Example response
{
  "query": "Chenn",
  "count": 3,
  "results": [
    { "place": "Chennai", "displayName": "Chennai, Tamil Nadu, India",
      "latitude": 13.0827, "longitude": 80.2707, "timezone": 5.5,
      "country": "India", "state": "Tamil Nadu" }
  ]
}

GET/

Service banner: name, version and a short endpoint index.

A cheap identity check. Returns the API's own name and version string plus a hand-maintained index of its headline endpoints — useful for confirming which deployment you are talking to.

Rate limit
Inherits the app default (200 requests/minute per IP)
Authentication
None. Open endpoint, no key required.
Response keys
message, version, endpoints
Example request
curl "https://jagannatha-hora-359167915530.europe-west1.run.app/"
Example response
{
  "message": "Kundli Studio Complete Horoscope API",
  "version": "1.0.0",
  "endpoints": {
    "POST /horoscope": "Generate complete horoscope from birth details",
    "POST /marriage-match": "Calculate marriage compatibility (Ashtakoota/10 Porutham)",
    "GET /location/search": "Search for location and get coordinates + timezone (162K+ cities)",
    "GET /location/autocomplete": "Get location suggestions for autocomplete",
    "GET /docs": "Interactive API documentation",
    "GET /health": "Health check endpoint"
  }
}
  • The index here is a literal dict in the handler, not generated — it lists six entries while the service actually exposes seventeen. Treat this page or /docs as the endpoint inventory, not that field.

GET/health

Liveness probe.

Returns a fixed healthy payload. Excluded from the service's request logging so that probing it every few seconds costs nothing in log volume.

Rate limit
Inherits the app default (200 requests/minute per IP)
Authentication
None. Open endpoint, no key required.
Response keys
status, service
Example request
curl "https://jagannatha-hora-359167915530.europe-west1.run.app/health"
Example response
{ "status": "healthy", "service": "Kundli Studio API" }

GET/debug/meminfo

Operational memory observability. Not a product endpoint.

Reports the answering worker's resident set size and peak RSS from /proc, the configured allocator, and the MCP horoscope cache occupancy. It exists to watch memory behaviour under load. With multiple workers each request may land on a different one, so a single sample means little. Read-only and cheap, but treat it as an internal detail that can change without notice.

Rate limit
Inherits the app default (200 requests/minute per IP)
Authentication
None. Open endpoint, no key required.
Response keys
allocator, rss_mb, rss_peak_mb, horoscope_cache
Example request
curl "https://jagannatha-hora-359167915530.europe-west1.run.app/debug/meminfo"
Example response
{
  "allocator": "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2",
  "rss_mb": 512.4,
  "rss_peak_mb": 690.1,
  "horoscope_cache": { "size": 3, "max_size": 8, "hits": 41, "misses": 12 }
}
  • The rss fields read /proc and are Linux-only; off Linux the response carries an rss_error field instead.

What POST /horoscope actually returns

The response is { "birth_details": {...}, "horoscope": {...} }. The example above shows a truncated excerpt because the real payload is roughly 3.5 MB of JSON — printing it would be useless to a human and worse than useless to a model. Instead, here are all 31 top-level keys inside horoscope, in the order the engine assigns them. That is the complete shape.

calendar_info
Panchanga and calendar context for the birth moment
bhava_chart
House (bhava) cusps under the selected bhava_madhya_method
ayanamsa_value
The ayanamsa in degrees actually applied
julian_day
Julian day number of the birth moment
divisional_charts
23 vargas, D-1 to D-144, each planet with sign and longitude
nakshatra_pada
Nakshatra, pada, lord and degrees-in-nakshatra per planet
amsa_rulers
D-150 Nadiamsa amsa rulers across planets, lagnas and sphutas
yogas
Classical yoga screening
doshas
Manglik, Kaal Sarp and the other classical doshas
chara_karakas
The eight variable karakas (Atma, Amatya, …)
sahams
The 36 sahams (Arabic parts)
upagrahas
The 11 shadowy sub-planets
special_lagnas
Bhava, Hora, Ghati and the other special ascendants
house_varnadas
Varnada lagna per house
graha_arudhas
Arudha padas computed from the grahas
surya_arudhas
Arudhas measured from the Sun
chandra_arudhas
Arudhas measured from the Moon
house_relationships
Argala and Vedha between houses
planetary_states
Retrograde, combustion, dignity and related states
shad_bala
The six-fold planetary strength
bhava_bala
House strength
other_bala
Harsha, pancha and dwadhasa vargeeya bala
vimsopaka_bala
Twenty-point varga strength
vaiseshikamsa_bala
Vaiseshikamsa strength
ashtakavarga
BAV, SAV, SoAV and PAV grids
arudha_padhas
Arudha padas (note the source spelling of this key)
sphuta
The 14 sphutas
graha_dashas
Graha dasha systems, Vimshottari included
rasi_dashas
Rasi dasha systems, Chara and Narayana included
saturn_transits
Sade Sati plus 4th/8th-house Saturn transits from Moon and Lagna
longevity_prediction
Classical longevity band with the combinations that produced it

Two of these carry most of the weight. divisional_charts holds all 23 vargas keyed as D-1_rasi through D-144_dwadas_dwadasamsa, each mapping every body to a sign and a longitude. graha_dashas and rasi_dashas hold the timing systems. If you only need one of these, the MCP server has a tool per section and will not make you download the rest.

Note the spelling of arudha_padhas. It is the engine's key, not a typo on this page, and the API returns it exactly that way.

Errors

Failures come back as a JSON object with a single detail field. The message names the field and the constraint it broke, so it is worth surfacing to your users rather than swallowing.

400 Bad Request
Invalid input. A validator rejected a field. The body is {"detail": "Invalid input: <reason>"}. /location/search and /location/autocomplete also return 400 when the query is under 2 characters.
401 Unauthorized
Missing or wrong shared secret. POST /horoscope/pdf only, when the X-PDF-Secret header is absent or does not match. Fails closed: if the server has no secret configured, every request is rejected.
404 Not Found
No location matched. GET /location/search when the query resolves to nothing in the world-cities index.
422 Unprocessable Entity
Schema violation. FastAPI's own validation, raised before the handler runs — a missing required field or a wrong JSON type.
429 Too Many Requests
Rate limited. The per-endpoint limit below, or the app-wide 200/minute default, was exceeded for your IP.
500 Internal Server Error
Calculation failed. An unexpected engine error. /location/search deliberately returns a generic message here and logs the trace server-side rather than leaking internals.
A validation failure
HTTP/1.1 400 Bad Request

{ "detail": "Invalid input: 1 validation error for BirthDetailsRequest\nlatitude\n  Latitude must be between -90 and 90" }

Before you build on this

  • There is no versioned URL prefix and no deprecation policy today. The service reports version 1.0.0 at GET / and the shapes documented here are the shapes it returns, but a breaking change would not announce itself with a new path.
  • There is no uptime SLA, no support contract and no status page. It is the same deployment that serves Horoscopes Today's own products.
  • The service returns computation, never interpretation. Nothing here decides whether a match is good or a date is auspicious — that judgement is yours, or your model's.