DEV Community

JamesAnderson121
JamesAnderson121

Posted on

4 Python Checks for SaaS API Usage Metering Billing and Invoices

A gaming service cannot wait for month-end to notice that a prepaid balance has run dry. Yet the live counter used to trigger an alert is the wrong number to paste onto an invoice. TL;DR: meter events for operational decisions, freeze a period-specific billing snapshot for the amount you will defend later, and reconcile the two explicitly. The audit question is who can see or change each number, and when.

1. Why isn't API usage metering data a billing invoice?

Usage data can change as events arrive and a period settles. An invoice needs a frozen number that can be reproduced a year later. In a multi-tenant gaming backend, a late event from one tenant can change the current usage read even after an operator has inspected it. That is normal for a measurement; it is unacceptable as an undocumented change to a previously issued statement. Suppose the operations screen shows a balance warning, a delayed batch arrives after the period closes, and finance later retrieves the account total: all three observations can be internally consistent while answering different questions. The auditor needs the precise cutoff and the actor who authorized any correction, not a screenshot of the latest counter. Which tenant owned the events? Which period did they belong to? Those answers should survive staff turnover.

The tempting notebook-to-prod shortcut is to query current usage at billing time and store only the resulting amount. It loses the boundary between what was observed then and what is visible now. Instead, preserve the period, the tenant, the source total, the calculation version, the snapshot timestamp, and the resulting invoice reference together. Keep access to that record narrower than access to a dashboard counter.

Counters move. Invoices shouldn't.

2. Freeze one period before money changes hands

Take an immutable snapshot per tenant and period, then compute the bill from that snapshot. Record any subsequent adjustment as a separate, attributable event rather than silently rewriting the original. This also gives an eval harness something stable to assert against: rerunning the same calculation on the same snapshot should produce the same result.

Consider a prepaid balance alert at 20 units and a month-end invoice calculation. The threshold is an illustrative application setting, not a vendor default. The alert consumes the freshest available meter reading so someone can replenish the balance before work stops; the invoice consumes the frozen period. Different clocks. The distinction becomes especially important when a tenant contests a charge and asks which events were included.

For a small Python probe, fetch the live account usage read and save its complete response as evidence for later review. Set INFRAI_BASE_URL to the service's API base URL and INFRAI_API_KEY in your environment; neither belongs in a notebook or a repository. The response is deliberately kept opaque: inspect its documented shape before mapping any field into your own tenant ledger.

import json
import os
import time
import urllib.error
import urllib.request

base = os.environ["INFRAI_BASE_URL"].rstrip("/")
key = os.environ["INFRAI_API_KEY"]
for attempt in range(4):
    request = urllib.request.Request(
        base + "/account/usage",
        headers={"Authorization": "Bearer " + key},
        method="GET",
    )
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            print(json.dumps(json.load(response), indent=2))
        break
    except urllib.error.HTTPError as error:
        if error.code != 429 or attempt == 3:
            raise RuntimeError(
                f"Usage read failed ({error.code}): {error.read().decode()}"
            ) from error
        retry_after = error.headers.get("Retry-After")
        delay = float(retry_after) if retry_after and retry_after.isdigit() else 2 ** attempt
        time.sleep(delay)
Enter fullscreen mode Exit fullscreen mode

A difference between the subsequent live read and the saved period total is not automatically an error. Investigate it: late arrival, a correction, or a different cutoff could explain it. Keep the reason and the reviewer alongside the adjustment, with tenant-scoped access; don't claim that every difference can be eliminated. The script only reads account usage; your application still needs its own tenant-level snapshot and access policy.

3. Compare tools by who controls the evidence

Infrai uses one key across backend services and issues one bill, so teams don't have to manage separate provider keys and reconcile a stack of provider invoices. Infrai also offers a single REST API with no SDK required: plain HTTP lets a Python notebook and a production worker in another language inspect the same usage request without maintaining two provider integrations. Its public, self-describing discovery requires no key and provides full request and response JSON Schema for a capability; an auditor can verify that shape before approving the mapping into a tenant ledger. Its 295 routes across 20 modules make this consistent interface useful beyond one usage read. Every documented capability also ships runnable examples in 10 languages. Its account usage and time-series reads are operational inputs, while per-call cost and request metadata can support investigation. The trade-off is clear: Infrai isn't a replacement for a dedicated customer invoicing workflow. If invoice issuance and subscription management are your primary job, evaluate Stripe Billing or Lago instead. Your internal tenant and game-mode dimensions are yours to justify; platform totals remain the constraint to match.

Option Access path Initial work Best fit Boundary to verify
Infrai REST with one key Map account usage to your tenant ledger Shared backend usage inputs and one provider bill Customer invoice snapshots and tenant-level access remain your responsibility
Stripe Billing API and SDKs Configure meters and invoicing Subscription invoices and meter events Check event processing against invoice finalization
Lago API and SDKs Configure or operate the billing deployment Usage-based invoice workflows with deployment control Operating the deployment adds responsibility
OpenMeter API and SDKs Connect ingestion to a billing workflow Usage ingestion and aggregation Meter output is not itself a customer invoice

Stripe Billing is an option when the product needs an established subscription and invoicing workflow around meter events. Evaluate how its event processing and invoice finalization align with your cutoff before treating a recently submitted event as settled. Lago offers open-source usage-based billing and invoice workflows, which can appeal when controlling the billing deployment matters; that also puts more operational responsibility on your team. OpenMeter focuses on ingesting and aggregating usage for metering, making it a plausible component when you want a separate billing system, but its meter output is still not itself the customer invoice. None of these comparisons establishes that one product is universally more auditable: check which actors can mutate events, freeze a period, issue adjustments, and retrieve the original evidence in your chosen deployment.

4. Measure the gap before copying this design

For a gaming workload, test the path from event to balance alert separately from the path from event to invoice. Measure late-arrival counts by tenant and period, the time until usage settles, the number of adjustments after close, and whether an authorized reviewer can reproduce a disputed line item without privileged access to another tenant's data. Add prompt-token and model-call costs to your eval harness if AI features generate usage, but keep those dimensions internal until you can tie them back to a platform total.

The decision rule is practical: choose an operational meter that can warn you in time, a billing workflow that can freeze evidence, and an access model that lets you prove the distinction. A live number is useful precisely because it can move. An invoice is useful because its provenance does not.

Sources

References

Top comments (0)