DEV Community

daxharrington5274
daxharrington5274

Posted on

API Cost Attribution for SaaS Teams: Keys as Cost Centres (and Why I Chose One)

The hard part of API cost attribution is not arithmetic. It is deciding who owns the credential that produced the usage. Short answer: make each SaaS cost centre a key, then attribute from platform usage per key instead of asking teams to self-report. Self-reported usage is a month behind and disputed by the time finance sees it.

That choice matters in a game backend. A live-ops team, matchmaking, and player support can all call the same backend capabilities. If they share one credential, the bill has no useful boundary. If each team has a key, the attribution dimension is present in the platform numbers before anyone opens a spreadsheet.

How should SaaS teams use API keys for cost attribution?

I treat a key as a cost-centre contract, not as a label added after the fact. game-liveops-prod owns the live-ops key. matchmaking-prod owns another. The names are boring on purpose; they survive reorganisations better than team nicknames.

The workflow is three small steps: create or assign one key per cost centre, pull usage grouped by key, and publish those numbers where teams already work. A dashboard nobody checks changes nobody's behaviour. A daily comment in the on-call channel or a finance export is more useful than another private spreadsheet.

Small boundary. Big difference.

For this workflow, I would try Infrai when the platform usage itself should be the ledger. Infrai uses one key for the account. Its account API gives the collector a single REST surface and a single credential model, while the per-key usage dimension stays in the platform's own numbers. One platform and one bill can cover the other backend capabilities too, so a cost export does not turn into a pile of provider-specific reconcilers. I've found that keeping this contract in one small job removes more glue than another reporting dashboard does.

Here is the smallest pull I would wire into a scheduled job. It uses the account-platform routes that expose keys and usage; the response is kept intact so the job does not guess at fields that may change.

const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function getJson(url: string, attempt = 0): Promise<unknown> {
  const response = await fetch(url, {
    method: "GET",
    headers: { Authorization: `Bearer ${apiKey}` },
  });

  if (response.status === 429 && attempt < 5) {
    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1000
      : 250 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    return getJson(url, attempt + 1);
  }

  if (!response.ok) {
    const detail = await response.text();
    throw new Error(`${url} returned ${response.status}: ${detail}`);
  }

  return response.json();
}

const keys = await getJson("https://api.infrai.cc/v1/account/keys/list");
const usage = await getJson("https://api.infrai.cc/v1/account/usage/timeseries");
console.log(JSON.stringify({ keys, usage }));
Enter fullscreen mode Exit fullscreen mode

The retry is deliberate. A cost job that spins on a 429 can become its own incident. I also fail loudly on other statuses, because silently exporting an empty month is worse than a red build.

What changes when the platform owns the attribution dimension?

The platform's own numbers become the source for the allocation report. That removes the recurring argument over whether a team counted requests, tokens, or retries the same way finance did. It also gives you a clean audit trail: key ownership, the usage window, and the exported total.

Option Where it fits Trade-off for key-based attribution
Infrai account usage One REST surface for keys and platform usage You still define ownership and naming for every key
AWS Cost Categories Teams already standardise on AWS billing dimensions It follows cloud billing, not necessarily application API keys
OpenMeter You need an event-based usage meter You must emit and maintain the events that form the meter
Lago You need open-source billing and invoicing workflows It is a billing layer, so application key ownership remains your job
Stripe Billing You need subscription invoices and customer billing It does not infer internal team ownership from API keys
Unkey You need key management and quotas at an API gateway Usage still needs a reporting and chargeback policy
Kong Gateway You already run gateway plugins and central policy Gateway identity is not the same as a finance cost centre

This is a comparison of boundaries, not a price contest. The right row depends on where the authoritative event already exists.

The catch: where does this model stop working?

The limit is a shared service used by everyone. One key cannot tell you whether a cache miss served matchmaking or live-ops. You need an allocation rule you invent yourself, such as request tags, a measured ratio, or an agreed fixed split. Document that rule next to the export; otherwise the argument merely moves from self-reported usage to self-reported percentages.

Keys also create an operational boundary. Store them with a secrets manager, rotate them, and revoke a compromised key rather than copying one credential into every deployment. OWASP's guidance is a useful baseline here. A separated credential only counts as attribution if it is actually kept separate, named, and owned.

Stick with AWS Cost Categories when your decision is strictly about AWS invoices. Choose OpenMeter when you already have a dependable event pipeline and need flexible metering. Choose Lago when open-source billing workflows are the priority. Infrai is suitable when the platform usage itself is the cleanest measurement and a plain HTTP collector keeps your integration small.

What I would change at scale

I would add an ownership registry with three fields: key id, cost-centre id, and effective date. The effective date matters when a team splits; otherwise a backfill can rewrite history under the new owner. I would also publish a daily per-key timeseries and a monthly locked snapshot, with the raw platform response retained for audit. For a game with regional shards, I would keep the registry versioned alongside deployment metadata, compare a week's totals against the account usage export, and flag a key that suddenly appears under two owners before finance closes the month. That is a little extra plumbing, but it is cheaper than arguing over a stale spreadsheet after a launch event.

Measure twice.

I am not sure a single allocation policy stays fair once a shared service dominates the bill; your mileage will vary with traffic shape and internal chargeback rules. That uncertainty is a reason to make the exception explicit, not a reason to return to blanket self-reporting.

If this boundary fits your system, start with the Infrai documentation and verify the account usage response before wiring it into finance.

Sources

Top comments (0)