Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Nepal Rastra Bank Forex API Integration

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.

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.

NRB ServerXML FeedUpdated ~10:00 NPTRate LimitedLaravel ApplicationScheduled JobRedis Cache (24h)DB FallbackConsumersE-commerce CartAccounting SystemPublic Dashboard
Nepal Rastra Bank Forex API Integration architecture: scheduled ingestion, multi-layer caching, and consumer isolation

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.

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.

LayerPurposeTTL / RetentionWhen Used
RedisPrimary read cache24 hoursEvery user-facing request
MySQL/PostgreSQLDurable fallback + audit trailIndefinite (partitioned yearly)Cache miss, reporting, compliance
Local file backupDisaster recoveryLast 30 daysBoth 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.

Fetch NRB XMLHTTP 200 + Valid XML?NoLog + Retry QueueYesNon-empty Rates Array?NoAlert + Serve StaleYesWrite Redis + DBUpdate Last-Success Meta
Validation pipeline for Nepal Rastra Bank Forex API Integration with explicit failure branches and stale-data fallback

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.

NRB Official Feed✓ Legally compliant for NPR✓ Free, no auth required✓ Matches bank settlement✗ XML only, no JSON✗ Once-daily updates✗ No SLA or supportBest for: Invoicing, Tax,Banking, ComplianceCommercial APIs✓ JSON, REST, webhooks✓ Real-time + deep history✓ SLA + documentation✗ Not legal tender in Nepal✗ NPR rate ≠ bank rate✗ Paid ($10–$100/mo)Best for: Analytics, UX,Non-NPR Currencies
Decision framework for choosing between NRB and commercial sources in Nepal Rastra Bank Forex API Integration projects

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.

Frequently Asked Questions

No, NRB does not offer a public REST API. Developers must scrape the daily CSV or HTML from nrb.org.np or use authorized commercial bank APIs that aggregate this data.

Parse the official daily CSV export using Laravel HTTP client and regex, or integrate with NIC Asia or Nabil Bank APIs which provide structured JSON feeds of NRB reference rates for authorized merchants.

NRB data is free but requires custom scraping development costing Rs 25,000–45,000 (USD 190–340). Commercial bank API access typically costs Rs 5,000–15,000 monthly plus setup fees for verified business accounts.

Yes, NRB publishes exchange rates as public information. However, automated scraping must respect server load, cache responses aggressively, and never misrepresent the source. I always add attribution linking back to nrb.org.np on production sites to maintain compliance and transparency with regulators.

NRB publishes reference rates only. Commercial banks add margins for profit and risk management. On eCommerce platforms like Petals Nepal, I display both NRB reference and actual bank transaction rates separately so customers understand pricing discrepancies and avoid checkout confusion during international payment processing.

Sync once daily after 10:00 AM NST when NRB updates rates. More frequent polling wastes bandwidth since rates change only on working days. In my Laravel applications, I schedule a queued job at 10:30 AM with Redis caching to serve cached rates instantly while background refresh prevents stale data during peak traffic hours.

Yes, services like Open Exchange Rates or CurrencyAPI include NRB data but charge USD 10–30 monthly. For Nepal-focused projects, direct NRB integration eliminates recurring costs and ensures regulatory accuracy. I recommend third-party APIs only when you need real-time intraday rates or multi-country coverage beyond NRB scope.

NRB provides daily CSV files and HTML tables containing currency code, buying rate, selling rate, and effective date. The CSV structure changes occasionally without notice. I build defensive parsers with fallback logic and validation tests to handle schema drift gracefully, logging parse failures to Sentry for immediate investigation during production deployments.

Implement circuit breakers with exponential backoff, cache last known good rates in Redis with 24-hour TTL, and queue retry jobs. Display cached rates with timestamp warnings to users. On legal-tech portals I have built, this pattern prevents checkout failures during NRB site maintenance windows while maintaining user trust through transparent data freshness indicators.

No formal approval required for displaying publicly published reference rates. However, financial institutions and payment processors may require compliance documentation. Always include disclaimers stating rates are indicative only and not binding offers. I consult clients to ensure their terms of service align with NRB publication guidelines and Nepali financial regulations.

NRB rates are reference benchmarks updated daily, not real-time transaction rates. Actual bank transfers use live interbank rates with spreads. For WooCommerce stores processing NPR payments, I configure dynamic conversion buffers of 2–3 percent above NRB rates to cover bank margins and prevent revenue loss from rate fluctuations between order placement and settlement.

Never expose API keys or scraping credentials in frontend code. Store secrets in .env files excluded from version control. Use HTTPS-only connections, validate SSL certificates, sanitize parsed data against injection attacks, and rate-limit internal endpoints. On Deployer-managed servers, I restrict cron job permissions and monitor outbound requests via fail2ban to detect compromised scraping routines.

Yes, Redis is ideal for caching daily NRB rates with 24-hour expiration. Tag cache entries by date for easy invalidation. Warm cache during scheduled syncs to prevent thundering herd on cache miss. In high-traffic Laravel applications, I use atomic locks to ensure only one worker refreshes rates while others serve cached values, eliminating duplicate scraping requests.

Mock HTTP responses using Laravel Fake or record VCR cassettes during development. Create fixture CSV files matching NRB schema for unit tests. Test edge cases like missing currencies, malformed dates, and empty responses. I maintain separate staging environments with mocked NRB endpoints to validate parsing logic before deploying scraper changes to production via GitLab CI pipelines.

Rotate user agents, implement respectful delays between requests, or switch to RSS feeds if available. Contact NRB IT department for whitelisting if serving legitimate public interest. As fallback, integrate with authorized data vendors like Fonepay or ConnectIPS that license NRB data commercially. On client projects, I always build adapter layers allowing quick provider swaps without rewriting business logic.

Share this article

Quick Contact Options
Choose how you want to connect me: