Skip to content

Subjects & settings

Every computation endpoint takes the same two objects. The subject says when and where the chart is for; the settings say how to calculate it. There is exactly one subject schema and one settings schema across the entire API — integrate them once and you have integrated every endpoint. There are no per-endpoint parameter spellings, no day/month/year/hour/min on one route and datetime on another.

Both objects travel in a JSON body on POST:

{
"subject": {
"datetime": "1992-11-22T08:45:00+05:30",
"location": { "latitude": 28.6139, "longitude": 77.2090 }
},
"settings": {
"zodiac": "tropical",
"node_type": "true"
}
}

subject is required; settings is optional and fully defaulted.

An ISO 8601 timestamp with an explicit UTC offset, for example 1992-11-22T08:45:00+05:30. The datetime is authoritative: the API converts it to Universal Time for the ephemeris exactly as given and never adjusts, guesses, or “helpfully” reinterprets it. A datetime without an offset is rejected with 422 and the error code subject.time.invalid_format — see Timezones & ambiguity for why the offset is required and how to derive it correctly.

Coordinates in decimal degrees: latitude from −90 to 90 (south negative), longitude from −180 to 180 (west negative).

Planetary positions depend on the moment, not on where the observer stands. If all you need is positions — to drive your own aspect logic, feed a model, or render a sign-only reading — omit location entirely:

Terminal window
curl -s https://astral-external-api-iksoi6t3nq-ue.a.run.app/v1/western/positions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": { "datetime": "1992-11-22T08:45:00+05:30" }
}' | jq .meta.subject_resolved

The echo reports "coordinates": null, so a stored response is unambiguous about whether a location was involved. Location-dependent outputs — house cusps and angles, when they ship — will require coordinates and will say so with a documented error code rather than inventing a default location.

Every response echoes meta.subject_resolved.time_known. Today it is always true, because v1 requires a full timestamp. The field exists because unknown-birth-time subjects (date-only) are planned: when they arrive, time_known: false will mark charts where time-dependent outputs (houses, angles, a precise Moon degree) are omitted or degraded. Since the field is already in the echo, your response handling won’t need a schema change when that lands.

All settings fields are optional, every field has a documented default, and the fully resolved set is echoed back on every response.

Field Type Default Notes
zodiac string: tropical or sidereal tropical Sidereal requires ayanamsa.
ayanamsa string: lahiri, raman, or krishnamurti none Required if and only if zodiac is sidereal. Sending it with the tropical zodiac is a 422 (settings.ayanamsa.not_applicable); omitting it with sidereal is a 422 (settings.ayanamsa.required).
node_type string: "true" or "mean" "true" A JSON string, not a boolean — "node_type": true is malformed. Also selects which node appears in the default bodies.
bodies array of body names the ten planets plus the lunar node The default node is true_node, or mean_node when node_type is "mean".

Supported bodies values (18): sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, mean_node, chiron, lilith_mean, ceres, pallas, juno, vesta.

Requesting the sidereal zodiac means naming the ayanamsa explicitly — there is no implied default, because the choice moves every longitude:

Terminal window
curl -s https://astral-external-api-iksoi6t3nq-ue.a.run.app/v1/western/positions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": { "datetime": "1992-11-22T08:45:00+05:30" },
"settings": { "zodiac": "sidereal", "ayanamsa": "lahiri" }
}' | jq .meta.settings_resolved

Both request objects are strict: an unknown field is rejected with 400 (request.malformed) naming the offending key, never silently ignored. If a request appears to accept a setting, it actually took effect.

Every successful response has the same two-key shape: data carries the endpoint-specific result, and meta carries provenance — identical fields on every endpoint.

{
"data": {
"positions": [
{
"body": "sun",
"longitude": 240.0766,
"sign": "sagittarius",
"sign_longitude": { "degrees": 0, "minutes": 4, "seconds": 36 },
"element": "fire",
"modality": "mutable",
"speed": 1.0108,
"retrograde": false,
"declination": -20.167,
"out_of_bounds": false
}
]
},
"meta": {
"calc_version": "0.1.1",
"settings_resolved": {
"zodiac": "tropical",
"node_type": "true",
"bodies": ["sun", "moon", "mercury", "venus", "mars", "jupiter",
"saturn", "uranus", "neptune", "pluto", "true_node"]
},
"subject_resolved": {
"utc_datetime": "1992-11-22T03:15:00Z",
"utc_offset": "+05:30",
"coordinates": { "latitude": 28.6139, "longitude": 77.2090 },
"time_known": true
},
"credits_charged": 1
}
}

(The positions array is truncated to one body here; a real response contains one entry per requested body.)

meta always carries:

  • calc_version — the calculation engine version that produced this result; identical inputs and identical calc_version produce byte-identical outputs.
  • settings_resolved — the full settings actually used, defaults filled in.
  • subject_resolved — what the API understood your subject to be: utc_datetime, utc_offset, coordinates (or null), time_known.
  • credits_charged — metering for this call, in-band rather than buried in a dashboard.

Errors use RFC 9457 application/problem+json with a stable, namespaced code field — the codes quoted on this page are contractual, not prose.

meta.settings_resolved and meta.subject_resolved appear on every response, not only when you ask. This is deliberate. Silent resolution is how calculation APIs acquire “your API is wrong” tickets: a default you didn’t know about, or an offset conversion you couldn’t see, produces an output that disagrees with your expectation and gives you nothing to check it against. The echo converts that support ticket into a documentation lookup — compare settings_resolved with what you intended and subject_resolved.utc_datetime with the birth moment you meant, and the discrepancy identifies itself.

Send only a subject and watch the defaults come back resolved:

Terminal window
curl -s https://astral-external-api-iksoi6t3nq-ue.a.run.app/v1/western/positions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": {
"datetime": "1990-05-15T14:30:00-04:00",
"location": { "latitude": 40.7128, "longitude": -74.0060 }
}
}' | jq .meta.settings_resolved

API spec v0.1.0 · docs 0e4f5ec