TypeScript SDK
BetaESM, zero runtime dependencies, uses the global fetch.
Beta
Install
Node 18+ or any runtime with a global fetch. ESM, zero runtime dependencies, uses the global fetch.
npm install @counters.dev/sdkInitialize
Create a client with your organization API key. The base URL defaults to the production host; override it with baseUrl for local dev.
import { CountersClient } from "@counters.dev/sdk";
const client = new CountersClient({
apiKey: process.env.COUNTERS_API_KEY!,
});Count
add() and subtract() are buffered — the SDK coalesces writes per counter and flushes them as a single POST /batch. Use addNow()/subtractNow() for an immediate write that returns the new counter state. amount accepts a number, bigint, or decimal string.
const registrations = client.counter("registrations");
registrations.add(1); // buffered
registrations.add(5); // coalesced with the line above
await client.flush(); // one POST /batch
// Immediate write — returns the new counter:
const c = await registrations.addNow(1);
console.log(c.value); // "42" — a string (arbitrary precision)Read
Read the current value, or a time series of per-bucket deltas. from/to accept an ISO string or a Date; bucket is one of 1m, 5m, 1h, 1d, 1w, 1mo (finer buckets are plan-gated).
const { value } = await registrations.value();
const series = await registrations.series({
from: "2026-01-01T00:00:00Z",
to: "2026-01-08T00:00:00Z",
bucket: "1d",
});Manage
clear() resets the counter to zero (a new epoch — history is retained); delete() tombstones it; list() pages the organization's counters.
await registrations.clear();
await registrations.delete();
const page = await client.list({ limit: 50 });Lifecycle
Buffered writes flush on an interval and at maxBatchSize. Always close() (or flush()) before your process exits so no buffered write is lost.
await client.close(); // flush + stop the background timerErrors
Invalid keys or amounts throw CountersValidationError before any request is sent. Failed requests throw CountersError, carrying .status and the RFC 9457 .problem body.
import { CountersError, CountersValidationError } from "@counters.dev/sdk";
try {
await registrations.addNow(1);
} catch (e) {
if (e instanceof CountersValidationError) throw e; // bad input
if (e instanceof CountersError) console.error(e.status, e.problem);
}