Späť na blog

Building Multi-Currency Expense Management with an Exchange Rate API

V
Vlado Grigirov
June 11, 2026
Currency API Exchange Rates Expense Management Multi-Currency Finexly Tutorial

Expense management software lives and dies by one number: the converted total your finance team sees in the company's base currency. Get the exchange rate for expense management wrong by even a fraction of a percent, and every foreign receipt drifts out of sync with your accounting ledger, your reimbursements over- or under-pay employees, and your month-end reconciliation turns into a manual hunt for pennies. This guide walks through how to build a multi-currency expense layer correctly with an exchange rate API — including the one rule that separates a toy converter from a system your auditors will actually accept.

If you are building an expense tool, a corporate card platform, a travel-and-expense (T&E) feature, or just adding receipt capture to an existing product, the currency conversion logic is deceptively tricky. The naive approach — call a "latest rates" endpoint and multiply — produces numbers that look right in a demo and fall apart in production. Let's do it properly.

Why Expense Management Needs an Exchange Rate API

A modern expense workflow tracks an employee who spends in one currency, a company that reports in another, and sometimes a reimbursement paid in a third. A consultant based in Berlin flies to Tokyo, pays for a hotel in JPY, works for a company that reports in USD, and gets reimbursed to a EUR bank account. Three currencies, one receipt.

Hard-coding rates or asking employees to convert manually is a non-starter. Manual conversion is error-prone, invites expense fraud through "rate shopping," and creates reconciliation gaps the moment a rate moves. Tools like Expensify, Ramp, and Zoho Expense all auto-fetch exchange rates precisely because outdated or manual rates introduce inaccuracies that complicate reconciliation.

An exchange rate API solves this by giving you programmatic access to accurate rates for any date and any currency pair, so the conversion happens automatically the moment a receipt is captured — and stays consistent forever after.

The Three Currencies Every Expense System Must Track

Before writing a line of code, model your data around three distinct currency roles:

  1. Transaction currency — what the employee actually paid in (the currency printed on the receipt or charged to the card). Always store this, untouched, alongside the original amount.
  2. Base / reporting currency — your company's functional currency, used for the ledger, reports, and budgets. Every expense is converted into this for roll-ups.
  3. Reimbursement currency — what the employee receives. Often the same as the transaction currency (best practice, so the employee bears no FX loss), but sometimes their local payroll currency.

The cardinal sin is destroying the original. Never overwrite the transaction amount with a converted value. Store the original amount and currency permanently, then compute conversions as derived values. This preserves the audit trail and lets you re-run reports if a rate is ever corrected.

The Critical Rule: Use the Transaction-Date Rate, Not Today's Rate

Here is the rule that most "currency converter" tutorials get wrong and that will fail an audit: you must convert each receipt using the exchange rate that was in effect on the date of the transaction — not the rate today.

This is not a stylistic preference. It is a requirement under both major accounting frameworks. Under IAS 21 (IFRS), a foreign currency transaction is initially recorded by applying the spot exchange rate at the date of the transaction. Under ASC 830 (US GAAP), each asset, liability, revenue, or expense is recorded in the functional currency using the exchange rate in effect at that date. In both standards, the transaction date controls the rate — not a later posting date and certainly not "whenever the report was submitted."

Why Live Rates Break Your Books

Imagine an employee buys a €500 dinner on March 15, submits the expense report on April 20, and your system converts it using the April 20 rate. If EUR/USD moved 2% in those five weeks, your reported expense is now wrong by $10 against what actually hit the corporate card statement. Multiply that across thousands of receipts and your expense subledger no longer ties to your bank feed. Reconciliation becomes a nightmare, and your auditors flag it.

The fix is simple: call a historical rates endpoint with the receipt's transaction date. Here is how it looks with Finexly:

curl "https://api.finexly.com/v1/historical?date=2026-03-15&base=JPY&symbols=USD&apikey=YOUR_KEY"
{
  "success": true,
  "base": "JPY",
  "date": "2026-03-15",
  "rates": {
    "USD": 0.006712
  }
}

You convert the receipt with the rate from 2026-03-15, store that rate on the expense record, and the number never changes again — exactly what your accountants need. For a deeper look at date handling and time-series queries, see our historical exchange rates API guide.

Building the Conversion Layer

Let's build the core conversion function. The key inputs are the original amount, the transaction currency, your base currency, and crucially the transaction date.

Python: Convert a Receipt at the Transaction-Date Rate

import requests
from datetime import date

FINEXLY_KEY = "YOUR_KEY"

def convert_receipt(amount, from_currency, to_currency, txn_date):
    """Convert a receipt amount using the rate on the transaction date."""
    if from_currency == to_currency:
        return round(amount, 2), 1.0

    url = "https://api.finexly.com/v1/historical"
    params = {
        "date": txn_date.isoformat(),
        "base": from_currency,
        "symbols": to_currency,
        "apikey": FINEXLY_KEY,
    }
    resp = requests.get(url, params=params, timeout=5)
    resp.raise_for_status()
    rate = resp.json()["rates"][to_currency]

    converted = round(amount * rate, 2)
    return converted, rate

# A ¥48,000 hotel in Tokyo on March 15, reported in USD
converted, rate = convert_receipt(48000, "JPY", "USD", date(2026, 3, 15))
print(f"Converted: ${converted} at rate {rate}")
# Store BOTH the converted value AND the rate on the expense record

Notice the function returns the rate as well as the converted amount. Persisting the rate alongside the expense is what makes the conversion reproducible and auditable months later.

JavaScript: The Same Logic in Node.js

