Documentation · itzsa
NRB Forex
Typed client for Nepal Rastra Bank’s public forex API — fetch, cache, and convert foreign currency → NPR with correct unit handling. No React required.
Unofficial
Not affiliated with or endorsed by Nepal Rastra Bank. Rates follow NRB’s publication schedule and API availability. Upstream docs: NRB Forex.Installation#
Zero runtime dependencies. Uses native fetch (Node 18+).
pnpm add @itzsa/nrb-forexQuick start#
Module helpers use a default client; create your own for Redis or custom fetch.
import {
getRate,
getRatesForDate,
convert,
createNrbForexClient,
} from "@itzsa/nrb-forex";
const usd = await getRate("USD"); // today (UTC)
const all = await getRatesForDate("2026-07-17");
// Unit-aware: 100 INR → NPR on buy side
const npr = await convert(100, "INR", "NPR", {
date: "2026-07-17",
side: "buy",
});
const client = createNrbForexClient({ fallbackToPreviousDay: true });
await client.getRate("USD", "2026-07-19");Examples#
Live via the docs proxy. NRB publishes ~22 ISO currencies (not every ISO 4217 code).
Latest rates#
Every currency for the day — date picker, multi-select popover, Buy / Sell / Mid.
Latest FOREX Rates
Nepal Rastra Bank · NPR quotes · —
No rates match the current filters.
Convert to NPR#
Unit-aware — INR (100) and JPY (10) stay correct automatically.
Rate history#
Pick currency, from, and to — getRateHistory with Buy / Sell.
No published days in range.
Published currencies#
All NRB currencies for a date with Unit / Buy / Sell / Mid.
Full NRB list with Buy / Sell · —
Loading…
Package API#
Typed helpers over the NRB V1 rates endpoint.
Client helpers
| Prop | Type | Default | Description |
|---|---|---|---|
| getRate | (currency, date?, options?) => Promise<ForexRate> | — | Single currency for a day (ISO3, e.g. USD). |
| getRatesForDate | (date?, options?) => Promise<ForexRate[]> | — | All currencies published for one day. |
| getRateHistory | (currency, from, to) => Promise<ForexRate[]> | — | One currency across a date range (charts). |
| getRatesInRange | (from, to) => Promise<DailyForexSnapshot[]> | — | Full snapshots; pagination handled internally. |
| convert | (amount, from, 'NPR', options?) => Promise<number> | — | Foreign → NPR using buy / sell / mid and unit. |
| getSupportedCurrencies | (date?, options?) => Promise<CurrencyInfo[]> | — | ISO3 + name + unit for the day. |
| createNrbForexClient | (options?) => NrbForexClient | — | Isolated client (cache, fetch, retries, fallback). |
Convert & units#
ForexRate
| Prop | Type | Default | Description |
|---|---|---|---|
| currency | string | — | ISO3 code (USD, INR, JPY, …). |
| currencyName | string | — | Display name from NRB (e.g. U.S. Dollar). |
| unit | number | — | NRB quotes buy/sell per this many units (often 1, 10, or 100). |
| buy / sell | number | — | As published — divide by unit for per-1 NPR. |
| date | string | — | Effective rate date (YYYY-MM-DD). |
| publishedOn | string | — | When NRB published the snapshot. |
| isFallback | boolean? | — | True when returned via fallbackToPreviousDay. |
Math
NPR = amount × (buy|sell|mid) / unit. Preferconvert or perUnitRates.Caching#
Past NST days forever; today soft-refreshes every 2h for rare midday revisions.
Why refresh still hit NRB before
Browser demos created a new client on each mount, so in-memory cache was empty after reload. The docs proxy now sendsCache-Control / Next revalidate (2h for today, longer for history), and demos reuse one tab-level client.NRB typically publishes once daily (~09:00–10:00 NST) and may revise
slightly during the business day. This client:
• Past NST dates → cache forever (immutable)
• Today / future → soft TTL of 2 hours (catch revisions, avoid NRB spam)
• Prefer one long-lived client (or Redis) — not a new client per request
• Browser demos: hit a proxy with Cache-Control; do not call NRB from the browserimport {
createNrbForexClient,
MemoryForexCache,
type ForexCache,
} from "@itzsa/nrb-forex";
// Long-lived Node process — in-memory is enough.
// Historical days: cached forever. Today (NST): soft-refresh every 2h
// so rare midday NRB revisions are picked up.
const client = createNrbForexClient({
cache: new MemoryForexCache(),
fallbackToPreviousDay: true, // weekends / pre-publish morning
});
await client.getRatesForDate(); // Nepal "today"
// Serverless / multi-instance — use Redis (or similar) so cache is shared.
const redisCache: ForexCache = {
async get(key) { /* redis.get + JSON.parse */ },
async set(key, value, ttlMs) { /* redis.set with EX */ },
async has(key) { /* … */ },
};
const shared = createNrbForexClient({ cache: redisCache });Errors#
NrbValidationError · NrbApiError · NrbRateNotFoundError — network retries up to 3×.
Mapped from NRB
Missing params or empty ranges surface as typed errors; this client also paginatesgetRatesInRange for you.Node / CLI#
Nightly sync helper and npx CLI.
import { syncDailyRates } from "@itzsa/nrb-forex/node";
await syncDailyRates({
write: async (snapshot) => {
// upsert snapshot.rates into DB / Redis
},
});
// CLI
// npx nrb-forex USD 2026-07-17
// npx nrb-forex USD 2026-07-19 --fallbackNRB Forex API (V1)#
Official upstream contract this package wraps. Published typically once per business day.
| Base URL | https://www.nrb.org.np/api/forex/v1/ |
| Endpoint | GET /rates |
| Full URL | https://www.nrb.org.np/api/forex/v1/rates |
| Empty days | Weekends/holidays may return payload: null — use fallbackToPreviousDay in this client. |
GET /rates#
Returns foreign exchange rates for a given date range. All four query parameters are required.
Query parameters
| Prop | Type | Default | Description |
|---|---|---|---|
| from | string (Y-m-d) | — | Starting date. Required. |
| to | string (Y-m-d) | — | Ending date. Required. |
| page | integer | — | Current page (cursor). Required. |
| per_page | integer 1–100 | — | Items per page. Required. Max 100. |
GET https://www.nrb.org.np/api/forex/v1/rates?page=1&per_page=100&from=2026-07-17&to=2026-07-17Unit quirk
Buy/sell are quoted percurrency.unit foreign units — not always per 1. INR/KRW are often 100; JPY often 10. Prefer convert so you do not forget to divide.Response shape#
Top-level envelope, daily payload, and per-currency rate objects.
Root fields
| Prop | Type | Default | Description |
|---|---|---|---|
| status | { code: number } | — | HTTP-style status from the API body. |
| errors | object | — | Validation and other errors (present even on some successes). |
| errors.validation | Record<string, string[]> | — | Field errors for per_page, page, from, to. |
| params | object | — | Echo of GET parameters (per_page, page, from, to). |
| data | object | — | Main response container. |
| data.payload | array | null | — | Array of daily forex snapshots, or null when empty/invalid. |
| pagination | object | — | Page cursor, totals, and prev/next links. |
data.payload[] day
| Prop | Type | Default | Description |
|---|---|---|---|
| date | string (Y-m-d) | — | FOREX rates for this calendar date. |
| published_on | string | — | When NRB published these rates. |
| modified_on | string | — | Last modification timestamp for the snapshot. |
| rates | array | — | Per-currency buy/sell quotes for the day. |
data.payload[].rates[]
| Prop | Type | Default | Description |
|---|---|---|---|
| currency.name | string | — | Currency display name. |
| currency.iso3 | string | — | ISO 4217 alpha-3 code (USD, INR, …). |
| currency.unit | number | — | Units the buy/sell figures are quoted for. |
| buy | number | — | Buying rate in NPR (for `unit` foreign units). |
| sell | number | — | Selling rate in NPR (for `unit` foreign units). |
pagination
| Prop | Type | Default | Description |
|---|---|---|---|
| page | number | null | — | Current page (cursor). |
| pages | number | null | — | Total number of pages. |
| per_page | number | null | — | Items per page. |
| total | number | null | — | Total number of items across pages. |
| links.prev | string | null | — | URL for the previous page. |
| links.next | string | null | — | URL for the next page. |
{
"status": { "code": 200 },
"errors": { "validation": null },
"params": {
"from": "2026-07-17",
"to": "2026-07-17",
"per_page": 100,
"page": 1
},
"data": {
"payload": [
{
"date": "2026-07-17",
"published_on": "2026-07-17 00:00:17",
"modified_on": "2026-07-17 00:00:17",
"rates": [
{
"currency": { "iso3": "USD", "name": "U.S. Dollar", "unit": 1 },
"buy": 133.45,
"sell": 134.05
},
{
"currency": { "iso3": "INR", "name": "Indian Rupee", "unit": 100 },
"buy": 160.0,
"sell": 160.15
}
]
}
]
},
"pagination": {
"page": 1,
"pages": 1,
"per_page": 100,
"total": 1,
"links": { "prev": null, "next": null }
}
}Status & errors#
Body status.code mirrors HTTP-style outcomes. Validation failures return 400 with field messages.
status.code
| Prop | Type | Default | Description |
|---|---|---|---|
| 200 | OK | — | Request succeeded; inspect data.payload for rates. |
| 400 | Bad Request | — | Invalid or missing arguments — see errors.validation. |
{
"status": { "code": 400 },
"errors": {
"validation": {
"per_page": [
"Per Page is required",
"Per Page must be an integer",
"Per Page must be at least 1",
"Per Page must be no more than 100"
],
"page": ["Page is required", "Page must be an integer"],
"from": [
"From is required",
"From must be date with format 'Y-m-d'"
],
"to": ["To is required"]
}
},
"params": { "from": "", "to": "", "per_page": "", "page": "" },
"data": { "payload": null },
"pagination": {
"page": null,
"pages": null,
"per_page": null,
"total": null,
"links": { "prev": null, "next": null }
}
}Browser / CORS
The official NRB host does not send CORS headers. Docs demos call/api/nrb-forex (same query shape). Node and server runtimes can hit NRB directly.