Why every amount in our API is an integer
Prices for AI models are tiny and volumes are large. Floating-point numbers do not survive that combination. Here is how micro-dollars work and how to use them in your code.
Every amount in the Tovano API is an integer number of micro-dollars, in a field whose name ends in _micro. One US dollar is 1000000. There are no floats anywhere: not in the API, not in the database, not in the invoice. This post explains why, and how to handle these numbers on your side.
Small prices, large volumes
A text model may cost $1.00 per million input tokens. A short prompt of 42 tokens therefore costs $0.000042. A busy product sends millions of such requests a day.
Floating-point numbers cannot represent most decimal fractions exactly. 0.1 + 0.2 is 0.30000000000000004 in almost every programming language. One such error is invisible. A billion of them, added up over a month, become a visible difference between your own records and ours. In billing, a difference you cannot explain is a support ticket, and often a lost customer.
Integers do not drift
With micro-dollars, the 42-token request costs 42. Four images at $0.020 cost 80000. Adding integers is exact, so the sum of a month of charges is exactly the number on the invoice. Our ledger stores every row as an integer and the balance is the integer sum of the rows.
Micro-dollars are fine-grained enough for every unit we sell. The smallest real charge is a handful of tokens, which still comes to at least one whole micro-dollar after rounding up.
The fields
| Field | Example | Means |
|---|---|---|
price_micro | 20000 | $0.020 per unit |
charged_micro | 80000 | $0.080 for this task |
balance_micro | 84120000 | $84.12 balance |
amount_micro | -80000 | a ledger row that took $0.080 off |
Handling micro-dollars in your code
Keep the integer for as long as you can, and turn it into text only for display.
// Sum charges as integers.
const total = tasks.reduce((sum, t) => sum + t.charged_micro, 0);
// Format only at the edge.
const usd = (micro: number) =>
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 6 }).format(
micro / 1_000_000,
);
usd(total); // "$0.08"
The SDK ships the same helper as formatUsd. In languages with arbitrary-precision integers, such as Python, nothing special is needed. In JavaScript, a regular number holds integers exactly up to about nine quadrillion micro-dollars, which is nine billion dollars, so plain numbers are safe for any single balance or invoice.
If you store our numbers in a database, use an integer column (BIGINT in most databases). Do not convert to a decimal dollar amount on the way in.
Re-billing your own customers
Many of our customers resell AI features and add their own margin. Do that in integers too: multiply, then divide once, and round in one place.
// 30% margin, rounded up to the next micro-dollar.
const resale = (micro: number) => Math.ceil((micro * 130) / 100);
When you convert to cents for your own invoice, round once at the end of the period, not per request. Rounding a million requests to the cent one by one creates exactly the drift that integers were supposed to prevent.