Skip to content
Quote conventions

Cross rates and base currency explained

How to read a currency pair, flip a rate, and compute a cross rate such as EUR/JPY from USD-based data – with the precision rules that keep the numbers right.

Last updated: · fxapi team

Most bugs in currency code are not network errors – they are direction errors. A rate read the wrong way around, an inverse taken from a rounded value, or a cross rate computed with the division flipped will produce numbers that look plausible and are wrong. This page explains how base currency, quote currency and cross rates fit together, so the arithmetic in your code matches what the data means.

A cross rate is an exchange rate between two currencies that is calculated through a third currency, typically the US dollar. If you know how many euros and how many yen one US dollar buys, dividing one by the other gives the EUR/JPY cross rate without needing a direct EUR/JPY quote.

Base currency and quote currency

A currency pair is written BASE/QUOTE. The rate tells you how many units of the quote currency one unit of the base currency costs.

EUR/USD = 1.0850
  └─ base: EUR (1 unit)
       └─ quote: USD (1.0850 units)
=> 1 EUR = 1.0850 USD

A foreign exchange rates API uses the same idea with explicit parameters. In fxapi, base_currency is the “1 unit” side and each entry in data is a quote currency:

{
  "meta": { "last_updated_at": "2026-10-08T09:31:00Z" },
  "data": {
    "USD": { "code": "USD", "value": 1.085 },
    "JPY": { "code": "JPY", "value": 163.2 }
  }
}

With base_currency=EUR, this (illustrative) response reads “1 EUR = 1.085 USD” and “1 EUR = 163.2 JPY”. The default base is USD.

Market quote conventions

In the interbank market, each major pair has a conventional direction. Dealers quote EUR/USD, not USD/EUR. The usual priority for which currency becomes the base is EUR, then GBP, AUD, NZD, USD, and then the other currencies.

Pair as quotedReads asNotes
EUR/USD1 EUR in USDMost traded pair
GBP/USD1 GBP in USDNicknamed “cable”
AUD/USD, NZD/USD1 AUD or NZD in USDCommonwealth currencies quoted as base
USD/JPY1 USD in JPYUSD is the base
USD/CHF, USD/CAD1 USD in CHF or CADUSD is the base
EUR/GBP, EUR/JPY1 EUR in GBP or JPYEuro crosses keep EUR as base

Conventions matter when you compare your numbers with a newspaper, a terminal or a bank statement. An API does not have to follow them: you choose the base. Just make sure your UI labels say which way the rate reads.

Inverse rates

To flip a pair, divide 1 by the rate:

EUR/USD = 1.0850
USD/EUR = 1 ÷ 1.0850 = 0.92165898…

Two rules prevent subtle errors:

  1. Invert the full-precision value. If you round 1.0850 to 1.09 first, the inverse becomes 0.9174 instead of 0.9217 – a 0.5% error.
  2. Mid-market inverses are exact; retail inverses are not. For a mid-market rate, 1 ÷ rate gives the mid in the other direction. For bid/ask quotes, the inverse of the bid is the ask of the flipped pair, so retail rates cannot be inverted one-for-one.

Computing cross rates from a USD base

Suppose you hold one response with base_currency=USD. Each value r[X] means “units of X per 1 USD”. The rate from currency A to currency B is:

rate(A → B) = r[B] ÷ r[A]

Worked example (illustrative values):

CodeUnits per 1 USD
EUR0.9200
GBP0.7700
JPY150.00
EUR → JPY = 150.00 ÷ 0.9200 = 163.0435   (1 EUR = 163.04 JPY)
GBP → EUR = 0.9200 ÷ 0.7700 = 1.1948     (1 GBP = 1.1948 EUR)
JPY → GBP = 0.7700 ÷ 150.00 = 0.0051333  (1 JPY = 0.00513 GBP)

A quick sanity check: the target currency’s value goes on top. If EUR→JPY comes out below 1, the division is upside down.

Triangulation

