π ad-bs API
β back to appRead-only Β· unauthenticated Β· JSON. Machine-readable schema: OpenAPI 3.1.
envelopeSuccess β { "ok": true, "data": β¦ } Β· Failure β { "ok": false, "error": "slug", "message": "β¦" } with a proper 4xx status. Responses are never malformed; a date outside the dataset returns 404 out_of_range with the valid range.
date inputsLenient everywhere: 2026-07-09, 20260709, 2026_07_09, 2026/07/09, 2026-07-09T13:45:00Z β separators optional, time stripped.
flagsEvery endpoint (except /dump) accepts ?quiet=true β only the asked-for values, no enrichment β and ?plain=true β text/plain instead of JSON (errors too). quiet+plain on /convert returns just the converted date. Span endpoints also take ?unit=days|weeks|months|years for whole-unit breakdowns with leftovers, e.g. 23 Years, 9 Months, 2 Weeks & 6 Days ago.
# quiet: only the asked-for values, no enrichment
$ curl -s 'https://ad-bs.ajam.dev/api/v1/convert?ad=2026-07-09&quiet=true'
{"ok":true,"data":{"ad":"2026-07-09","bs":"2083-03-25"}}
# plain: plaintext instead of JSON (errors become "error: ..." text too)
$ curl -s 'https://ad-bs.ajam.dev/api/v1/convert?ad=2026-07-09&plain=true'
ad: 2026-07-09
bs: 2083-03-25
day: Thursday
ad_human: Thursday, 9th July - 2026 AD
bs_human: Thursday, 25th Ashad - 2083 BS
divergence_ad_to_bs: +56y 8m 16d
divergence_bs_to_ad: -56y 8m 16d
# KISS: quiet + plain = just the answer
$ curl -s 'https://ad-bs.ajam.dev/api/v1/convert?ad=2026-07-09&quiet=true&plain=true'
2083-03-25
Endpoints
| route | params | description |
|---|---|---|
GET /api/v1/convert | ad | bs | Convert one date. Returns the full entry: both dates, weekday, human forms, divergence (both directions). |
GET /api/v1/today | tz? | Todayβs entry. tz = IANA name or +HH:MM; default Asia/Kathmandu. |
GET /api/v1/range | β | Dataset range (first/last AD+BS), total entries, last update, seed hash. |
GET /api/v1/month | ad | bs (YYYY-MM) | Every day of one calendar month β powers calendar grids. |
GET /api/v1/days-since | ad | bs, tz? | Signed day count from a date to today. |
GET /api/v1/days-between | from_ad|from_bs, to_ad|to_bs | Signed day count between any two dates; calendars can be mixed. |
GET /api/v1/months/:bsYear | path: BS year | BS month lengths for a year + total days. |
GET /api/v1/seasons | β | Static season/climate hint for each of the 12 BS months (also embedded in every entry as season). |
GET /api/v1/dump | β | Full database dump as a JSON attachment: meta, month table, and every mapping fully enriched (day, human dates, divergence). |
Example
$ curl -s 'https://ad-bs.ajam.dev/api/v1/convert?ad=2026-07-09'
{
"ok": true,
"data": {
"id": 38907,
"ad": "2026-07-09",
"bs": "2083-03-25",
"day": "Thursday",
"day_of_week": 4,
"ad_human": "Thursday, 9th July - 2026 AD",
"bs_human": "Thursday, 25th Ashad - 2083 BS",
"ad_month_name": "July",
"bs_month_name": "Ashad",
"season": {
"emoji": "π¦οΈ",
"label": "Hot Β· monsoon onset"
},
"divergence": {
"ad_to_bs": "+56y 8m 16d",
"bs_to_ad": "-56y 8m 16d"
}
}
}
Divergence
Every entry carries a component-wise offset such that AD + ad_to_bs = BS (days borrow against real BS month lengths, months against years). The reverse direction is its exact negation. It lets you fast-forward/rewind between the calendars by whole years/months/days without re-querying.
Caching & limits
Responses are CORS-open (Access-Control-Allow-Origin: *) and carry ETag + Cache-Control (5 min for stable data, 60 s for today-dependent endpoints). Send If-None-Match to get cheap 304s β the ETag changes the moment the dataset is corrected. Heavy callers may hit a best-effort per-IP rate limit (429 with Retry-After); pace to a few requests per second and you will never see it.
Reliability
The dataset is a manually curated mapping of 81,919 consecutive days. Every rebuild is verified with round-trip regeneration plus a deterministic anomaly checker before it goes live; full dumps and pre-mutation snapshots are kept in object storage, restorable after validation. Inputs are validated; the API returns typed errors rather than guesses.