pg-api-guard
v1.0.0
Published
PostgreSQL-backed daily API usage limiter. Prevent bill shock.
Maintainers
Readme
pg-api-guard
PostgreSQL-backed daily API usage limiter. Prevent bill shock from runaway API calls.
The Problem
You use OpenAI, Stripe, Twilio, or any paid API. A bug, a bot, or a traffic spike causes thousands of unexpected calls. You wake up to a $500 invoice.
pg-api-guard adds a daily usage counter per API, backed by your existing PostgreSQL database. No Redis, no extra infrastructure.
Install
npm install pg-api-guardRequires pg as a peer dependency:
npm install pg pg-api-guardQuick Start
import pg from 'pg';
import { createApiGuard } from 'pg-api-guard';
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const guard = createApiGuard(pool, {
limits: {
openai: 2000,
stripe: 500,
sendgrid: 1000,
},
});
// Create the table (run once, safe to call repeatedly)
await guard.init();
// Before making an API call
const { allowed, usage, limit } = await guard.check('openai');
if (!allowed) {
console.log(`OpenAI limit reached: ${usage}/${limit}`);
return;
}
// After a successful call
await guard.increment('openai');API
createApiGuard(pool, options)
Creates a guard instance.
| Option | Type | Default | Description |
|----------|-------------------------|---------------|--------------------------------|
| limits | Record<string, number> | required | Daily call limits per API name |
| schema | string | "public" | PostgreSQL schema |
| table | string | "api_usage" | Table name |
guard.init()
Creates the usage table if it doesn't exist. Safe to call on every app startup.
guard.check(apiName)
Returns { allowed: boolean, usage: number, limit: number }. Does not increment the counter. APIs not listed in limits are always allowed (limit = Infinity).
guard.increment(apiName, count?)
Increments the daily call counter. Uses an upsert — no need to pre-create rows. Default count is 1.
guard.incrementErrors(apiName, count?)
Increments the daily error counter separately from the call counter. Useful for tracking API reliability.
guard.getUsage(apiName)
Returns today's call count for a single API.
guard.getAllUsage()
Returns all API usage rows for today:
[
{ api_name: 'openai', day: '2026-02-21', call_count: 142, error_count: 3 },
{ api_name: 'stripe', day: '2026-02-21', call_count: 89, error_count: 0 },
]Table Schema
guard.init() creates:
CREATE TABLE IF NOT EXISTS "public"."api_usage" (
api_name TEXT NOT NULL,
day DATE NOT NULL DEFAULT CURRENT_DATE,
call_count INTEGER NOT NULL DEFAULT 0,
error_count INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (api_name, day)
);Counters reset automatically each day — no cron jobs needed.
Why PostgreSQL?
- Zero extra infrastructure — you already have Postgres
- Transactional — no race conditions on concurrent increments
- Queryable —
SELECT * FROM api_usage ORDER BY day DESCfor usage history - Durable — survives restarts, unlike in-memory counters
Production Usage
Used in production by city-buddy.com to cap daily spending across OpenAI, Replicate, and Pexels APIs.
License
MIT
