Build a currency converter that gets the money right

A command-line converter backed by live exchange rates, where every amount is an integer of cents (or yen, or fils) and the only rounding happens once, on purpose.

Open a browser console and type 0.1 + 0.2. You get 0.30000000000000004. Most of the time that last digit is harmless. In a currency converter it is the difference between a total that reconciles and one that is a cent out, and a cent out across ten thousand invoices is a support queue.

This project is a converter you run from the terminal: node convert.js 19.99 USD JPY. It fetches a live rate, caches it for ten minutes, does the arithmetic without ever touching a floating-point number, and prints a correctly formatted result. Then you will wrap the same code in a small web endpoint. The API calls are the easy part. The interesting work is in what you do with the numbers once they arrive.

Picking the right API

The catalog has two APIs that look interchangeable, and the difference between them is exactly the subject of this lesson.

Currency Converter (currencyconverter) takes value, from and to and does the multiplication for you. It returns convertedValue, rate, changeDirection, date and lastUpdated; the 24-hour change, high and low are premium.

Try it liveCurrency Converter2 credits
GEThttps://api.apiverve.com/v1/currencyconverter
curl "https://api.apiverve.com/v1/currencyconverter?value=19.99&from=USD&to=JPY" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

convertedValue is a raw float with many decimal places. Rounding it is your job.

When this lesson was written, that call returned a convertedValue of 3140.6347794587996. That is not a price. Nobody pays 0.6347794587996 of a yen, and the converter is doing the arithmetic with more precision than the rate it shows you, so multiplying value by rate yourself gives a slightly different float. Fine for a quick display, but you would still have to round it, and you would be rounding a number whose history you cannot see.

Exchange Rate (exchangerate) takes currency1 and currency2 and returns just the rate. The detail that makes it the better fit here: exchangeRate is a string, like "0.891915". A string can be parsed straight into an exact integer representation without ever becoming a float. The inverse rate is premium; you will not need it.

Try it liveExchange Rate2 credits
GEThttps://api.apiverve.com/v1/exchangerate
curl "https://api.apiverve.com/v1/exchangerate?currency1=USD&currency2=EUR" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

exchangeRate arrives as a string with six decimal places. Parse it as text, not with parseFloat.

Both are 2 credits per call in the catalog, and both accept a premium date for historical rates. This project uses exchangerate and does its own maths.

The third API, Currency Symbols (currencysymbols), maps a currency code to the countries that use it. On plans that include them, it also returns currency_symbol and currency_mnr_unts, the number of minor units, which is exactly the "how many decimal places does this currency have" fact you need.

Try it liveCurrency Symbols1 credit
GEThttps://api.apiverve.com/v1/currencysymbols
curl "https://api.apiverve.com/v1/currencysymbols?currency=JPY" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

countriesFound lists who uses the yen. The symbol and minor units are premium fields.

Because those two fields are premium, the Node code gets the same facts from Intl.NumberFormat, which ships with Node and knows every ISO 4217 currency. The Python version, which has no equivalent in the standard library, is where Currency Symbols earns its place.

The money rules

Three rules, and the code follows directly from them.

Store and compute in minor units. A minor unit is the smallest denomination: cents for USD, yen for JPY (which has no subunit), fils for KWD (which has three decimal places). $19.99 is the integer 1999. Integers add, compare and multiply exactly. In JavaScript you use BigInt so even very large amounts never lose precision; in Python, int or Decimal.

Never parse money through a float. parseFloat("19.99") is already approximately 19.99. Parse the string's digits directly into an integer, and parse the rate the same way: "0.891915" is 891915 over 10^6.

Round once, at the end, with a stated rule. The result of multiplying two exact numbers is exact; only the final conversion back to minor units needs rounding. This project rounds half-up. Some accounting contexts require half-to-even (banker's rounding), which avoids a systematic upward bias over many transactions. Whichever you pick, pick it deliberately and write it down.

Check yourself

A user converts 19.99 USD. Which first step keeps the calculation exact?

Answer the check above to continue.

Run every example on your own key. Free plan, no card.

Scaling up?

Volume pricing, custom SLAs, and dedicated support for high-traffic teams.

Contact sales