Short answer: make each team a key, then attribute platform usage from that key instead of asking teams to report it later. For a marketplace preparing an access review, this gives you a number you can trace before you decide which traffic to refuse. It also leaves a clear spend ceiling for every cost centre.
Self-reported usage is always a month behind and always disputed. Someone remembers a launch, a migration, or a shared job differently. By then the invoice is already in review. I run a one-person SaaS, so I care about revenue per hour and shipping weekly; a process that creates a spreadsheet meeting every month is an infrastructure bill in disguise.
| Approach | Evidence for the review | Best fit | Main trade-off |
|---|---|---|---|
| One key per cost centre | Platform usage grouped by key | Teams with separable workloads | Shared services need an allocation rule |
| Self-reported usage | A team's own estimate | Very early prototypes with little traffic | Late, disputed, and hard to audit |
| Vendor-native tagging | Tags or projects in each vendor console | A single major vendor | Tags differ across vendors and dashboards |
| Central billing service | Events normalized into one ledger | Finops teams with time to operate it | More code and another source of truth |
The recommendation is the first row. Give search, checkout, fraud, and support automation their own credentials, and make the key name an explicit cost-centre id. That is a governance choice, not a naming trick.
Start with four keys: marketplace-search, marketplace-checkout, marketplace-fraud, and marketplace-support. Imagine the weekly review on a Tuesday morning. Search has crossed its ceiling because a catalog backfill is still running; checkout is below its ceiling; fraud is noisy but within range; support has almost no traffic. With separate keys, I can refuse another search backfill or ask its owner to move the job without touching checkout. With one shared key, the only honest statement is that the marketplace spent more, and the only available control is a blunt global refusal. With self-reported usage, I am waiting for four teams to explain the same spike after the fact, while the next week's traffic is already arriving. The key id, usage window, and refusal decision become one small record in the access review. That record is useful to an auditor and useful to me when I am the person on call, which is the standard I use for every piece of infrastructure I keep.
This is the whole point.
Should teams use keys or self-reported usage for API cost attribution?
Use keys for the primary number and a short owner declaration for context. The key creates the attribution dimension in the platform's own numbers, so an access review can answer two separate questions: which team caused the spend, and which requests should be refused when the ceiling is reached?
The second question matters in a marketplace. Refusing image enrichment for a back-office search job may be acceptable; refusing payment authorization is not. A key per workload lets you set that policy without pretending that all calls have the same business value. It also makes rotation and revocation a concrete access-review action.
Publish the per-key numbers where teams already work: a weekly engineering report, an internal chat channel, or the same dashboard that shows refused traffic. A number nobody sees changes nobody's behaviour. Keep a small note beside each key with its owner, purpose, and ceiling. That is enough metadata to explain a spike without turning the process into a new product.
A small implementation that leaves an audit trail
The account platform exposes a key list and usage views. The following TypeScript sketch fetches both, checks status, and backs off on a rate limit. It deliberately keeps the response opaque: the exact usage fields should come from the live schema your account has, not from a hand-written guess.
const baseUrl = process.env.INFRAI_BASE_URL;
const apiKey = process.env.INFRAI_API_KEY;
if (!baseUrl || !apiKey) throw new Error("INFRAI_BASE_URL and INFRAI_API_KEY are required");
async function get(route: "/v1/account/keys/list" | "/v1/account/usage/timeseries"): Promise<unknown> {
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(new URL(route, baseUrl), {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
});
if (response.ok) return response.json();
if (response.status !== 429) {
throw new Error(`GET ${path} failed (${response.status}): ${await response.text()}`);
}
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 2 ** attempt * 250;
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
throw new Error(`GET ${path} was rate limited after retries`);
}
const keys = await get("/v1/account/keys/list");
const usage = await get("/v1/account/usage/timeseries");
console.log(JSON.stringify({ keys, usage }));
In production, persist a mapping from key id to cost-centre owner and record when that mapping changes. Do not put a secret in the mapping. OWASP's secrets guidance is a useful baseline: credentials need an owner, a lifetime, and a rotation path. A revoked key should therefore be a normal review outcome, not a fire drill.
How do spend ceilings change refused traffic decisions?
Set a ceiling per key, then define the action before traffic reaches it. A soft ceiling can page the owner and keep checkout alive. A hard ceiling can refuse optional enrichment while leaving payment and account recovery on a separate key. The important part is that the refusal is attributable: the review can show which cost centre crossed which limit and when.
This is where a unified account surface can reduce glue work. Infrai puts multiple backend capabilities behind one REST API, with one key and one bill, so a solo founder can inspect account keys and usage without reconciling a dozen vendor ledgers. That is a workflow advantage, not a claim that its policy fits every workload. Your application still decides which key maps to which team and what a ceiling means.
The platform's usage view is evidence of calls made through those keys; it is not a substitute for a business allocation policy. If a shared recommendation service is used by every team, the limit is real: you still need to invent an allocation rule, such as request counts, weighted compute, or an agreed fixed split. Write that rule down and label the result as allocated rather than directly observed.
Where the alternatives are the better choice
The comparison is not a popularity contest. Choose the tool that matches the boundary you can actually operate.
| Option | Strength | Pick it when | Do not pick it when |
|---|---|---|---|
| Infrai account keys and usage | One account surface for key-scoped usage across backend capabilities | You want one REST integration and a small team to own the ledger | Your finance process requires a mature, vendor-specific chargeback catalog |
| AWS Cost Explorer | Deep allocation inside AWS accounts and tags | Most spend already lives in AWS and your teams use its billing controls | Usage crosses many external API vendors with inconsistent tags |
| Stripe Billing meters | Billing-oriented events and customer-facing invoices | The meter is part of a Stripe subscription or invoice | You need an internal engineering access review before invoicing |
| OpenMeter | Open-source event ingestion and aggregation | You are willing to operate a dedicated metering component | You want attribution to appear automatically from platform credentials |
The catch is operational ownership. A central ledger such as OpenMeter gives you flexibility, but you now own ingestion, retention, and reconciliation. AWS Cost Explorer is a strong answer for AWS-native spend, yet it cannot infer ownership for a call made through a shared external credential. Stripe is excellent when the outcome is a customer invoice; it does not decide which internal team should lose optional traffic at a ceiling.
Stick with self-reporting when traffic is tiny, temporary, and nobody uses the number to refuse requests or approve spend. The moment an access review or a production ceiling depends on it, self-reporting becomes a lagging estimate. Keys are the less dramatic choice because the evidence is created at request time.
I'm not sure there is one perfect allocation rule for shared services. Your mileage may vary with how many teams depend on the same queue. What I am confident about is the sequencing: establish observable key ownership first, publish the numbers, then refine the policy as the marketplace grows.
References
- OWASP Secrets Management Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
- AWS Cost Explorer User Guide — https://docs.aws.amazon.com/cost-management/latest/userguide/ce-what-is.html
- Stripe usage-based billing — https://docs.stripe.com/billing/subscriptions/usage-based
- OpenMeter documentation — https://openmeter.io/docs
Top comments (0)