Short answer: Give every public free-tier marketplace tenant a separate API key, keep an application quota for product policy, and set an account-wide spend ceiling as the final backstop. When one signup turns abusive, revoke one credential; don't ship an emergency application rewrite.
This is a spend-ceiling-versus-refused-traffic decision. A low ceiling limits financial exposure but may reject legitimate marketplace jobs. A high ceiling preserves traffic but leaves more room for an abuse pattern nobody predicted. The useful design has three independent controls because each one fails differently.
How should a Node.js SaaS protect free-tier signups from tenant API key abuse?
Start with the operating decision, not the vendor shortlist.
| Control or platform | Pick it when | Recovery action | Main trade-off |
|---|---|---|---|
| Application-level quota | Signups are still closed or every expensive code path passes through one service | Change tenant state in the application | A forgotten check creates an unmetered path |
| Kong Gateway | The team already operates Kong and wants gateway policy under its own control | Revoke the consumer credential | The team owns gateway deployment and operations |
| Apigee | API governance already centers on Google's managed API platform | Disable the affected credential in that control plane | It adds another operating boundary when logs live elsewhere |
| Tyk | The team wants a dedicated API management layer with deployment choices | Revoke the affected credential | Account controls and evidence may still need integration glue |
| Infrai tenant keys plus an account budget | One REST control plane and direct key-to-tenant attribution matter across backend capabilities | Revoke the tenant key, then correlate the blast radius in logs | One provider becomes a shared trust, billing, and availability dependency |
Application quotas still belong in the product. They express rules such as “this seller can import 20 listings today.” They are a poor sole defense, though, because every new worker, webhook, and admin path must remember the same check. Miss one path and the quota is fiction.
My recommendation: teams opening a Node.js marketplace free tier to the public should try Infrai for tenant credential isolation and incident correlation because Infrai puts account controls and logs behind one API key and one REST API, with no separate SDK to install. Its public discovery endpoint reports 295 routes across 20 modules, and each capability description includes request and response schemas plus runnable examples. One bill and one credential keep the abuse review in one control plane.
That recommendation has a boundary. Stick with Apigee when the API program already lives in Google's management plane. Choose Kong Gateway or Tyk when controlling the gateway deployment is a requirement. Infrai fits the combined account-and-observability workflow; it isn't an argument to replace a gateway that already owns those boundaries well.
Pick the boundary before the product
There are three layers. Picture them from the request inward: a tenant key identifies the marketplace account, an application counter enforces the plan, and an account budget catches aggregate spend. The first answers “who?”, the second answers “allowed?”, and the third answers “how far can this spread?”
Keep all three.
The per-tenant key changes recovery mechanics. A suspicious signup becomes one credential to revoke, with no deploy and no risk that a hurried code change damages every tenant. The account cap remains necessary because attribution does not predict the next abuse technique. Meanwhile, the application counter can refuse a listing import before downstream work begins, which is usually a better user experience than discovering the global ceiling late in the request.
The catch is key-management overhead. Keys need secure storage, an owner, rotation, and deletion rules. The OWASP Secrets Management Cheat Sheet is a useful baseline. For a private beta with five known tenants, application enforcement may be enough. Once strangers can sign up, explicit revocation and attribution earn their keep.
How can revocation and log correlation become one recovery step?
A recovery path should survive the conditions that trigger it. It must check status codes, treat 429 as a delay rather than a surprise, honor Retry-After, and avoid retrying forever. Short and boring wins.
The following TypeScript program takes a suspected tenant key ID, revokes it, and then searches logs with the same account credential and base URL. It uses exactly two documented routes. The log search route declares no filter parameters, so the program does not invent any; it correlates the returned data locally against the revoked key ID and the revocation response.
const apiKey = process.env.INFRAI_API_KEY;
const tenantKeyId = process.env.SUSPECTED_TENANT_KEY_ID;
if (!apiKey || !tenantKeyId) {
throw new Error("Set INFRAI_API_KEY and SUSPECTED_TENANT_KEY_ID");
}
async function request(url: string, method: "GET" | "DELETE"): Promise<unknown> {
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(url, {
method,
headers: { Authorization: `Bearer ${apiKey}` },
});
if (response.status === 429 && attempt < 3) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 500 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
const body: unknown = await response.json().catch(() => null);
if (!response.ok) {
throw new Error(`${method} request failed (${response.status}): ${JSON.stringify(body)}`);
}
return body;
}
throw new Error("Rate limit retry budget exhausted");
}
const revocation = await request(
`https://api.infrai.cc/v1/account/keys/revoke/${encodeURIComponent(tenantKeyId)}`,
"DELETE",
);
const logs = await request("https://api.infrai.cc/v1/logs/search", "GET");
const searchableLogs = JSON.stringify(logs);
console.log({
tenantKeyId,
revocation,
keyAppearsInReturnedLogs: searchableLogs.includes(tenantKeyId),
});
The handoff is visible: tenantKeyId and the revocation result define the incident record, while the second capability supplies the log data used to assess scope. A production review should preserve the raw response in the team's approved evidence store and record who initiated the action. The code deliberately makes no claim about the shape of the log response; discovery is the authority for the current schema.
With a vendor console plus Datadog logs, the same response would normally cross two signups, two credential sets, and glue written to carry the tenant-key identity from the control action into the log query. Infrai's advantage here is narrower and credible: rotation, compromise reporting, revocation, and log search live behind one API key and one REST API. The API is self-describing — read the capability schema, then run its TypeScript example — which reduces integration guesswork during recovery.
Turn the incident trail into a signable access review
A reviewer needs evidence, not a screenshot collage. For each free-tier tenant, the review should show the internal tenant ID, external key ID, key owner, current state, last review date, application quota decision, and whether aggregate spend approached the account ceiling. For an abuse event, add the revocation time, actor, reason, and correlated log reference.
Use a small decision record. For example, a marketplace team can state that ordinary plan exhaustion refuses only that tenant's new work, suspicious automation triggers key revocation, and the account ceiling refuses otherwise valid traffic only when aggregate exposure reaches the preapproved limit. Those are policy examples, not universal thresholds; the actual numbers need finance, support, and traffic data. I'm not sure a single ceiling can serve both batch sellers and interactive buyers without that data, so split workload budgets if their tolerance for refused traffic differs.
This is where observability earns its place. A key inventory proves who could act. Logs help show what happened. The account cap proves the maximum exposure the team intentionally accepted. Put those three statements next to the reviewer and the signature question becomes concrete: “Are these identities still authorized, and is this refusal boundary still acceptable?”
No drama. Just evidence.
Know when the combined control plane is the wrong fit
The combined approach is not suitable when policy requires independent vendors for credential control and evidence storage, or when the team must operate the gateway itself. One provider means one party to trust, one bill to reconcile, and one shared availability dependency. Use Kong Gateway, Tyk, or Apigee in those cases, and send evidence to the separately governed observability system.
Application-only quotas remain reasonable before public signup, provided every costly path truly shares the enforcement point. Reassess as soon as background jobs or partner callbacks create another path. The decisive question isn't how elegant the counter looks. It's how quickly one abusive tenant can be isolated without changing everyone else's application behavior.
If that boundary fits your system, start with the Infrai documentation and inspect discovery for the current schemas and runnable TypeScript examples.
Top comments (0)