Ten sub-cent usage rows: why cents fields never match debited_usd

Sume prints three roundings for the same money: credit_amount rounds up, billable_amount_usd_cents rounds half-up, debited_usd rounds once. Sum micros instead.

5 min readSume
All posts

Ten usage rows of $0.004 each give three different answers in cents, because Sume rounds the same money three ways: each row's credit_amount rounds up (1 cent per row, 10 in total), each row's billable_amount_usd_cents rounds half-up (0 cents per row, 0 in total), and the summary's debited_usd sums micros and rounds once (4 cents). Only the last is right; the exact figure is the sum of billable_amount_usd_micros, 40,000 micros.

The three rules are in the API's usage code, and the Usage page (read 2026-10-11) says compatibility fields can show rounded cent values as credits. The $0.004 rows are a hypothetical used to make the rounding visible, not a quoted price.

The three roundings side by side

Every ledger row stores an integer number of USD micros, where one cent is 10,000 micros and one dollar is 1,000,000. Cents exist only at the edge, and the edge has three different conventions that grew at different times.

For ten hypothetical rows of 4,000 micros each, the arithmetic is below. Per row, 4,000 / 10,000 is 0.4 cents. Ceiling turns 0.4 into 1; half-up turns it into 0; summing first gives 40,000 micros, which is 4.0 cents.

Ten hypothetical rows of 4,000 micros each, under each Sume rounding rule (arithmetic checked 2026-10-11)
FieldRulePer rowSum of 10 rows
credit_amountLegacy per-row ceiling1 cent10 cents
billable_amount_usd_centsPer-row half-up0 cents0 cents
billable_amount_usd_microsExact integer4,00040,000 micros
summary.debited_usdSum micros, round half-up oncen/a$0.04

Which one should a budget use?

Use micros. The summary's debited_usd_micros is documented as the amount the wallet deducted, and debited_usd is that figure turned into dollars with the only rounding in the response. If you need a row-level number, use billable_amount_usd_micros and divide by 1,000,000 yourself, at the end.

Do not add up credit_amount over many small rows. It is kept for compatibility and the API schema labels it a legacy rounded cent amount, so sub-cent rows are overstated (2.5 times in the 4,000-micro example above). The reverse trap exists too: summing billable_amount_usd_cents over sub-cent rows can read as zero. The related post on the credits field being rounded cents covers the balance side.

Where cents mismatches usually come from

A cost chart in your own app that sums credit_amount will drift above the Billing page whenever your volume is made of cheap rows: speech clips, short captions, thumbnails. A chart that sums billable_amount_usd_cents will drift below. Both are rounding, not a billing error, and neither means a job was charged twice.

When you reconcile, pick a window, sum billable_amount_usd_micros over captured rows only, and compare after one rounding. Reserved rows are holds and refunded rows are given back, so counting them double-counts. If your list is long, remember that limit caps only the rows listed, per usage limit caps rows, not the summary.

A reconciliation that cannot drift

This sums micros over the captured rows returned and prints dollars with one rounding. Scope it with a thread_id, run_id or job_id when you can, because then the summary does the same fold for you.

import os
import requests


def main():
    r = requests.get(
        "https://api.sume.com/v1/usage",
        params={"limit": 100},
        headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
        timeout=30,
    )
    r.raise_for_status()
    rows = r.json()["data"]["usage"]
    micros = sum(x["billable_amount_usd_micros"] for x in rows
                 if x["status"] == "captured")
    cents = (micros + 5_000) // 10_000
    print(f"{len(rows)} rows, captured {micros} micros = ${cents / 100:.2f}")


main()

Sources

Related posts

More in Pricing

All Pricing posts

Written by Sume