GET /convert & /range
GET /convert
Section titled “GET /convert”Converts one date in the direction implied by calendar.
GET/api/v1/convert?date={YYYY-MM-DD}&calendar={gregorian|hijri}&retro={true|false}Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
date |
string | yes | ISO date (YYYY-MM-DD) |
calendar |
string | no (default gregorian) |
Calendar of the input date |
retro |
true / false |
no (default false) |
Unlocks computed retro dates below the curated table (down to 1945-01-01), tagged mabims-retro. See data coverage for the reason why |
Example
Section titled “Example”curl "https://api.mabims.dev/api/v1/convert?date=2025-01-03&calendar=gregorian"const res = await fetch("https://api.mabims.dev/api/v1/convert?date=2025-01-03&calendar=gregorian");const data = await res.json();console.log(data.output.date); // "1446-07-03"$response = GuzzleHttp\request('GET', 'https://api.mabims.dev/api/v1/convert?date=2025-01-03&calendar=gregorian');$data = json_decode($response->getBody(), true);echo $data['output']['date']; // "1446-07-03"Responses
Section titled “Responses”200 OK
{ "input": { "date": "2025-01-03", "calendar": "gregorian" }, "output": { "date": "1446-07-03", "calendar": "hijri", "day": 3, "month": 7, "month_name": "Rajab", "year": 1446, "weekday": "Jumat" }, "source": "mabims", "warnings": []}400 — invalid date format or unknown calendar
{ "error": { "code": "invalid_date", "message": "'not-a-date' is not a valid ISO date (YYYY-MM-DD)." }}code |
Cause |
|---|---|
invalid_date |
Date is not in YYYY-MM-DD format |
invalid_calendar |
calendar parameter is not gregorian or hijri |
missing_parameter |
date query parameter is missing |
out_of_coverage |
Date is outside table coverage; see /meta |
invalid_retro |
retro param is not a boolean value (accepts true/false, 1/0) |
Boolean flags accept true/false (case-insensitive) and 1/0; absent = false.
404 — date_not_found: no pair exists for that date
{ "error": { "code": "date_not_found", "message": "No calendar pair exists for 2023-01-01 (gregorian). See /api/v1/meta for coverage." }}GET /range
Section titled “GET /range”Converts every day in an inclusive range.
GET/api/v1/range?start={YYYY-MM-DD}&end={YYYY-MM-DD}&calendar={gregorian|hijri}&retro={true|false}Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
start / end |
string | yes | ISO dates; start ≤ end; max span 45 days |
calendar |
string | no (default gregorian) |
Calendar of the input bounds |
retro |
true / false |
no (default false) |
Unlocks computed retro dates below the curated table (down to 1945-01-01), tagged mabims-retro. See data coverage for the reason why |
Example
Section titled “Example”curl "https://api.mabims.dev/api/v1/range?start=2025-01-01&end=2025-01-03&calendar=gregorian"const res = await fetch("https://api.mabims.dev/api/v1/range?start=2025-01-01&end=2025-01-03&calendar=gregorian");const data = await res.json();data.items.forEach(i => console.log(i.gregorian, "→", i.hijri));$response = GuzzleHttp\request('GET', 'https://api.mabims.dev/api/v1/range?start=2025-01-01&end=2025-01-03&calendar=gregorian');$data = json_decode($response->getBody(), true);foreach ($data['items'] as $item) { echo $item['gregorian'] . ' → ' . $item['hijri'] . PHP_EOL;}{ "input": { "start": "2025-01-01", "end": "2025-01-03", "calendar": "gregorian" }, "count": 3, "items": [ { "gregorian": "2025-01-01", "hijri": "1446-07-01", "weekday": "Rabu", "source": "mabims" }, { "gregorian": "2025-01-02", "hijri": "1446-07-02", "weekday": "Kamis", "source": "mabims" }, { "gregorian": "2025-01-03", "hijri": "1446-07-03", "weekday": "Jumat", "source": "mabims" } ], "warnings": []}Each item carries its own source — ranges crossing the table boundary can mix
authoritative and computed data.
For calendar=hijri, ranges can extend past the public table coverage — Hijri
months beyond the table are served from the Neo MABIMS computed tier (up to
2100). start/end still cap at 45 days.
Caching
Section titled “Caching”Responses are immutable per input and sent with Cache-Control: max-age=86400 — safe to cache
at any layer for a full day.
Every response also carries an ETag. Send If-None-Match: <etag> and the server answers 304 Not Modified (empty body) when the data has not changed, with the same Cache-Control and ETag headers.
Errors
Section titled “Errors”All errors return a standard JSON envelope:
{ "error": { "code": "range_too_large", "message": "Range is limited to 45 days." }}code |
HTTP | Cause |
|---|---|---|
invalid_step |
400 | Step is not day |
range_too_large |
400 | Range exceeds 45 days |
out_of_coverage |
400 | Date outside table coverage |
invalid_retro |
400 | retro param is not a boolean value (accepts true/false, 1/0) |
rate_limit_exceeded |
429 | Too many requests; wait per the Retry-After header (seconds) |
not_found |
404 | Unknown path (uniform error envelope) |
Boolean flags accept true/false (case-insensitive) and 1/0; absent = false.