Calculating through a third currency is called triangulation. In the market, triangular arbitrage keeps direct crosses close to their triangulated value: if EUR/JPY drifted away from EUR/USD × USD/JPY, traders would buy the cheap side and sell the expensive one until the gap closed. That is why a cross computed from two mid-market rates is a sound reference – but it will not match a directly quoted cross to the last digit, because the direct quote has its own spread and timestamp.

Always triangulate from one snapshot. Mixing a EUR rate from 09:00 with a JPY rate from 15:00 produces a cross that never existed.

When to compute crosses and when to ask for a new base

SituationRecommended approach
A few pairs, rarelyCall the API with the base you need, e.g. base_currency=EUR
Many pairs, frequent displayCache one USD-based response and derive crosses locally
Metals or crypto as baseUse the API base directly (base_currency=XAU or BTC) to avoid tiny divisors
Accounting amountsRequest the exact base and date your policy requires, and store the response

fxapi accepts every one of its 190+ codes as base_currency on every plan, so the first option never costs extra. The second option saves requests: one cached call to /v1/latest contains enough information to derive tens of thousands of currency pairs. See caching exchange rates for cache lifetimes per plan.

Precision: keep digits until the end

Cross rates multiply rounding errors, so:

  • Store rates with full precision as returned (fxapi returns values like 0.8890791312).
  • Use decimal types for money: decimal.Decimal in Python, BigDecimal in Java, decimal in C#, or integer minor units in JavaScript.
  • Round once, at the end, to the target currency’s minor unit – 2 decimals for EUR, 0 for JPY, 3 for KWD. The /v1/currencies endpoint returns decimal_digits per currency, and ISO 4217 currency codes explains minor units.

The currency rounding and precision guide covers rounding modes and display formatting in depth.

Example: derive any cross from one fxapi call

// One request, any pair: fetch USD-based rates once, then derive crosses locally.
const res = await fetch("https://api.fxapi.com/v1/latest?base_currency=USD", {
  headers: { apikey: process.env.FXAPI_KEY },
});
const { data, meta } = await res.json();

const perUsd = (code) => (code === "USD" ? 1 : data[code].value);

export function crossRate(from, to) {
  return perUsd(to) / perUsd(from); // target on top
}

console.log(`1 EUR = ${crossRate("EUR", "JPY").toFixed(4)} JPY (as of ${meta.last_updated_at})`);
console.log(`1 GBP = ${crossRate("GBP", "CHF").toFixed(6)} CHF`);

If you would rather not compute anything, the currency converter API (/v1/convert, Basic and up) returns converted amounts directly for any base. For a broader introduction, read what is an exchange rate API, or see the US dollar page for USD-specific notes.

Key takeaways

  • The base currency is the “1 unit” side; the value is how many quote-currency units it costs.
  • Invert with 1 ÷ rate, always on full-precision values.
  • Cross rate from a USD base: rate(A → B) = r[B] ÷ r[A], from a single snapshot.
  • Round once, at the end, to the target currency’s minor unit.

Frequently asked questions

What is a cross rate?
A cross rate is the exchange rate between two currencies calculated from each currency’s rate against a third currency, usually the US dollar. EUR/JPY, for example, can be derived from USD/EUR and USD/JPY.
What is the base currency in an exchange rate?
The base currency is the one that counts as one unit. In EUR/USD = 1.0850, EUR is the base and USD is the quote currency: 1 euro costs 1.0850 US dollars.
How do I invert an exchange rate?
Divide 1 by the rate: if 1 EUR = 1.0850 USD, then 1 USD = 1 ÷ 1.0850 ≈ 0.921659 EUR. Invert the full-precision value, not a rounded one.
Do I need to calculate cross rates myself with fxapi?
No. Set base_currency to any of the 190+ supported codes and fxapi returns all rates relative to it. Computing crosses locally is still useful when you want to derive many pairs from one cached response.
Why does my calculated cross rate differ slightly from a quoted one?
Directly quoted crosses include their own spread and update at their own moment, while a computed cross uses two mid-market rates from the same snapshot. Small differences in the last decimals are normal.
Free plan · no credit card

Get your free exchange rate API key

300 requests a month, latest and historical rates, fluctuation and averages – free forever. Upgrade when you need faster updates or more requests.