Sky Oracle · API v1
The sky, in JSON.
Sun and Moon positions, local light events, shadow geometry, and eclipses for your own tools. A small, token-protected service with explicit limits on calculation work.
Validated against JPL DE440
Sky Oracle computes solar positions with VSOP87D and lunar positions with the full Meeus chapter 47 series. Our reference tests compare those calculations with coefficients taken directly from JPL’s DE440s ephemeris. NASA/JPL Horizons currently uses the related DE441 solution for the Sun, Earth, and Moon. DE440 and DE441 belong to the same JPL ephemeris family, but our served calculations are an independent implementation, not the Horizons engine or its API.
The DE440 comparison checks the underlying series; it shares our coordinate transformations and cannot independently verify them. Published USNO event times and NASA eclipse tables provide additional checks. API inputs are supported from 1900 through 2050 UTC, with app validation scenarios covering 1950–2049. These checks do not establish identical Horizons results or uniform accuracy across every date and location. Moon Shadows is not affiliated with or endorsed by NASA or JPL.
JPL Horizons model documentationJPL DE440 / DE441 documentation
Make your first request
Request access with a short description of your project, the endpoints you need, and your expected request volume. Tokens are issued manually. Set SKYORACLE_TOKEN in your local environment, then send it in the Authorization header over HTTPS. Every calculation requires a token; health is public.
Keep tokens private. Do not put them in URLs, public code, or a shipped website bundle. For the observatory on this site, you can enter your own token for the current page session. There is no shared browser key or automatic token signup.
curl --fail-with-body --get \
'https://moonshadows.app/horizons/api/v1/position' \
--header "Authorization: Bearer $SKYORACLE_TOKEN" \
--header 'Accept: application/json' \
--data-urlencode 'lat=34.118' \
--data-urlencode 'lon=-118.300' \
--data-urlencode 'at=2026-09-05T20:00:00Z' \
--data-urlencode 'height_m=1.8'This request asks for the geometric Sun and Moon positions and the shadow cast by a 1.8-metre object at Griffith Observatory at an explicit UTC instant. The response includes sun, moon, light_kind, and shadow objects.
Six focused endpoints
https://moonshadows.app/horizons/api/v1
| Endpoint | Parameters | Result | Credits |
|---|---|---|---|
GET /position | lat, lon [, at, height_m] | Sun and Moon direction, phase, illumination, and shadow. | 1 |
GET /events | lat, lon, zone [, date] | A civil day’s light events, solar noon, and moonrise / moonset. | 10 |
GET /day | lat, lon, zone [, date] | A complete daily report, including a 21:00 local-time Moon and shadow summary. | 10 |
GET /ephemeris | lat, lon [, from, to, step] | A bounded sequence of Sun and Moon positions. | 10 |
GET /eclipse | [at, lat, lon] | Eclipse geometry at an instant, with optional local circumstances. | 1 |
GET /health | — | Liveness and build version; no token or work credits required. | 0 |
Parameters in brackets are optional. Coordinates use decimal degrees, with east-positive longitude. Instants use RFC 3339 with an offset; dates use YYYY-MM-DD and an IANA zone such as America/Los_Angeles. Omitted instants mean now; omitted dates mean today in the requested zone. For eclipse, supply both latitude and longitude together.
Ephemeris defaults to the next 24 hours at hourly steps. Use a positive Go duration such as 30m or 1h for step. The endpoint emits at most 3,000 rows; check truncated before assuming the requested interval is complete. For long sweeps, increase the step.
Positions are geometric: no atmospheric refraction is applied to displayed elevations. Azimuth is degrees clockwise from north. Phase and illumination are fractions from 0 to 1; distances and lengths carry km or m in their field names. height_m accepts 0.3–6 metres and defaults to 1.8. Polar events may be absent.
A budget for useful work
Default allowance: 60 work credits per minute per token, with a burst of 30. Position and eclipse cost 1 credit; events, day, and ephemeris cost 10. A shared budget of 600 credits per minute, with a burst of 60, applies across all tokens. Credits refill continuously. Up to four expensive calculations run at once; the queue waits up to two seconds. Deployment limits may be adjusted.
A token is permission to make bounded requests. Invalid queries and conditional requests also spend credits once authenticated. On 429 or 503, wait at least the Retry-After number of seconds, then back off if the service remains busy. Authentication failures should be fixed before retrying. Usage counters are held in memory and reset when the daemon restarts.
HTTP/1.1 429 Too Many Requests
Retry-After: 10
Cache-Control: no-store
{"error":{"code":"rate_limited","message":"calculation budget exhausted; wait for Retry-After before retrying"}}400— Invalid parameters or an unsupported date.401— Missing, invalid, or revoked Bearer token.404— Unknown endpoint.405— Unsupported method; use GET.429— Per-token or shared work budget exhausted.503— The calculation queue is full, or the service is unavailable.
A stable contract
All endpoints return JSON. Within v1, existing fields keep their names, types, units, and meaning. Clients should tolerate additional fields and new enum or error-code values. The URL namespace is our own: this service does not accept Horizons CGI parameters or promise Horizons response compatibility. Use a server-side client for integrations; cross-origin browser access is not enabled.
Token-protected results use Cache-Control: no-store, including explicitly dated results. ETags support conditional GETs, but a 304 still requires a calculation and spends credits. Request only what you need and reuse results within your own application when appropriate.
Use your token in the live observatory