Real-Time Exchange Rate API for SaaS Billing

Show plan prices in a customer’s currency and save the rate behind each displayed or reported amount. Use the payment processor’s own rate for charges and settlement.

Show plan prices in a customer’s currency

Fetch the small set of currencies you display, cache the response for a short interval, and render the result with Intl.NumberFormat. Quantize the displayed amount for the currency, but keep the API rate at full precision in your records. That matters for zero-decimal currencies such as JPY, KRW, and VND, where a two-decimal money formatter produces an odd-looking price.

Actively traded currencies can update live, about every 60 seconds on trading days. Other coverage uses ECB/FRED reference data. Read source, data_updated_at, and market_session with the rate instead of guessing whether a displayed conversion is current or carried from a closed market.

Make billing analytics explainable

If a revenue dashboard converts invoices into a reporting currency, persist the rate, timestamp, source, and session used for that calculation. Re-running an old report against today’s rate changes the story; storing the original observation lets finance and support reproduce the number a customer saw.

For backfills or month-end reporting, use the historical endpoints rather than applying the latest rate to old invoices. A one-date response marks a carried weekend or holiday value with is_forward_filled; range results contain published business-day observations only.

A display conversion is not payment settlement

This API can power a reference-price display, reporting normalization, or an internal estimate. It does not set, control, or guarantee the exchange rate used by Stripe or any payment processor. Your processor’s settlement rate, fees, timing, and presentment-currency rules determine what is actually charged or paid out.

Treat these rates as indicative, not settlement instructions. Keep the processor’s payment record as the source for completed charges and use the FX observation only to explain your own display or analytics calculation.

Server-side SaaS price display

Keep the API key on your server. This example records the rate and its freshness fields alongside the value your application calculated.

TypeScript · server-side plan-price display
type LatestRates = {
  rates: Record<string, number>;
  source: 'live' | 'ecb_daily' | 'fred_daily';
  sources: Record<string, 'live' | 'ecb_daily' | 'fred_daily'>;
  data_updated_at: string;
  market_session: 'open' | 'weekend' | 'interbank_closed';
};

const response = await fetch(
  'https://api.exchangerate.dev/v1/latest/USD?symbols=EUR,JPY',
  { headers: { Authorization: `Bearer ${process.env.EXR_API_KEY}` }, next: { revalidate: 60 } },
);
if (!response.ok) throw new Error('Could not load FX rates');

const fx = (await response.json()) as LatestRates;
for (const currency of ['EUR', 'JPY'] as const) {
  if (!Number.isFinite(fx.rates[currency]) || !fx.sources[currency]) {
    throw new Error(`Missing ${currency} FX data`);
  }
}

const displays = [
  { currency: 'EUR', amount: Math.round(29 * fx.rates.EUR * 100) / 100 },
  { currency: 'JPY', amount: Math.round(29 * fx.rates.JPY) }, // zero-decimal currency
] as const;

// Implement this adapter with your server-side database client.
await savePlanPriceDisplays(displays.map((display) => ({
  plan: 'pro', displayed_currency: display.currency, displayed_amount: display.amount,
  base_amount_usd: 29, rate: fx.rates[display.currency], source: fx.sources[display.currency],
  data_updated_at: fx.data_updated_at, market_session: fx.market_session,
})));

Ready to test the API?

Start with the quickstart, check the response fields, and see pricing when you need account quota.

Free is for evaluation and internal, non-commercial use. Basic and Pro include commercial use.

Create a free evaluation key →