Guides
Move from self-hosted Swiss Ephemeris to the API
This guide is for teams running their own Swiss Ephemeris (swisseph) installation who want out of maintaining it. The guide maps common swisseph workflows to AstrologyAPI endpoints, verified one by one, and is honest about what has no equivalent yet.
Why teams switch, and why some shouldn't
Running swisseph yourself means owning the ephemeris files and the configuration around them:
- Keeping the
.se1data current, deployed, and on the right path in every environment. - Choosing and configuring an ayanamsha (sidereal mode) yourself, and understanding what that choice does to your output.
- Tracking library upgrades across whatever language binding you use, and testing that upgrades don't shift your results.
Teams that move to a hosted API are usually trying to trade that maintenance load for a network call.
That trade isn't right for everyone. Stay self-hosted if any of these apply:
- You need to compute charts fully offline, with no network path in your deployment. A hosted API doesn't help you.
- You need sub-millisecond latency. An in-process library call is faster than any network round trip, full stop.
- You need control over ephemeris precision, data files, or exact sidereal settings beyond what a hosted API exposes.
Weigh the maintenance you're removing against the control you're giving up before you commit.
Licensing
Swiss Ephemeris itself is dual-licensed: it is available under the AGPL-3.0, or under a paid professional license from Astrodienst for closed-source use.
Read the authoritative terms at astro.com/swisseph and review your specific case with your counsel. This page states the shape of the question, not a legal conclusion.
Mapping swisseph workflows to API endpoints
Each row below is verified against the live endpoint metadata. Where nothing verifies, the table says so instead of stretching a mapping that doesn't hold.
| swisseph workflow | API equivalent | Notes |
|---|---|---|
swe_calc_ut, planetary positions, sidereal | planets | One call per birth record returns every planet with fullDegree, normDegree, speed, isRetro, sign, signLord, nakshatra, nakshatraLord, nakshatra_pad, house, and planet_awastha. |
swe_calc_ut, planetary positions, tropical | planets/tropical | Same birth-data body plus a house_type param. Returns fullDegree, normDegree, speed, isRetro, sign, and house per planet. No nakshatra field: that's a sidereal concept and doesn't apply to tropical output. |
swe_houses, house cusps | house_cusps/tropical | Takes the same house_type param, documented values placidus (default), koch, topocentric, poryphry, equal_house, and whole_sign. Returns a houses array (house, sign, degree) plus ascendant, midheaven, and vertex. |
swe_set_sid_mode, sidereal mode selection | ayanamsha | Returns six named systems for a birth moment in one call: LAHIRI, KP, YUKTESHWAR, RAMAN, JN_BHASIN, and FAGAN_BRADLEY. The planets endpoint's sidereal output uses the Lahiri value; call this endpoint to see or cross-check the number directly. |
| Chart rendering (custom drawing on top of swisseph output) | natal_wheel_chart | Takes the birth body plus styling params (chart_size, planet_icon_color, house_type, and others) and returns a hosted chart_url image instead of raw coordinates you'd render yourself. |
Eclipses, fixed stars (swe_rise_trans, fixed-star catalogs) | No direct equivalent verified | We found no eclipse or fixed-star endpoint in the current API. If your product depends on either, keep that calculation in swisseph, or check again before you commit. |
Before and after
The swisseph side below is the well-known public pyswisseph pattern, shown for comparison. It is illustrative, not something this guide runs. The Node side is complete and runs as-is on Node 18 or later.
# Illustrative only, the standard pyswisseph public API.
# Not run as part of this guide; shown for comparison with the Node example below.
import swisseph as swe
# You maintain the .se1 ephemeris files on disk and keep the path current.
swe.set_ephe_path('/opt/ephe')
# You choose and configure the sidereal mode yourself.
swe.set_sid_mode(swe.SIDM_LAHIRI)
# You compute the Julian day from UT (birth local time adjusted by tzone).
jd_ut = swe.julday(1990, 5, 10, 19.9167 - 5.5)
# One planet at a time, flag-driven.
xx, retflag = swe.calc_ut(jd_ut, swe.SUN, swe.FLG_SIDEREAL)
sun_longitude = xx[0] # sign, nakshatra, house, retrograde: your own code// Node 18+ (global fetch). No external packages.
// USER_ID and API_KEY come from your AstrologyAPI dashboard.
const USER_ID = 'USER_ID'
const API_KEY = 'API_KEY'
const AUTH = 'Basic ' + Buffer.from(USER_ID + ':' + API_KEY).toString('base64')
async function getPlanets(birth) {
const res = await fetch('https://json.astrologyapi.com/v1/planets', {
method: 'POST',
headers: {
Authorization: AUTH,
'Content-Type': 'application/json',
},
body: JSON.stringify(birth),
})
if (!res.ok) {
throw new Error('planets failed: ' + res.status + ' ' + res.statusText)
}
return res.json()
}
getPlanets({
day: 10,
month: 5,
year: 1990,
hour: 19,
min: 55,
lat: 19.2056,
lon: 25.2056,
tzone: 5.5,
})
.then((planets) => {
const sun = planets.find((p) => p.name === 'Sun')
// sign, nakshatra, house, and retrograde status all come back in this
// one response element, no separate flag-driven calls per planet.
console.log(sun.fullDegree, sun.sign, sun.nakshatra, sun.house, sun.isRetro)
})
.catch((err) => {
console.error(err.message)
process.exit(1)
})Differences to plan for
You send birth data instead of a precomputed Julian day. swisseph wants a Julian day number in UT, which means you convert local birth time to UT yourself before calling swe_calc_ut.
The API takes local birth fields directly: day, month, year, hour, min, lat, lon, and tzone. The API resolves the rest server-side.
Timezone handling moves to you either way, in a different form. You no longer convert to UT for the Julian day calculation.
Instead, resolve the correct decimal tzone offset for the birth place and date, and send that offset as a field. See timezones, DST, and historical birth data for how to resolve the offset correctly, including daylight saving edge cases.
Every calculation is now a network round trip instead of an in-process library call. For a fixed birth record, natal positions never change, so cache the response indefinitely instead of recomputing it on every page load.
See the production checklist for caching, retries, and credit management once you move past a proof of concept.
Where to go next
For what the API returns and how to read it, see how charts are computed. Before you send real traffic, read the production checklist. For your first authenticated request, start with the quick start guide.