Calculate charts on our server.
Six endpoints under https://zodiacs.org/api/v1/ calculate from a JSON body sent with POST. There is no key and no account. Each answer carries the result, a receipt that says how it was computed, and the name and version of the engine that computed it: @zodiacs/engine 0.1.1-rc.15, with astronomy-engine 2.1.19, the engine this site's calculators run in the browser.
The endpoints answer only while their per-address rate limit is in place. Until it is, and whenever it cannot be checked, every endpoint answers 503 with Retry-After, and nothing is read or computed.
POST /api/v1/chartBodies, angles, house cusps and aspects for an instant and a place.
POST /api/v1/positionsPositions and speeds of the bodies you name at up to 100 instants.
POST /api/v1/housesAngles and house cusps for an instant, a place and a house system.
POST /api/v1/eventsSign changes, stations and new and full moons in a window of up to 92 days.
POST /api/v1/timeA local civil time as UTC and Terrestrial Time, with flags for skipped and repeated clock times.
POST /api/v1/sky-factWhether a stated fact about the sky holds on a date or at an instant: true, false or depends.
What the API receives and keeps
Put birth data in the body, and only there. The API takes the endpoint from the path and ignores the query string, but the host's short-lived logs keep the full web address and the IP address of every request, so birth details put in a query string would be kept in them.
The function writes nothing from a request or its result to a log, a file or a database. An error repeats nothing you sent: each refusal is a fixed sentence that names at most the field it refuses. Every response carries Cache-Control: no-store, so no cache keeps a copy.
Between requests a running instance keeps in memory the public tables it has loaded to answer: the time zone tables it has read, by zone name, and working values of the ephemeris for the periods asked about. They show which zones and periods were asked about. The ephemeris library also keeps two values from its last calculations, to reuse them: sidereal time at the moment of the last chart or houses request, and the Earth's tilt at the last moment any calculation used. So those two moments stay in memory until later requests replace them, or the instance is replaced. No place is kept, and none of this is written anywhere.
To apply the rate limit, the function sends the request's headers, which include your IP address, to Vercel's firewall, never the body; the firewall counts requests from each address. The site's own calculators do not use this API: they compute in your browser. The privacy page covers the API too.
Requests
Send Content-Type: application/json and a UTF-8 body of at most 16,384 bytes, with no content encoding. A field an endpoint does not take is refused rather than ignored. A key written twice in one object is read as JSON.parse reads it: the last value counts. Answers allow any origin and a browser's CORS preflight is answered, so a page on another site can call the API directly.
An instant is ISO 8601 with Z or a numeric offset, to the second or the millisecond: 2026-09-29T08:15:00Z or 1990-06-15T14:30:00+02:00. Instants run from 1800-01-01T00:00:00.000Z to 2199-12-31T23:59:59.999Z, the years the site's own forms take. A local time is an object with a real date, a time in hours and minutes, and a zone named as in the IANA database, such as Europe/Paris, in any letter case: the answer names the zone as tzdb spells it. Its date runs from 1800-01-01 to 2199-12-31. That span is where requests are accepted, not a span in which every date has been checked, and every receipt states the engine's own coverage. A local time at either end of it can fall just outside the instant span, such as midnight on 1 January 1800 in Tokyo, which is still 31 December in UTC; it is computed and flagged outside-reference-span.
The engine reads an instant as a chart reads it. From 1972 to 2 October 2027 it is UTC: Terrestrial Time is UTC plus the leap seconds and 32.184 seconds, and UT1 comes from the IERS measurements of UT1 − UTC. Before 1972 and after that date it is read as UT1, and Terrestrial Time is UT1 plus the engine's ΔT model. Every result that gives an instant's ΔT also gives its timeScale, which says which way it was read.
A latitude is degrees north, greater than −90 and less than 90: the engine computes no angles at the poles. A longitude is degrees east, from −180 to 180. A house system is one of the engine's thirteen: whole, placidus, porphyry, equal, equal-mc, vehlow, koch, regiomontanus, campanus, topocentric, alcabitius, morinus and meridian, with placidus when none is given.
Chart
POST /api/v1/chart takes exactly one of utc, an instant, and local, a local time, with a latitude, a longitude and an optional houseSystem. It returns what the engine's natalChart returns: twelve bodies (the Sun, the Moon, the eight planets and the two nodes) with longitude, latitude, speed, sign and degree; the four angles; the house system used and its twelve cusps; the aspects; flags; and the ΔT and time basis used. A local time is resolved as the time endpoint resolves it, with the place's longitude, and result.local says how it was read. The receipt is the engine's own chart receipt.
Inside the polar circle Placidus and Koch fall back to whole sign. The house system in the result, its flags and the receipt's houses.actual then say so.
curl --silent --show-error https://zodiacs.org/api/v1/chart \
--header 'Content-Type: application/json' \
--data '{"local":{"date":"1990-06-15","time":"14:30","zone":"Europe/Paris"},"latitude":48.8566,"longitude":2.3522,"houseSystem":"whole"}'The answer's result
{
"instant": "1990-06-15T12:30:00.000Z",
"local": {
"offsetMinutes": 120,
"flags": [],
"localMeanTime": null,
"zoneHistory": "runtime",
"zoneUncertain": false
},
"bodies": [
{
"body": "Sun",
"lon": 84.14928812963558,
"lat": -0.000018277038787882812,
"speed": 0.9551334400725864,
"retrograde": false,
"sign": "gemini",
"degree": 24.14928812963558
},
{
"body": "Moon",
"lon": 345.63672700444613,
"lat": 3.150108951597779,
"speed": 13.369243714805634,
"retrograde": false,
"sign": "pisces",
"degree": 15.63672700444613
},
{
"body": "Mercury",
"lon": 65.72654152736055,
"lat": -1.6591301441455806,
"speed": 1.717033450688632,
"retrograde": false,
"sign": "gemini",
"degree": 5.726541527360553
},
{
"body": "Venus",
"lon": 48.80187356528023,
"lat": -1.9454777463945472,
"speed": 1.1760244524907648,
"retrograde": false,
"sign": "taurus",
"degree": 18.801873565280232
},
{
"body": "Mars",
"lon": 11.056043552598055,
"lat": -1.985442010929648,
"speed": 0.7189199438641936,
"retrograde": false,
"sign": "aries",
"degree": 11.056043552598055
},
{
"body": "Jupiter",
"lon": 105.89400144525308,
"lat": 0.18682220954897247,
"speed": 0.21665555112804213,
"retrograde": false,
"sign": "cancer",
"degree": 15.894001445253082
},
{
"body": "Saturn",
"lon": 294.03064795082776,
"lat": 0.11719975460325135,
"speed": -0.05865971991170227,
"retrograde": true,
"sign": "capricorn",
"degree": 24.030647950827756
},
{
"body": "Uranus",
"lon": 278.1631287788913,
"lat": -0.3255654628085349,
"speed": -0.038931821222831786,
"retrograde": true,
"sign": "capricorn",
"degree": 8.163128778891291
},
{
"body": "Neptune",
"lon": 283.7163300513614,
"lat": 0.8770484012609029,
"speed": -0.025028642028246395,
"retrograde": true,
"sign": "capricorn",
"degree": 13.71633005136141
},
{
"body": "Pluto",
"lon": 225.40176208946352,
"lat": 15.85358596624236,
"speed": -0.02016973694196622,
"retrograde": true,
"sign": "scorpio",
"degree": 15.401762089463546
},
{
"body": "North Node",
"lon": 308.1218057000367,
"lat": 0,
"speed": 0.013241043506809547,
"retrograde": false,
"sign": "aquarius",
"degree": 8.121805700036703
},
{
"body": "South Node",
"lon": 128.1218057000367,
"lat": 0,
"speed": 0.013241043506809547,
"retrograde": false,
"sign": "leo",
"degree": 8.121805700036703
}
],
"angles": {
"asc": 182.46512110224094,
"mc": 93.10546966521451,
"dsc": 2.4651211022409143,
"ic": 273.1054696652145
},
"houses": {
"system": "whole",
"cusps": [
180,
210,
240,
270,
300,
330,
0,
30,
60,
90,
120,
150
]
},
"aspects": [
{
"a": "Moon",
"b": "Pluto",
"type": "trine",
"orb": 0.23496491498258365,
"applying": false
},
{
"a": "Moon",
"b": "Jupiter",
"type": "trine",
"orb": 0.25727444080689565,
"applying": true
},
{
"a": "Jupiter",
"b": "Pluto",
"type": "trine",
"orb": 0.49223935578956457,
"applying": false
},
{
"a": "Neptune",
"b": "Pluto",
"type": "sextile",
"orb": 1.6854320381021353,
"applying": false
},
{
"a": "Moon",
"b": "Neptune",
"type": "sextile",
"orb": 1.920396953084719,
"applying": false
},
{
"a": "Jupiter",
"b": "Neptune",
"type": "opposition",
"orb": 2.1776713938916714,
"applying": false
},
{
"a": "Mars",
"b": "Neptune",
"type": "square",
"orb": 2.660286498763355,
"applying": true
},
{
"a": "Mars",
"b": "Uranus",
"type": "square",
"orb": 2.8929147737067638,
"applying": false
},
{
"a": "Venus",
"b": "Jupiter",
"type": "sextile",
"orb": 2.9078721200271502,
"applying": false
},
{
"a": "Moon",
"b": "Venus",
"type": "sextile",
"orb": 3.165146560834046,
"applying": true
},
{
"a": "Venus",
"b": "Pluto",
"type": "opposition",
"orb": 3.400111475816715,
"applying": false
},
{
"a": "Mars",
"b": "Jupiter",
"type": "square",
"orb": 4.837957892655027,
"applying": true
},
{
"a": "Venus",
"b": "Neptune",
"type": "trine",
"orb": 5.085543513918822,
"applying": false
},
{
"a": "Venus",
"b": "Saturn",
"type": "trine",
"orb": 5.228774385547524,
"applying": true
},
{
"a": "Uranus",
"b": "Neptune",
"type": "conjunction",
"orb": 5.553201272470119,
"applying": false
},
{
"a": "Jupiter",
"b": "Uranus",
"type": "opposition",
"orb": 7.73087266636179,
"applying": false
}
],
"flags": [],
"deltaT": {
"seconds": 57.19704162597656,
"sigma": 0.000846,
"model": "iers-utc/1",
"table": "2026-09-24",
"tableDigest": "064d98b4a531053a",
"segment": "observed"
},
"timeScale": {
"input": "utc",
"basis": "iers",
"ut1MinusUtc": {
"seconds": -0.013041666666662621,
"sigma": 0.000846,
"source": "observed"
},
"leapSeconds": {
"taiMinusUtc": 25,
"listed": true
}
}
}Positions
POST /api/v1/positions takes instants, from 1 to 100, and optionally bodies, any of the Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, North Node and South Node, all twelve when omitted. At each instant it returns the engine's positions() rows for those bodies: tropical longitude and latitude of date, speed in degrees a day, whether the body is retrograde, and its sign and degree, with the ΔT and time basis used and any flags.
curl --silent --show-error https://zodiacs.org/api/v1/positions \
--header 'Content-Type: application/json' \
--data '{"instants":["2026-09-29T12:00:00Z","2026-12-31T00:00:00-05:00"],"bodies":["Sun","Moon","Mercury"]}'The answer's result
{
"instants": [
{
"instant": "2026-09-29T12:00:00.000Z",
"bodies": [
{
"body": "Sun",
"lon": 186.36510292214544,
"lat": 0.000040745528335972984,
"speed": 0.9817149955893001,
"retrograde": false,
"sign": "libra",
"degree": 6.365102922145411
},
{
"body": "Moon",
"lon": 42.54210931920164,
"lat": 4.950806474193535,
"speed": 14.176526701334069,
"retrograde": false,
"sign": "taurus",
"degree": 12.542109319201643
},
{
"body": "Mercury",
"lon": 208.6691238803002,
"lat": -1.5664060143340435,
"speed": 1.3549243577983816,
"retrograde": false,
"sign": "libra",
"degree": 28.669123880300162
}
],
"deltaT": {
"seconds": 69.20533325195312,
"sigma": 0.0012697,
"model": "iers-utc/1",
"table": "2026-09-24",
"tableDigest": "064d98b4a531053a",
"segment": "predicted"
},
"timeScale": {
"input": "utc",
"basis": "iers",
"ut1MinusUtc": {
"seconds": -0.02133333333333809,
"sigma": 0.0012697,
"source": "predicted"
},
"leapSeconds": {
"taiMinusUtc": 37,
"listed": true
}
},
"flags": []
},
{
"instant": "2026-12-31T05:00:00.000Z",
"bodies": [
{
"body": "Sun",
"lon": 279.51344376954717,
"lat": 0.00005713042741092159,
"speed": 1.019138996014135,
"retrograde": false,
"sign": "capricorn",
"degree": 9.513443769547166
},
{
"body": "Moon",
"lon": 194.499690644731,
"lat": -4.135221345445716,
"speed": 12.904041858803339,
"retrograde": false,
"sign": "libra",
"degree": 14.499690644731004
},
{
"body": "Mercury",
"lon": 278.6336286217399,
"lat": -1.6656442594625178,
"speed": 1.5992625331193722,
"retrograde": false,
"sign": "capricorn",
"degree": 8.633628621739945
}
],
"deltaT": {
"seconds": 69.30520825195312,
"sigma": 0.009847975000000002,
"model": "iers-utc/1",
"table": "2026-09-24",
"tableDigest": "064d98b4a531053a",
"segment": "predicted"
},
"timeScale": {
"input": "utc",
"basis": "iers",
"ut1MinusUtc": {
"seconds": -0.12120833333332826,
"sigma": 0.009847975000000002,
"source": "predicted"
},
"leapSeconds": {
"taiMinusUtc": 37,
"listed": true
}
},
"flags": []
}
]
}Houses
POST /api/v1/houses takes the same fields as chart and returns only the angles, the house system used and its twelve cusps, with flags and the ΔT and time basis used. Its receipt is the chart's.
curl --silent --show-error https://zodiacs.org/api/v1/houses \
--header 'Content-Type: application/json' \
--data '{"utc":"2026-06-21T12:00:00Z","latitude":-33.8688,"longitude":151.2093,"houseSystem":"koch"}'The answer's result
{
"instant": "2026-06-21T12:00:00.000Z",
"local": null,
"angles": {
"asc": 335.538860059695,
"mc": 242.95819054092098,
"dsc": 155.53886005969503,
"ic": 62.95819054092101
},
"houses": {
"system": "koch",
"cusps": [
335.538860059695,
4.91348875675925,
34.142165776173044,
62.95819054092101,
97.20365849857853,
126.66878984401257,
155.53886005969503,
184.9134887567593,
214.142165776173,
242.958190540921,
277.20365849857853,
306.6687898440125
]
},
"flags": [],
"deltaT": {
"seconds": 69.1725,
"sigma": 0.000817,
"model": "iers-utc/1",
"table": "2026-09-24",
"tableDigest": "064d98b4a531053a",
"segment": "observed"
},
"timeScale": {
"input": "utc",
"basis": "iers",
"ut1MinusUtc": {
"seconds": 0.011499999999998067,
"sigma": 0.000817,
"source": "observed"
},
"leapSeconds": {
"taiMinusUtc": 37,
"listed": true
}
}
}Events
POST /api/v1/events takes a window, from and to, at most 92 days long, and optionally bodies, of the ten from the Sun to Pluto, and kinds, of ingress, station and lunation; all of each when omitted. It returns, in time order, every sign change of those bodies, every station of the planets among them, and every new and full moon it finds in the window. It finds them with the engine's crossing search: a sign change is the body's longitude crossing a sign boundary, a station is its speed crossing zero, and a new or full moon is the Moon's distance from the Sun reaching 0° or 180°.
The search samples every 5 days, or every day for the Moon and for new and full moons, and narrows each crossing it finds to the step divided by 224. A window excludes its start and includes its end. The search can miss a pair of crossings that both fall between two samples, such as a body that crosses a boundary and crosses back around a station: it has been tested, not proven complete. A request whose searches would need more than 12,000 evaluations is refused whole, and no events are returned. The receipt records the step, the evaluations used and this limit. For 2026 the endpoint finds every station, new and full moon, and sign change of the Sun and planets on the site's transit calendar, each within 5 minutes of the calendar's time, and nothing else of those kinds.
curl --silent --show-error https://zodiacs.org/api/v1/events \
--header 'Content-Type: application/json' \
--data '{"from":"2026-10-01T00:00:00Z","to":"2026-11-01T00:00:00Z","bodies":["Sun","Mercury","Venus"],"kinds":["ingress","station","lunation"]}'The answer's result
{
"from": "2026-10-01T00:00:00.000Z",
"to": "2026-11-01T00:00:00.000Z",
"events": [
{
"kind": "station",
"body": "Venus",
"at": "2026-10-03T07:10:13.002Z",
"type": "retrograde",
"lon": 218.48665989824488,
"sign": "scorpio",
"degree": 8.486659898244852
},
{
"kind": "lunation",
"type": "new",
"at": "2026-10-10T15:50:02.099Z",
"lon": 197.3600967127593,
"sign": "libra",
"degree": 17.360096712759287
},
{
"kind": "ingress",
"body": "Sun",
"at": "2026-10-23T09:38:28.143Z",
"sign": "scorpio",
"retrograde": false
},
{
"kind": "station",
"body": "Mercury",
"at": "2026-10-24T07:15:27.755Z",
"type": "retrograde",
"lon": 230.97960799993507,
"sign": "scorpio",
"degree": 20.97960799993507
},
{
"kind": "ingress",
"body": "Venus",
"at": "2026-10-25T08:57:04.055Z",
"sign": "libra",
"retrograde": true
},
{
"kind": "lunation",
"type": "full",
"at": "2026-10-26T04:11:44.973Z",
"lon": 32.76153623301718,
"sign": "taurus",
"degree": 2.761536233017182
}
]
}Time
POST /api/v1/time takes local and, optionally, the place's longitude. It returns the instant in UTC, the offset from UTC in minutes, flags, the local mean time used if any, which zone history was read, whether the zone is uncertain, the instant in Terrestrial Time, and ΔT. It reads the local time with the birth chart calculator's own resolver, built into the function with its tables:
- A clock time that happened twice takes the earlier instant and the flag
dst-fold; one that was skipped moves forward by the gap and hasdst-gap. - With a longitude, a time before the place adopted a legal time uses the place's own mean time, flagged
lmt. - With a longitude, a date up to 1 January 1970 in a zone the pinned tzdb release 2025c keeps takes its legal offset from that release, backzone file included, and
zoneHistoryispinned. - Otherwise the offset comes from the time zone data of the server's runtime, and
zoneHistoryisruntime. For a date up to 1 January 1970 that history can differ from the pinned one, andzoneUncertainis true. - Terrestrial Time and ΔT are the engine's for that instant, to the millisecond, read as described above. ΔT comes with its one-sigma band and the source it came from: the IERS measurements, or the part of the model.
curl --silent --show-error https://zodiacs.org/api/v1/time \
--header 'Content-Type: application/json' \
--data '{"local":{"date":"1947-07-01","time":"12:00","zone":"Europe/Stockholm"},"longitude":18.07}'The answer's result
{
"utc": "1947-07-01T11:00:00.000Z",
"offsetMinutes": 60,
"flags": [],
"localMeanTime": null,
"zoneHistory": "pinned",
"zoneUncertain": false,
"tt": "1947-07-01T11:00:28.008",
"deltaT": {
"seconds": 28.008138261464772,
"sigma": 0.46,
"model": "zodiacs-deltat/1",
"table": "2026-09-24",
"tableDigest": "6371988c510a1c6c",
"segment": "reconstructed"
},
"timeScale": {
"input": "utc",
"basis": "delta-t",
"ut1MinusUtc": null,
"leapSeconds": null
}
}Sky facts
POST /api/v1/sky-fact takes one fact and answers true, false or depends, with the computed values that decide it. It returns no interpretation. The facts it takes:
sign: a body is in a sign, at aninstantor on adate.retrograde: a body is retrograde, at an instant or on a date.ingress: a body enters a sign on a date.phase: a new moon, first quarter, full moon or last quarter falls on a date.
A date is read in the zone you give, from its midnight to the next. Without a zone it is read in every UTC offset in use, from 14 hours before its midnight in UTC to 36 hours after. The answer is depends when it turns on the time of day, because the body changes sign or stations during that day, or, without a zone, on the zone: whether an ingress or a phase falls on a date always depends on where the date is read. At an instant the answer is true or false.
A zone that moved across the date line skipped a whole date: Samoa went from 29 to 31 December 2011. A fact about a date its zone skipped is refused with invalid-request at /date, since there was no such day to hold it. The facts carry flags, with outside-reference-span when the day read reaches past the instant span, as the first and last accepted dates can.
curl --silent --show-error https://zodiacs.org/api/v1/sky-fact \
--header 'Content-Type: application/json' \
--data '{"kind":"retrograde","body":"Mercury","date":"2026-10-24","zone":"America/New_York"}'The answer's result
{
"answer": "depends",
"basis": "local-day",
"fact": {
"kind": "retrograde",
"body": "Mercury",
"instant": null,
"date": "2026-10-24",
"zone": "America/New_York"
},
"instant": null,
"window": {
"from": "2026-10-24T04:00:00.000Z",
"to": "2026-10-25T04:00:00.000Z"
},
"zone": {
"start": {
"utc": "2026-10-24T04:00:00.000Z",
"offsetMinutes": -240,
"flags": [],
"localMeanTime": null,
"zoneHistory": "runtime",
"zoneUncertain": false
},
"end": {
"utc": "2026-10-25T04:00:00.000Z",
"offsetMinutes": -240,
"flags": [],
"localMeanTime": null,
"zoneHistory": "runtime",
"zoneUncertain": false
}
},
"facts": {
"atStart": {
"lon": 230.97839204376257,
"sign": "scorpio",
"degree": 20.978392043762597,
"speed": 0.017888278179611916,
"retrograde": false
},
"atEnd": {
"lon": 230.9292038226267,
"sign": "scorpio",
"degree": 20.929203822626732,
"speed": -0.11771949399985715,
"retrograde": true
},
"stations": [
{
"at": "2026-10-24T07:15:27.737Z",
"type": "retrograde"
}
],
"flags": []
}
}Receipts and citation
Every successful answer has five fields:
schema, the answer's shape and version, such aszodiacs.compute-api.time.v1.result, what was computed.receipt, how. For chart and houses it is the engine's own chart receipt,zodiacs.calculation-receipt.draft-v1. For the other four it iszodiacs.compute-receipt.v1: the engine's conventions and coverage as its receipts state them, the two sources of ΔT its time basis uses, and, where they apply, the search's limits and how local time was read.backend, the engine and its version, and the ephemeris it uses.cite, what to quote beside a result:url, this page at the endpoint's section;receipt, a digest of the receipt;engine; andversion.
The digest is sha256: followed by the SHA-256, in hexadecimal, of the receipt written as RFC 8785 canonical JSON, with sorted keys and no spaces. Anyone holding the answer can compute it again, and anyone holding only the citation can tell which receipt it names. A digest keeps the citation short enough to quote, which a second copy of the receipt would not.
A digest does not hide what it identifies. For chart and houses the receipt holds the instant and the coordinates, so the digest identifies the birth details: anyone who knows the date and the place can find the time by trying each minute of the day until the digest matches. Quote it only where the birth details may be known.
A whole answer, from the time example above
{
"schema": "zodiacs.compute-api.time.v1",
"result": {
"utc": "1947-07-01T11:00:00.000Z",
"offsetMinutes": 60,
"flags": [],
"localMeanTime": null,
"zoneHistory": "pinned",
"zoneUncertain": false,
"tt": "1947-07-01T11:00:28.008",
"deltaT": {
"seconds": 28.008138261464772,
"sigma": 0.46,
"model": "zodiacs-deltat/1",
"table": "2026-09-24",
"tableDigest": "6371988c510a1c6c",
"segment": "reconstructed"
},
"timeScale": {
"input": "utc",
"basis": "delta-t",
"ut1MinusUtc": null,
"leapSeconds": null
}
},
"receipt": {
"schema": "zodiacs.compute-receipt.v1",
"endpoint": "time",
"engine": {
"name": "@zodiacs/engine",
"version": "0.1.1-rc.15",
"ephemeris": {
"name": "astronomy-engine",
"version": "2.1.19"
}
},
"conventions": {
"angles": "gast-and-true-obliquity",
"aspects": "major-aspects;sun-moon-eight-planets;no-nodes;applying-instantaneous-orb-rate",
"calendar": "proleptic-gregorian",
"deltaT": "tt-minus-ut1;value-in-result",
"localTime": "tzdb-shards-before-1970;host-intl-from-1970;flags-from-transition-record",
"longitudeUnit": "degrees-[0,360)",
"moonNodes": "instantaneous-geocentric-moon-orbit-plane",
"moonPosition": "astronomy-engine-ecliptic-geo-moon;no-light-time;no-aberration",
"planetPositions": "aberrated-geocentric-ecliptic-of-date;no-deflection",
"speed": "degrees-per-day;central-difference-plus-minus-0.001-day;nodes-plus-minus-0.25-day",
"timeScale": "tt-from-leap-seconds-and-ut1-from-iers-1972-to-table-end;delta-t-model-otherwise;in-result",
"zodiac": "tropical"
},
"coverage": {
"angleExclusions": "exact-geographic-poles-and-ecliptic-horizon-coincidence",
"assessment": "finite-reference-cases-only",
"broadDateRange": "not-certified",
"inputSyntax": "not-an-astronomical-accuracy-guarantee"
},
"referenceSpan": {
"from": "1800-01-01T00:00:00.000Z",
"to": "2200-01-01T00:00:00.000Z"
},
"deltaT": [
{
"model": "iers-utc/1",
"table": "2026-09-24",
"tableDigest": "064d98b4a531053a"
},
{
"model": "zodiacs-deltat/1",
"table": "2026-09-24",
"tableDigest": "6371988c510a1c6c"
}
],
"timeResolution": {
"resolver": "src/lib/time/localToUtc.ts",
"policy": {
"fold": "earlier",
"gap": "shift-forward"
},
"pinnedTzdb": {
"release": "2025c",
"dataForm": "main+backzone",
"appliesBefore": "1970-01-02",
"requires": "longitude"
},
"runtimeTzdb": "2026c"
}
},
"backend": {
"name": "@zodiacs/engine",
"version": "0.1.1-rc.15",
"ephemeris": {
"name": "astronomy-engine",
"version": "2.1.19"
}
},
"cite": {
"url": "https://zodiacs.org/developers/compute/#time",
"receipt": "sha256:3d848ffef84f7b1768ba78ecc65d7d9ec7ac947e13ceecc653ff3c60ea21b78d",
"engine": "@zodiacs/engine",
"version": "0.1.1-rc.15"
}
}Limits
| Limit | At most | Over it |
|---|---|---|
| Body | 16,384 bytes | 413 payload-too-large |
| Instants in one positions request | 100 | 422 budget-exhausted, limit positions.instants |
| Length of an events window | 92 days | 422 budget-exhausted, limit events.windowDays |
| Evaluations in one events request | 12,000 | 422 budget-exhausted, limit events.samples |
| Evaluations in one sky-fact request | 1,000 | 422 budget-exhausted, limit sky-fact.samples |
| Requests from one IP address | set by the rate limit; events requests count against a second, lower limit too | 429 rate-limited, Retry-After: 60 |
A request over a limit is refused whole, before anything is computed, except the two evaluation limits, which are counted as the searches run: when one is spent the request is refused and nothing is returned. The limits come from the measured cost of requests in every year the API takes: on the machine they were measured on, one address at its rate limits can cost at most about 8.7 seconds of CPU time a minute. The measurements are in the site repository. When the API is switched off, or its rate limit is not in place, every endpoint answers 503.
Errors
A refusal is JSON, {"error": {"code": …, "message": …}}, with the status below. The code keeps its meaning within v1; the message is a fixed sentence. invalid-request adds pointer, a JSON Pointer to the field; budget-exhausted and payload-too-large add limit and max; rate-limited and disabled add retryAfterSeconds, which the Retry-After header repeats.
| Code | Status | When |
|---|---|---|
method-not-allowed | 405 | A method other than POST. OPTIONS is answered as a CORS preflight. |
disabled | 503 | The API is switched off. Retry-After is 3600 seconds. |
rate-limit-unavailable | 503 | The rate limit is not in place, or could not be checked, so nothing was read or computed. Retry-After is 300 seconds. |
rate-limited | 429 | Too many requests from one address. Retry-After is 60 seconds. |
unsupported-media-type | 415 | The body is not declared as application/json in UTF-8, or it has a content encoding. |
payload-too-large | 413 | The body is over 16,384 bytes, declared or counted. |
invalid-json | 400 | The body is not JSON in UTF-8, or its length is not the one declared. |
invalid-request | 400 | A field is missing, out of range, of the wrong type, or not one the endpoint takes. |
budget-exhausted | 422 | The request is over one of the limits above; limit and max name it. |
calculation-failed | 500 | The engine could not complete the calculation. |
not-found | 404 | The request reached the function by a path that is not one of the six endpoints. |
OpenAPI and versions
The six operations are in the OpenAPI 3.1 document at /api/v1/openapi.json, tagged compute, with a JSON Schema for every request and answer and an example of each that the handler itself produced. A schema cannot state every rule, such as a real calendar date, a zone the server knows, or an events window whose to is after its from, so a request the schema accepts can still be refused with invalid-request.
Within v1, fields are added, never renamed or removed. The engine is a release candidate: when the site adopts a new one, the version in backend and cite changes with it, and results can move with the engine's conventions, which the receipt names. Compare versions and receipts before comparing numbers from different days. The engine page says how close its positions are to Swiss Ephemeris and NASA JPL Horizons.











