
August 16, 2026
10 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Building financial or e-commerce software in Nepal requires accurate, compliant exchange rate data, and the only authoritative source is the central bank. A correct Nepal Rastra Bank Forex API Integration allows your application to fetch daily buying, selling, and reference rates directly from the regulator rather than relying on unverified third-party scrapers. This guide covers the exact HTTP endpoints, XML parsing logic, caching architecture, and error handling patterns I use in production Laravel systems serving Nepali businesses.
/api/forex/v1/daily-rates, parsing the response into structured data, and caching it in Redis with a TTL aligned to NRB’s publishing schedule. Always implement fallback storage and never call the upstream endpoint on every user request to avoid rate limiting and ensure compliance.For developers building payment gateways or financial dashboards in Nepal, treating this data as a first-class infrastructure dependency is non-negotiable. The central bank publishes rates once per business day, typically between 9:30 AM and 10:30 AM NPT, but the exact timing varies. Your system must handle this asynchronous update cycle gracefully while serving stale-but-valid cached data during off-hours. If you are architecting a broader financial platform, understanding these constraints early prevents costly rewrites later. For teams evaluating whether to build custom integrations versus using existing packages, my article on Laravel API best practices covers the architectural decisions that apply here.
How does the Nepal Rastra Bank Forex API integration work technically?
The NRB exposes foreign exchange rates through a public REST-like endpoint that returns XML. Despite being called an "API" in common parlance, it functions more like a scheduled data feed. Understanding this distinction prevents most implementation failures. The primary endpoint for daily rates is:
GET https://www.nrb.org.np/api/forex/v1/daily-rates This endpoint returns an XML document containing all published currencies for the current business day. Each entry includes the currency ISO code, name, unit quantity (usually 1, but 100 for JPY and IDR), buying rate, selling rate, and reference rate. There is no authentication required for read access, but the server enforces implicit rate limits. Making hundreds of requests per minute will result in temporary IP blocks.
In practice, your Laravel application should never call this endpoint during a user-facing HTTP request. Instead, a scheduled Artisan command fetches the data, validates it, stores it in both Redis and a database table, and serves all subsequent reads from cache. This pattern decouples your application’s availability from NRB’s uptime and keeps you well within acceptable usage limits.
Parsing the XML response safely
The XML structure is straightforward but requires defensive parsing. Currency codes are uppercase ISO 4217, but the unit field varies. Japanese Yen (JPY) and Indonesian Rupiah (IDR) are quoted per 100 units; everything else is per 1 unit. Missing this detail causes 100x pricing errors in production.
<?php
// app/Services/NrbForexParser.php
namespace App\Services;
use SimpleXMLElement;
use App\Models\ExchangeRate;
class NrbForexParser
{
public function parse(string $xmlContent): array
{
$xml = new SimpleXMLElement($xmlContent);
$rates = [];
foreach ($xml->Currency as $currency) {
$unit = (int) $currency->Unit;
$buying = (float) $currency->Buying;
$selling = (float) $currency->Selling;
// Normalize to per-unit rate for consistent storage
$rates[] = [
'currency_code' => strtoupper((string) $currency->Code),
'currency_name' => (string) $currency->Name,
'unit' => $unit,
'buying_rate' => $buying / $unit,
'selling_rate' => $selling / $unit,
'reference_rate'=> ((float) $currency->Reference) / $unit,
'published_date'=> now()->toDateString(),
];
}
return $rates;
}
} Always validate that the parsed array is non-empty before writing to cache or database. An empty result usually means the XML changed structure or the network returned an error page disguised as 200 OK. Log the raw response body when validation fails; debugging without it wastes hours.
What is the recommended caching strategy for NRB exchange rates?
Since NRB publishes rates once per business day, your cache TTL should reflect that cadence. A 24-hour Redis TTL works for most applications, but aligning cache invalidation with the actual publish window improves freshness. I schedule the fetch job at 10:30 AM NPT on weekdays with a retry at 11:30 AM if the first attempt fails.
# routes/console.php (Laravel 12)
use Illuminate\Support\Facades\Schedule;
Schedule::command('nrb:fetch-rates')
->weekdays()
->at('10:30')
->timezone('Asia/Kathmandu')
->onFailure(function () {
Schedule::command('nrb:fetch-rates')
->at('11:30')
->timezone('Asia/Kathmandu');
}); Redis serves as the primary read cache because sub-millisecond latency matters for checkout flows and real-time dashboards. But Redis can evict keys under memory pressure, and deployments may flush it accidentally. Always maintain a database fallback. Query the DB only when Redis misses, then repopulate the cache immediately.
| Layer | Purpose | TTL / Retention | When Used |
|---|---|---|---|
| Redis | Primary read cache | 24 hours | Every user-facing request |
| MySQL/PostgreSQL | Durable fallback + audit trail | Indefinite (partitioned yearly) | Cache miss, reporting, compliance |
| Local file backup | Disaster recovery | Last 30 days | Both Redis and DB unavailable |
For teams managing multiple Nepal-focused projects, this three-tier approach prevents single points of failure. On a legal-tech portal I built that processes notarization fees in USD and EUR, losing exchange rate data mid-transaction would create reconciliation nightmares. The DB fallback ensured we could always reconstruct the exact rate used at invoice time, even if Redis was flushed during a deploy.
How do you handle errors and rate limiting in production?
NRB’s infrastructure is generally reliable but not designed for high-frequency programmatic access. Common failure modes include SSL certificate rotation without notice, XML schema changes, and silent rate limiting that returns HTML error pages with HTTP 200 status. Your integration must detect these conditions explicitly.
Implement exponential backoff for retries, but cap total attempts. Three retries over 30 minutes is reasonable; beyond that, the issue is likely upstream and further attempts waste resources. Store the last successful fetch timestamp in a dedicated meta table or cache key. Expose this timestamp in your admin dashboard so operations staff can verify data freshness without querying the database directly.
// app/Console/Commands/FetchNrbRates.php
public function handle(NrbForexParser $parser): int
{
try {
$response = Http::timeout(15)
->get('https://www.nrb.org.np/api/forex/v1/daily-rates');
if (!$response->successful() || !str_contains($response->body(), '<Currency>')) {
Log::error('NRB fetch failed', ['status' => $response->status(), 'body_preview' => substr($response->body(), 0, 500)]);
return self::FAILURE;
}
$rates = $parser->parse($response->body());
if (empty($rates)) {
Log::warning('NRB returned empty rates');
return self::FAILURE;
}
ExchangeRate::upsert($rates, ['currency_code', 'published_date']);
Cache::put('nrb_rates_today', $rates, now()->addHours(24));
Cache::put('nrb_last_success', now(), now()->addDays(7));
$this->info("Stored {$rates} rates");
return self::SUCCESS;
} catch (\Throwable $e) {
Log::error('NRB fetch exception', ['message' => $e->getMessage()]);
return self::FAILURE;
}
} This command returns explicit exit codes so your CI/CD or monitoring system can distinguish success from failure. On projects where I’ve integrated multiple Nepal-specific services, consistent exit-code discipline makes automated alerting trivial. If you’re building a team around hiring web developers in Nepal, enforcing these patterns early reduces onboarding friction and production incidents.
Why should you store historical NRB rates in a database?
Caching solves today’s problem; databases solve next year’s audit. Nepali tax authorities, auditors, and payment reconciliators frequently require proof of the exact exchange rate applied on a specific past date. Redis alone cannot satisfy this requirement. A normalized exchange_rates table with composite unique index on (currency_code, published_date) provides both fast lookups and immutable history.
Partition this table by year once it exceeds a few million rows. Queries filtered by date range stay fast, and archival becomes simple. For e-commerce platforms processing cross-border transactions, joining order records against historical rates enables accurate NPR revenue reporting without recalculating from live data. On a WooCommerce multi-currency store I maintained, this approach cut monthly reconciliation time from two days to under an hour.
Schema design for compliance and performance
CREATE TABLE exchange_rates (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
currency_code CHAR(3) NOT NULL,
currency_name VARCHAR(100) NOT NULL,
unit SMALLINT UNSIGNED NOT NULL DEFAULT 1,
buying_rate DECIMAL(12,6) NOT NULL,
selling_rate DECIMAL(12,6) NOT NULL,
reference_rate DECIMAL(12,6) NOT NULL,
published_date DATE NOT NULL,
fetched_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_currency_date (currency_code, published_date),
INDEX idx_published_date (published_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; Use DECIMAL(12,6) instead of FLOAT. Floating-point rounding errors compound across thousands of transactions and cause penny discrepancies that frustrate accountants. Six decimal places accommodates NRB’s precision while leaving headroom for future granularity. The fetched_at timestamp records when your system actually ingested the data, which differs from published_date and matters during outage investigations.
How does NRB data compare to commercial forex APIs for Nepal projects?
Commercial providers like Open Exchange Rates or Fixer.io offer cleaner JSON APIs, higher rate limits, and historical data going back decades. But they derive their NPR rates from interbank markets, not NRB’s official publication. For any transaction touching Nepali banking channels, NRB rates are legally binding. Commercial rates may differ by 0.5–2%, creating settlement mismatches and compliance exposure.
A hybrid approach works best for sophisticated applications. Use NRB as the source of truth for NPR conversions and compliance reporting. Supplement with a commercial API for real-time currency selectors, trend charts, or non-NPR pairs where legal compliance isn’t required. Clearly label which source powers each UI element to avoid user confusion and auditor questions.
Final recommendations for Nepal Rastra Bank Forex API Integration
Treat Nepal Rastra Bank Forex API Integration as infrastructure, not a feature. Build the scheduled ingestion, multi-layer caching, and validation pipeline before writing any business logic that depends on rates. Test your parser against actual NRB responses, not mocked data, because schema drift happens without announcement. Document your fallback behavior so non-technical stakeholders understand why yesterday’s rate might appear during an upstream outage.
If you’re implementing this for a client project and need hands-on guidance, or if your team is struggling with unreliable rate data in production, reach out to discuss your specific requirements. I’ve shipped this integration across legal-tech portals, e-commerce platforms, and financial dashboards in Nepal, and can help you avoid the pitfalls that only surface after launch.

