Developer guide
Build with the Nepali calendar
The BS Calendar API gives your website and app Bikram Sambat ⇄ AD conversion, ready-to-render month grids, holidays and events, and a date-picker theme that admins control from one dashboard.
Quick start
- Get an API key. An admin creates one in the dashboard under API keys. For local development use
pk_dev_local_public_key_0001. - Send it in the
X-Api-Keyheader. Every endpoint is under/api. - Render what comes back. Month grids already contain both dates for every cell, so your app never converts dates itself.
API keys
There are two kinds of key. Both go in the X-Api-Key header.
pk_ Public key
For code that runs on the user's device: browsers and mobile apps. Anyone can read it, so it is read-only, rate-limited per user IP, and for websites it can be locked to your site's address with allowedOrigins.
sk_ Server key
For your own backend only (for example a Next.js server component or route handler). Never ship it to a browser or app bundle. Its rate limit applies to the key as a whole.
CORS_ALLOWED_ORIGINS. Ask an admin to add your site's address, and to issue a public key with the same allowedOrigins.
Base URL and versions
The API is served on the same address as this site. Both forms work:
| Form | Example |
|---|---|
With the /api prefix | /api/v1/today |
| Direct | /v1/today |
All paths start with /v1. New fields may be added to responses at any time, so ignore fields you don't know. Breaking changes would go to /v2.
Endpoints
The public read API. Full parameters and schemas are in the API reference.
| Endpoint | Returns |
|---|---|
GET /v1/today | Today in BS and AD. Optional ?tz=Asia/Kathmandu. |
GET /v1/convert?ad=… | ?bs=… | One date in both calendars, with weekday and month names. |
GET /v1/months/{BS|AD}/{year}/{month} | A 42-cell month grid. Add include=events, weekStart, weekendDays. |
GET /v1/events?from=…&to=… | Published events in a range. Filters: basis, category, q. |
GET /v1/categories | Event categories, their colours and holiday flags. |
GET /v1/manifest?app=web | Current versions of everything an app caches, and links to them. |
GET /v1/calendar/data/{version} | The full BS year table (immutable), for offline conversion. |
GET /v1/ui-config/{app}/{version} | The admin-designed theme and defaults (immutable). |
GET /v1/events/buckets/{bsYear}/{version} | All events of one BS year (immutable), for offline apps. |
GET /v1/events.ics | A calendar subscription feed. |
Dates and conversion
Dates are always YYYY-MM-DD strings, in either calendar. BS months are numbered 1 (Baisakh) to 12 (Chaitra). Weekdays are 0 (Sunday) to 6 (Saturday).
- The supported range comes from the official year table (see the manifest's
supportedRange). Dates outside it return422. - Each BS year is
verifiedorprojected. Projected years are estimates that can change when the official calendar is published, so show a hint if it matters to your users. bs.dateNegives the date in Devanagari digits, and every name comes in English (en) and Nepali (ne).
Try it without code: the converter on the home page uses this same data.
Month grids
GET /v1/months/BS/2083/6?include=events returns everything a calendar view needs: always 42 cells (6 weeks), each with both dates and its flags, plus the events of the visible days.
Cells outside the month have inMonth: false; render them dimmed or blank. isWeekend follows weekendDays (default Saturday).
Holidays and events
Admins add festivals, public holidays and events on BS or AD dates, including yearly repeats. Apps only see published events.
Every event has a light and dark colour from its category. Use the colour for the current colour scheme.
Theme and UI config
Admins design the date picker in the dashboard: colours for light and dark mode, the default calendar (BS or AD), language, digits and week start. Your app reads it and applies it, so a change needs no release.
Caching and sync
Almost everything is immutable and versioned. Only the manifest changes often, so apps check it and download only what changed.
- Check
GET /v1/manifest(sendIf-None-Matchto get304). - CompareEach version with the one in your cache.
- FetchOnly the changed links. They are cached for a year.
- RenderFrom the cache, so the calendar works offline.
Month grids and conversions can be cached for 5 minutes and one hour respectively. The shared client below caches months in memory; the React Native guide adds a device cache.
Errors
Errors use RFC 9457 problem documents (application/problem+json). Switch on code, show detail, and log requestId.
| Status | Meaning |
|---|---|
401 | Missing or unknown API key |
403 | Key not allowed from this origin |
404 | Unknown resource or version |
422 | Bad input or date out of range |
429 | Rate limited; wait Retry-After seconds |
Rate limits
Each key has a per-minute limit set by the admin. Every response carries RateLimit-Limit and RateLimit-Remaining. On 429, wait for Retry-After seconds before trying again. Caching as described above keeps you far below the limit.
Integrations
Shared API client
One small TypeScript file with no dependencies. It works in React, Next.js and React Native, so every guide below uses it. Save it as lib/calendar-api.ts.
And one hook, shared by React and React Native, in lib/use-month.ts:
React
Vite, Create React App, or any client-side React app.
1. Configure
Use a public key locked to your site's origin, and add the origin to the API's CORS_ALLOWED_ORIGINS.
2. A month calendar
BS/AD switch, previous and next month, holidays and event dots. Every cell already has both dates.
3. A BS ⇄ AD converter
Next.js
App Router (Next.js 15). Keep a server key on the server; the browser never sees it.
1. Configure
Use a server key (sk_…). Variables without NEXT_PUBLIC_ stay on the server.
2. Server components: render today and holidays with caching
Fetch on the server with Next's data cache. Pages stay fast and the key stays secret.
3. A proxy route for interactive components
Client components can't hold the server key, so they call your own route handler, which adds the key and only forwards read-only calendar paths.
NEXT_PUBLIC_CAL_KEY=pk_…. Then add your site to CORS_ALLOWED_ORIGINS and lock the key to your origin.
React Native
Expo or bare React Native. No native modules, works in Expo Go and offline.
1. Setup
| App runs on | API address |
|---|---|
| Android emulator | http://10.0.2.2:8080 |
| iOS simulator | http://localhost:8080 |
| A phone on the same Wi-Fi | http://<your computer's LAN IP>:8080 |
| Production | https://… (release builds block plain http) |
Mobile apps send no Origin header, so CORS doesn't apply. Ask for a public key without allowedOrigins.
2. The client, with an offline cache
Copy calendar-api.ts and use-month.ts into lib/. Then wrap months so the last good copy is kept on the device:
3. A month calendar screen
4. A converter screen
docs/react-native.md adds a bottom-sheet date picker that applies the admin theme from /v1/ui-config/mobile/…, and full offline sync through the manifest.
Calendar apps (ICS)
Show Nepali holidays in Google Calendar, Outlook or Apple Calendar.
Calendar apps can't send headers, so this one endpoint accepts the key as a query parameter. Use a public key.
- Google Calendar: Other calendars → + → From URL.
- Outlook: Add calendar → Subscribe from web.
- Apple Calendar: File → New Calendar Subscription.
Optional parameters: fromYear, toYear (BS, at most 5 years), category, lang.