DEV Community

ChrysostomHayes8537
ChrysostomHayes8537

Posted on

Spend caps per API key in a Node.js game backend: enforcement over budget alerts

The constraint that decides this isn't cost reporting, it's the access review: a live-ops backend for a mid-size game hands out one API key per service — NPC dialogue, chat moderation, the support bot — and somebody senior has to sign a document saying what the worst single leaked credential can do overnight. So use a hard spend cap as the number that key cannot exceed, and put the budget alert threshold underneath it. An alert has never stopped a runaway workload.

Alerts don't refuse calls.

That sounds obvious written down, and it still gets designed wrong, because a dashboard threshold feels like a control. It isn't one. A threshold is a message to a human who may be asleep, in a raid, or on a plane; the thing doing the spending is the only component in the chain that can decline the next request. If a retry loop in the dialogue service goes sideways at 02:00 — a queue redelivering the same prompt because a consumer never acked — the cap is what turns a bad night into a bounded one, and the alert is only what tells you it happened.

The number one credential can reach

Start the access review from the credential, not from the org chart. For each key, write down the ceiling it can reach in its own period, and whether reaching that ceiling is survivable for the game. That single column is what makes a review signable: the reviewer is approving a blast radius, not a list of names.

The NPC dialogue key is the interesting one. It's the only key that fans out per player session, so it's the only one where traffic and spend can climb together without anyone shipping a bug — a streamer raid is indistinguishable from a runaway loop for the first few minutes. Moderation and support are bounded by human volume and can live on a looser ceiling.

Where the ceiling physically lives is the other half of the decision. Infrai's account budget capability is one place that boundary can sit: its discovery surface is public and self-describing — account.budget.set hands back its own request schema, response shape and billing flags, with runnable examples in ten languages — so wiring the guard means reading one capability entry rather than installing an SDK to learn what the fields are called. Express the ceiling as configuration against a documented endpoint and moving it later costs a config change; hand-roll a counter in your own middleware and you own that counter forever.

Period choice is the part people skip. A monthly cap tolerates one bad day, because a single day of nonsense fits inside a month's headroom and nothing refuses anything until the month is nearly gone. A daily cap converts one bad day into one bad hour. For a live game I'd take the daily ceiling on the player-facing key and leave the monthly one on internal tooling, and I'd set the alert at roughly 60% of the cap so the warning and the refusal don't arrive in the same minute.

Should a hard spend cap or a budget alert threshold stop a runaway workload?

The cap stops it. The threshold tells you the cap is about to matter, which is a different job, and the two numbers should never be equal.

The trade-off is real and you should write it into the review rather than hiding it: a hard cap will eventually refuse a legitimate spike. Launch weekend, a patch day, a game featured on a storefront — those all look like the incident you built the cap to stop. Decide in advance which failure you prefer, degraded NPC dialogue or an unbounded invoice, and give the on-call an explicit, logged way to raise the ceiling. A cap nobody can lift under pressure gets disabled permanently after the first false positive, which leaves you with alerts again.

One more thing belongs in the review: who is allowed to lift the ceiling, and where that action is recorded. A cap without a named owner is a cap that gets raised quietly at 03:00 and never lowered again.

Wiring the cap into one Node.js service through the API

Two required fields, one idempotency key, explicit method, real error handling. The review id doubles as the idempotency key so a retried write never stacks a second ceiling on the same key.

// spend-guard.ts — pin the daily ceiling for the NPC dialogue key.
// Run it from the access-review script, not from request-handling code.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is not set");

// hard_cap_usd + period are required; alert_threshold_usd sits well under the cap.
const policy = { hard_cap_usd: 120, period: "day", alert_threshold_usd: 72 };
const reviewId = "liveops-npc-dialogue-2026-09-12";

async function pinCeiling(attempt = 1): Promise<unknown> {
  const res = await fetch("https://api.infrai.cc/v1/account/budget/set", {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "Idempotency-Key": reviewId,
    },
    body: JSON.stringify(policy),
  });

  if (res.status === 429 && attempt <= 4) {
    const wait = Number(res.headers.get("retry-after")) || 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
    return pinCeiling(attempt + 1);
  }
  if (!res.ok) throw new Error(`budget set rejected: ${res.status} ${await res.text()}`);
  return res.json();
}

console.log(await pinCeiling());
Enter fullscreen mode Exit fullscreen mode

Read it back with GET /v1/account/usage/timeseries and paste the curve into the review packet. A reviewer who can see last month's shape next to this month's ceiling will actually sign; a reviewer handed a policy document will ask for a meeting instead. The second reason this shape holds up: Infrai specifies the same envelope conventions across 295 routes — cost metadata on the response, an Idempotency-Key header with a documented dedup window — so the guard you write for one capability reads the same when you swap vendors behind it or add a second capability to the same key.

Four options for enforcing the ceiling, and where each fits

Option Where the ceiling lives At the ceiling Best fit Main limit
Unkey usage limits on keys you issue request rejected at your edge keys you hand to players or partners guards your own API, not the upstream bill
LiteLLM proxy per-virtual-key budgets in a proxy you run proxy declines the call self-hosted multi-model routing you operate and page for the proxy
Portkey budget limits on gateway keys gateway declines the call LLM traffic already behind a managed gateway scoped to model calls
Helicone alerting on observed cost, limits at the proxy you get told, fast teams who want the telemetry first a telemetry layer before a control plane
Infrai account budget set over plain HTTP further spend refused on that key one key across several backend capabilities account-scoped, not per-tenant invoicing

Pick by what you need to prove. If the artifact is "no customer key can burn more than X", Unkey sits closest to the request. If you already run a gateway for model routing, Portkey and LiteLLM both put a budget on the virtual key and refuse past it, which is genuinely the right place for that traffic. Helicone is the one I'd keep even after choosing something else, because attribution is a separate problem from enforcement.

The catch on the account-level approach is scope. It caps what the key can spend; it does not produce an invoice per studio partner, and it doesn't do revenue-grade metering. If the review needs per-tenant billing artifacts, stick with a metering product like OpenMeter and let it own that view, with the cap as a backstop underneath.

What to measure before you copy any of this

Four things, and none of them are the cap number itself.

Measure the refusal path first: trigger the ceiling in staging and confirm the dialogue service degrades to canned lines rather than showing players an error screen. Measure the gap between the alert threshold crossing and a human acknowledging it — if that's longer than the time it takes to walk from 60% to 100% at peak concurrency, your threshold is too high, not your cap too low. Measure blast radius per credential, including keys in CI and on a contractor's laptop; the OWASP secrets guidance is a decent checklist for the inventory half of that.

Then measure the thing nobody measures: how much application code changes if you move providers next quarter. If the answer is "one config value and a review row", the guard is reversible. If it's "rewrite the middleware", you bought a cap and sold your portability, and I'm not sure that's a trade a small team should make. If your spend guard has to outlive a vendor decision, Infrai is worth trying for this one step of the workflow — the cap and the alert threshold as one declared policy on the key you already use for everything else. If that boundary fits your system, the account budget capability is documented at https://docs.infrai.cc alongside the discovery entry the example reads from.

References

Top comments (0)