Accuracy and verification
A chart is only useful if it is right, and you should not have to take that on trust. This page describes how the engine is tested, the limits it must meet, the latest results and the data you can use to check the numbers yourself.
Method
Section titled “Method”-
Tested against Swiss Ephemeris. The engine is compared with Swiss Ephemeris 2.10.03 and its JPL-based ephemeris files on 5,433 births drawn from a fixed random seed (
0x1a6a2026), so the same sample is used every time:- 5,000 random births from 1900 to 2100: half in 29 cities weighted towards Nepal and India, half at random places between 60° S and 65° N.
- 433 edge cases placed seconds to minutes away from sign, nakshatra, pada, lagna sign, tithi and yoga boundaries, and at clock changes in Nepal, the UK, the US and Australia.
Five of the clock-change cases are local times that repeat or never happen, sent without
disambiguation. The engine correctly answersambiguous_local_timefor them, so 5,428 births are compared. -
Same instant, same conventions. Both sides are evaluated at the same UT instant: geocentric apparent positions, true equinox of date, Lahiri ayanamsha including nutation, mean node, and the lagna as the tropical ascendant minus the ayanamsha. Vimshottari dates on the reference side come from its sidereal Moon with 365.25-day years.
-
Sunrise and sunset are compared for every day of 1990, 2026 and 2075 in Kathmandu with the
center_geometricconvention, and for 2026 withupper_limb_refractedin Kathmandu andcenter_geometricin London and Reykjavik. -
Hard limits block the release. If any sample crosses a hard limit below, the release does not ship.
-
Reviewed conventions. Guna tables, dasha rules and choghadiya sequences are reviewed by a contracted jyotish reviewer before they ship.
-
Deterministic output. The same input with the same engine and data versions returns byte-identical JSON. Dasha output never depends on the server clock.
-
Everything named.
metain every response states the engine version, ayanamsha and its value, node type and data versions. See Conventions.
Tolerances
Section titled “Tolerances”The target is what each release aims for. The hard limit is the line no sample may cross.
| Quantity | Target | Hard limit |
|---|---|---|
| Moon longitude | ≤ 15″ | > 60″ |
| Sun and planet longitudes | ≤ 5″ | > 60″ |
| Lagna longitude, same UT | ≤ 10″ | > 30″ |
| Ayanamsha | ≤ 1″ | > 1″ |
| Mean node | ≤ 5″ | > 10″ |
| Discrete fields: sign, D9, nakshatra, pada, tithi, yoga | Exact | A mismatch is allowed only when the underlying value is within that quantity’s hard limit of a boundary |
| Dasha boundary dates | ± 1 day | > 1 day |
| Sunrise and sunset, against the configured convention | ± 1 min | > 2 min |
The “discrete fields” rule matters in practice. A Moon 20″ from a nakshatra boundary is inside the Moon’s 60″ hard limit, so two correct engines may legitimately disagree on its nakshatra. Everywhere else, discrete fields must match exactly.
Latest results
Section titled “Latest results”Run of 2026-10-21, engine 0.1.0, against Swiss Ephemeris 2.10.03 with JPL-based ephemeris files. Differences are |engine − reference| over the 5,428 compared births, using the longitudes the API returns (4 decimal places).
| Quantity | Worst case | 99th percentile | Target | Result |
|---|---|---|---|---|
| Moon longitude | 4.20″ | 2.43″ | ≤ 15″ | Target met |
| Sun longitude | 2.19″ | 1.63″ | ≤ 5″ | Target met |
| Mercury longitude | 11.33″ | 7.28″ | ≤ 5″ | Target missed, inside hard limit |
| Venus longitude | 20.24″ | 12.73″ | ≤ 5″ | Target missed, inside hard limit |
| Mars longitude | 12.68″ | 7.51″ | ≤ 5″ | Target missed, inside hard limit |
| Jupiter longitude | 10.14″ | 8.86″ | ≤ 5″ | Target missed, inside hard limit |
| Saturn longitude | 13.07″ | 11.06″ | ≤ 5″ | Target missed, inside hard limit |
| Lagna longitude | 4.67″ | 2.42″ | ≤ 10″ | Target met |
| Ayanamsha (Lahiri) | 0.43″ | 0.35″ | ≤ 1″ | Target met |
| Mean node | 0.43″ | 0.36″ | ≤ 5″ | Target met |
| Dasha boundary dates (maha and antar) | 0.56 days | 0.31 days | ± 1 day | Target met |
Sunrise and sunset, center_geometric, Kathmandu |
0.80 s | 0.70 s or less | ± 1 min | Target met |
Sunrise and sunset, upper_limb_refracted, Kathmandu 2026 |
4.80 s | 4.64 s | ± 1 min | Target met |
Discrete fields: 227,976 checks of sign, D9, nakshatra, pada, tithi and yoga gave 57 mismatches, every one with the reference value within 2.3″ of a boundary, so all are allowed by the boundary rule. Most are the edge cases placed on purpose next to a boundary.
No sample crossed a hard limit. The Moon, Sun, lagna, ayanamsha, node, dasha and sunrise figures meet their targets. Mercury, Venus, Mars, Jupiter and Saturn do not yet meet the 5″ target, at up to 20.24″ for Venus, but stay well inside the 60″ hard limit. Seven births are left out of the dasha row because the Moon sits less than 1″ from a nakshatra boundary and the two sides start from a different lord; they are counted under discrete fields.
Birth-time sensitivity
Section titled “Birth-time sensitivity”Ephemeris error is rarely the biggest uncertainty in a chart: the recorded birth time is. Set options.sensitivityMinutes on a kundali request and the response lists any change of the D1 lagna sign, D9 lagna sign, Moon nakshatra or Moon pada within that many minutes, with the instant of each change. The engine’s tests check that each changesAt falls within 1.5 seconds of the change in the engine’s own positions. Compared with a reference, the position differences above move these instants slightly: a 4.7″ lagna difference is about 0.3 seconds of time, and a 4.2″ Moon difference about 8 seconds. See Kundali.
The golden corpus
Section titled “The golden corpus”Version 1 of the golden corpus is public: 500 of the compared births, with the request, our output, the Swiss Ephemeris reference values and the difference for each. 400 cases are spread evenly through the random births and 100 through the edge cases.
- Corpus: golden-corpus-v1.json (JSON, 1.7 MB, published 2026-10-21, engine
0.1.0) - Summary: golden-corpus-v1.summary.json, with the worst case, 99th percentile, target and hard limit of every quantity above, the sample size and seed, and the SHA-256 of the corpus file
The current engine is a pre-release (engineVersion 0.1.0), and its results may still change within the tolerances above. When an engine release changes the results, a new corpus version will be published and announced in the changelog.
Format
Section titled “Format”The file is one JSON object. The top level describes the run:
| Field | Meaning |
|---|---|
version, published |
Corpus version and publication date. |
engine |
engineVersion, the source commit, deltaTModelVersion, and the versions of the ephemeris library, time zone library and tzdb. |
reference |
The reference (Swiss Ephemeris 2.10.03), the SHA-256 of each ephemeris file it used, and the conventions in plain words. |
sample |
The seed, the number of cases, how they were picked and the size of the full run. |
cases |
The 500 cases. |
Each case has these fields:
| Field | Meaning |
|---|---|
id |
R0000 to R4999 for random births, E-… for boundary edge cases, TZ-… for clock-change cases. |
kind |
random-place, city:<name>, or edge:<type> such as edge:moon-nakshatra or edge:timezone. |
request |
The body to send to POST /v1/kundali: birth (calendar, date, time, place, sometimes utcOffset) and options (ayanamsha, nodeType, and disambiguation or nepalHistoricOffset when the case needs them). |
resolved |
instantUtc, tzid and utcOffset: the UTC instant every value below was computed for. |
engine |
Our output: ayanamsha in degrees; lagna and grahas.<id> (sun, moon, mercury, venus, mars, jupiter, saturn, rahu, ketu), each with sidereal longitude in degrees and 1-based sign, nakshatra, pada and d9 (D9 sign); 1-based tithi and yoga; and vimshottari, the maha dasha periods with lord, start and end. |
reference |
The same fields from Swiss Ephemeris. Longitudes have 6 decimal places; the signs, nakshatras, padas, tithi and yoga are derived from them the same way. |
deltaArcsec |
Engine minus reference in arcseconds, signed, for each graha (Rahu stands for both nodes), the lagna and the ayanamsha. |
Compare with your own reference
Section titled “Compare with your own reference”-
Download the corpus and check its SHA-256 against the summary.
-
For each case, compute positions in your own reference at
resolved.instantUtc, atrequest.birth.place, with the conventions above: geocentric apparent, true equinox of date, Lahiri including nutation, mean node, lagna as tropical ascendant minus ayanamsha. Useresolved.instantUtcdirectly so time zone handling does not mix with the astronomy. -
Compare each longitude with
referenceand withengineon a circle, so 359.9999° and 0.0001° are close:const diffArcsec = (a, b) => ((((a - b) % 360) + 540) % 360 - 180) * 3600; -
Compare the discrete fields exactly, and apply the boundary rule above to any mismatch.
-
To check the live API, send each
requesttoPOST /v1/kundali. With the samemeta.engineVersionas the corpus, the longitudes should matchengine. Ifsubject.instantUtcdiffers fromresolved.instantUtc, the difference comes from time zone data, not from the astronomy.
Without the corpus, you can compare any of your own results the same way:
- Record the full request and the response’s
meta. - Configure your reference to the same conventions: the same ayanamsha (
meta.ayanamsha.id; comparemeta.ayanamsha.degreesfirst), node type, whole-sign houses and the UTC instant fromsubject.instantUtc. - Compare longitudes in arcseconds, and discrete fields with the boundary rule above.
Most disagreements turn out to be a different time zone offset (check subject.utcOffset and offsetSource), a different ayanamsha, or the true node against the mean node.
Report a problem
Section titled “Report a problem”If a result is outside the tolerances, write to hello@navapatra.com with the request, the response’s meta and x-request-id, and your reference values. We aim to fix confirmed accuracy bugs within 7 days; see Reporting wrong results in the Terms.