Skip to content
Back to Blog
July 24, 2026

Money in APIs: Getting Currency Right

Floating-point money is a bug waiting to happen. Integer cents, explicit currency codes, and careful rounding are non-negotiable for travel APIs handling 160+ currencies.

Money in APIs: Getting Currency Right
M

Open a JavaScript console and type `0.1 + 0.2`. You get `0.30000000000000004`. Now imagine that rounding error compounding across thousands of flight booking transactions per day. A fraction of a cent here, a fraction there. At scale, the numbers stop adding up and your accounting team starts asking uncomfortable questions.

This is not a theoretical problem. I have seen a travel integration where cumulative floating-point rounding errors added up to over a thousand dollars in discrepancies within a single quarter. The company spent weeks reconciling transactions that should have matched perfectly. The root cause was storing prices as floating-point numbers in the API layer.

Travel APIs handle 160+ currencies. At least seven of those are zero-decimal currencies where cents do not exist — the Japanese yen, the Korean won, and several others. Multi-currency flight search results can span five or more currencies in a single response. Getting currency wrong does not just produce incorrect displays. It produces incorrect charges.

Integer cents as the canonical representation

Illustration for this section

Every monetary amount in the Nowah API is represented as an integer in the smallest unit of the currency, paired with an explicit currency code.

A flight priced at $149.99 is:

{
 "amount": 14999,
 "currency": "USD"
}

A hotel in Tokyo priced at 15,000 yen is:

{
 "amount": 15000,
 "currency": "JPY"
}

No floating-point numbers. No ambiguity. No rounding errors. The integer representation eliminates an entire category of bugs at the API boundary.

IEEE 754 floating-point representation, which is what virtually every programming language uses for decimal numbers, cannot exactly represent most decimal fractions. $0.10 is actually stored as something like $0.1000000000000000055511151231257827021181583404541015625. For a single value, the difference is invisible. For arithmetic operations — adding prices, calculating taxes, computing totals — the errors accumulate.

Integer arithmetic is exact. `14999 + 300 = 15299` is always correct. There is no rounding, no precision loss, no platform-dependent behavior. The math just works.

The zero-decimal currency problem

Most developers think of money as dollars-and-cents, where 100 cents make a dollar. But that model does not apply universally.

The Japanese yen has no subdivision. 15,000 JPY is 15,000 yen, not 150.00 yen with an implied decimal. The Korean won, the Vietnamese dong, the Hungarian forint (for practical purposes), and several others work the same way.

If your API represents all amounts as "value in cents," then 15000 in USD means $150.00 but 15000 in JPY means 15,000 yen. The interpretation of the integer depends entirely on the currency code. Miss that distinction and you might display a 15,000 yen hotel room as 150.00 yen — off by a factor of 100.

We handle this by including the currency code with every amount and providing a client-side utility that knows how many decimal places each currency uses. The API never sends a pre-formatted price string like "$149.99" because formatting is locale-dependent. An American developer wants "$149.99." A German developer wants "149,99 $." A Japanese developer wants 15,000 yen with no decimal point at all.

The API sends the raw integer and the currency code. The client formats it according to the user's locale. This separation keeps the API locale-agnostic and puts display logic where it belongs — in the client.

Multi-currency search results

Supporting diagram

A flight search from New York to Tokyo might return results priced in US dollars, Japanese yen, euros, and British pounds. Each airline prices in its own base currency, and the aggregated search results include all of them.

This creates a display challenge. How do you compare $850 to 128,000 yen to 780 euros? You need exchange rates, and exchange rates are themselves a minefield.

Our approach: we return prices in the provider's native currency and optionally include a converted amount in the user's preferred currency. The conversion uses a rate that we fetch and cache on a schedule, with the rate and its timestamp included in the response:

{
 "nativeAmount": {
 "amount": 12800000,
 "currency": "JPY"
 },
 "convertedAmount": {
 "amount": 85400,
 "currency": "USD",
 "exchangeRate": 0.006672,
 "rateTimestamp": "2026-03-15T12:00:00Z"
 }
}

The native amount is the source of truth. The converted amount is a convenience for comparison. We include the exchange rate and timestamp so developers can decide whether the conversion is fresh enough for their use case.

We explicitly do not use the converted amount for booking. When a traveler books a flight priced in yen, they pay in yen (or their payment method's currency, with the conversion handled by the payment processor). The API never charges a converted amount because the rate might have changed between search and booking. The booking always uses the native currency amount.

Rounding policies

When conversion is necessary, rounding becomes a question. Round half-up? Truncate? Banker's rounding?

For display purposes, we use round-half-up because it matches what most people expect. $149.994 rounds to $149.99. $149.995 rounds to $150.00. This is the rounding behavior taught in school and the one that creates the least surprise.

For accounting and settlement, we use banker's rounding (round half to even). In banker's rounding, $149.995 rounds to $150.00 but $150.005 also rounds to $150.00 (because 0 is even). This eliminates the systematic upward bias of round-half-up over large transaction volumes.

For developer-facing API responses, we avoid the rounding question entirely by returning integers. There is nothing to round when the amount is already in the smallest unit. Rounding only becomes relevant when converting between currencies or when the client needs to display the amount in a human-readable format.

Testing currency edge cases

We maintain a test suite specifically for currency handling. It covers scenarios that are easy to miss:

Zero-decimal currencies with large amounts (1,000,000 JPY is a reasonable hotel booking in Tokyo). Currencies where the smallest unit is not a cent (the Bahraini dinar subdivides into 1,000 fils). Amounts at the boundary of JavaScript's safe integer range (amounts over 9,007,199,254,740,991 cents, which matters for some currencies at high transaction volumes). Conversion between currencies with different decimal places (USD to JPY, where the result should have no decimal places).

We also test the display formatting path: ensuring that every currency code in ISO 4217 renders correctly with the right number of decimal places, the right symbol placement, and the right thousands separator for common locales.

Currency handling is one of those areas where the correct solution is boring and simple — integers with explicit currency codes — but the incorrect solutions are creative and numerous. Every travel API that uses floating-point for money eventually discovers the rounding bugs. The question is whether they discover it during testing or in production.

We chose to be boring about money representation so we could be interesting about everything else.


Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.

Share this article

Ready to Plan with Nowah?

Bring the idea. Nowah will help turn it into a trip.

Try Nowah