async function convertReceipt(amount, from, to, txnDate) {
  if (from === to) return { converted: Number(amount.toFixed(2)), rate: 1 };

  const url = new URL("https://api.finexly.com/v1/historical");
  url.search = new URLSearchParams({
    date: txnDate,          // "2026-03-15"
    base: from,
    symbols: to,
    apikey: process.env.FINEXLY_KEY,
  });

  const res = await fetch(url);
  if (!res.ok) throw new Error(`Finexly ${res.status}`);
  const data = await res.json();
  const rate = data.rates[to];

  return { converted: Number((amount * rate).toFixed(2)), rate };
}

Caching Daily Rates to Stay Fast and Cheap

Historical rates for a given date never change, which makes them perfectly cacheable. When you import a batch of receipts, group them by date and currency pair so you fetch each rate once rather than per-receipt. A simple (date, base, symbol) -> rate cache — in Redis or even a database table — eliminates redundant calls and keeps you well within your plan limits. For production-grade caching and failure handling patterns, see our guide on API caching and error handling best practices.

rate_cache = {}

def get_cached_rate(from_currency, to_currency, txn_date):
    key = (txn_date.isoformat(), from_currency, to_currency)
    if key not in rate_cache:
        _, rate = convert_receipt(1, from_currency, to_currency, txn_date)
        rate_cache[key] = rate
    return rate_cache[key]

Handling Corporate Card Markups and Foreign Transaction Fees

There is a subtlety that trips up teams reconciling card-fed expenses: the rate on a corporate card statement is not the mid-market rate. Card networks apply their own exchange rate plus a foreign transaction fee, frequently in the 1–3% range. So the figure Visa or Mastercard posts will differ slightly from the mid-market rate your API returns.

You have two sound options:

  • For card-fed transactions, trust the amount the card network actually charged in your base currency. The card statement is the source of truth for money that already moved — don't re-convert it. Use the API rate only to show the employee an informational mid-market comparison.
  • For out-of-pocket (cash) receipts that an employee will be reimbursed for, the mid-market rate from your API on the transaction date is the fair, defensible basis for reimbursement.

Being explicit about which rate applies to which expense type prevents the "why doesn't this match my card bill?" support tickets that plague naive implementations. The mid-market rate is the honest midpoint, and you can read more about why it matters in our accounting software integration guide.

Multi-Currency Reporting and Reconciliation

Once every expense carries a stored base-currency value and the rate used, reporting becomes straightforward. Roll-ups, budget tracking, and department summaries all operate on the pre-computed base-currency column — no live conversion at report time, which means reports are fast and never shift because a rate moved.

A clean expense record looks like this:

{
  "expense_id": "exp_8842",
  "original_amount": 48000,
  "original_currency": "JPY",
  "base_amount": 322.18,
  "base_currency": "USD",
  "rate_used": 0.006712,
  "rate_date": "2026-03-15",
  "rate_source": "finexly:historical"
}

Every field an auditor could ask about — what was paid, in what currency, what it converted to, at what rate, on what date, from what source — is captured. This is the difference between an expense system that scales internationally and one that generates month-end fire drills.

Architecture Checklist for Multi-Currency Expenses

Before you ship, verify your implementation against this checklist:

  • Store the original amount and currency immutably — never overwrite with a converted value.
  • Convert using the transaction-date rate, fetched from a historical endpoint, not today's rate.
  • Persist the rate and rate date on every expense record for auditability.
  • Cache rates by (date, pair) to stay fast and within plan limits.
  • Distinguish card-fed amounts from out-of-pocket reimbursements and apply the correct rate source to each.
  • Handle the same-currency case without an API call (rate = 1).
  • Fail gracefully — if a rate fetch fails, queue the receipt rather than blocking submission, and backfill the rate.
  • Pick an API that covers every currency your employees might spend in — a global workforce will surprise you with exotic currencies.

Finexly covers 170+ currencies with both real-time and historical data, which is exactly the coverage a global expense tool needs. As your transaction volume grows, the pricing plans scale with you without forcing an architecture rewrite.

Frequently Asked Questions

Which exchange rate should an expense report use — the transaction date or the submission date? Always the transaction date. Both IFRS (IAS 21) and US GAAP (ASC 830) require recording a foreign currency transaction at the spot rate in effect on the date the transaction occurred. Using the submission or approval date will misstate the expense and break reconciliation against card statements.

Why doesn't the converted amount match my corporate card statement exactly? Card networks apply their own exchange rate plus a foreign transaction fee (often 1–3%), so they will differ from the mid-market rate an API returns. For card-fed expenses, treat the amount the card actually charged as the source of truth; use the API rate only for an informational mid-market comparison.

How do I handle reimbursements so employees don't lose money on FX? Reimburse in the same currency the employee spent whenever possible, so they bear no conversion loss. If you must reimburse in their local payroll currency, convert at the transaction-date mid-market rate and disclose the rate used on the reimbursement statement.

Can I convert thousands of historical receipts efficiently? Yes. Historical rates for a past date are fixed and cacheable. Group receipts by date and currency pair, fetch each unique rate once, and store it. This keeps you fast and well within API rate limits even when importing large batches.

Do I need real-time rates for expense management? Usually not for the core conversion — historical (daily close) rates are the correct, auditable basis. Real-time rates are useful for showing employees an instant estimate at the moment of capture, but the official record should use the settled transaction-date rate.

Get Started

Ready to add accurate, audit-ready multi-currency support to your expense tool? Get your free Finexly API key — no credit card required. Start with 1,000 free requests per month, fetch real-time and historical rates for 170+ currencies, and upgrade as your transaction volume grows. Your finance team — and your auditors — will thank you.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →

Zdieľať tento článok