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

  1. Get an API key. An admin creates one in the dashboard under API keys. For local development use pk_dev_local_public_key_0001.
  2. Send it in the X-Api-Key header. Every endpoint is under /api.
  3. 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.

Websites: the API only accepts browser requests from origins listed in 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:

FormExample
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.

EndpointReturns
GET /v1/todayToday 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/categoriesEvent categories, their colours and holiday flags.
GET /v1/manifest?app=webCurrent 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.icsA 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 return 422.
  • Each BS year is verified or projected. Projected years are estimates that can change when the official calendar is published, so show a hint if it matters to your users.
  • bs.dateNe gives 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.

  1. CheckGET /v1/manifest (send If-None-Match to get 304).
  2. CompareEach version with the one in your cache.
  3. FetchOnly the changed links. They are cached for a year.
  4. 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.

StatusMeaning
401Missing or unknown API key
403Key not allowed from this origin
404Unknown resource or version
422Bad input or date out of range
429Rate 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

N

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.

Prefer a public key? You can also use the React setup in client components with 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 onAPI address
Android emulatorhttp://10.0.2.2:8080
iOS simulatorhttp://localhost:8080
A phone on the same Wi-Fihttp://<your computer's LAN IP>:8080
Productionhttps://… (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

Going further: the repository's 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.

API reference →Every endpoint, parameter and schema Try it →Send real requests from your browser Converter →Convert a date without code