Reach for an SDK when your service already knows who the customer is and what to count, and you want Aforo to own buffering, batching, and retry. Every SDK is emit-only: track(...) pushes onto an in-memory buffer and returns immediately, and a background worker flushes batches to the ingestor — so metering never sits in your request path.
No gateway present
Internal microservices, background jobs, or CLI tools that bypass the API gateway
Drop-in middleware
Meter one event per HTTP request with a framework adapter (Express, FastAPI, Chi, a Spring filter) — no per-route code
Custom metering logic
Count computed metrics (tokens processed, MB stored, minutes elapsed) that the gateway never sees, with a manual track() call at the point you know the quantity
Hybrid coverage
Gateway plugins meter edge traffic; SDKs meter the internal services and batch jobs behind them
INFO
All four SDKs live in one public distribution repo: github.com/aforoai/SDKs (under aforo-metering-sdks/). The install commands below show the intended public package names; where a package isn't on its registry yet, clone or Download ZIP from the repo and install from source. Tenant scope comes from the API key; there is no tenantId argument, and these SDKs only emit usage — they do not read entitlements or block requests.
@aforo/metering requires Node 18+ (it uses the built-in fetch). Events POST to https://ingest.aforo.ai/v1/ingest/batch.
terminal
# Not yet on npm — clone or Download ZIP and install from source:
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-metering-sdks/node
npm install && npm run build && npm pack
# then in your app:
npm i /path/to/aforo-metering-1.0.0.tgz
Manual Tracking
usage.js
import { AforoClient } from '@aforo/metering';
const aforo = new AforoClient({ apiKey: process.env.AFORO_API_KEY });
// Count one billable action — non-blocking, flushes in the background
await aforo.track({
customerId: 'cust_123',
metricName: 'api_calls',
quantity: 1,
});
// Flush remaining events before the process exits:
await aforo.shutdown();
Express Middleware
app.js
import { expressMiddleware } from '@aforo/metering/middleware/express';
// Meters every request after the response is sent — zero added latency
app.use(expressMiddleware({
apiKey: process.env.AFORO_API_KEY,
customerId: (req) => req.user?.id ?? null, // return null to skip metering
}));
The middleware hooks res.on('finish'), so it runs after the response. The default metric name is "<METHOD> <normalized-path>" (e.g. GET /users/:id). Fastify and Koa adapters live under the same @aforo/metering/middleware/* path.
The only hard dependency is httpx>=0.25. AforoClient enqueues into a ring buffer and a background daemon thread flushes batches.
terminal
# Not yet on PyPI — clone and install from source:
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-metering-sdks/python
pip install -e . # or: pip install -e ".[fastapi]"
Manual Tracking
usage.py
import os
from aforo import AforoClient
client = AforoClient(api_key=os.environ["AFORO_API_KEY"])
client.track(
customer_id="cust_1", # who is billed
metric_name="api_calls", # what you're metering
quantity=1,
)
# Force a synchronous flush when you need delivery confirmed:
result = client.flush() # FlushResult(sent=..., failed=...)
client.shutdown() # also registered via atexit
# Not yet on Maven Central — build from source into your local ~/.m2:
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-metering-sdks/java
mvn clean install
Manual Tracking
Usage.java
import com.aforo.metering.AforoClient;
import com.aforo.metering.AforoOptions;
import com.aforo.metering.TrackEvent;
// AforoClient is AutoCloseable — the buffer flushes on close
try (AforoClient client = new AforoClient(new AforoOptions(System.getenv("AFORO_API_KEY")))) {
client.track(TrackEvent.builder("cust_acme_001", "api_calls")
.quantity(1)
.metadata(java.util.Map.of("route", "POST /v1/charges"))
.build());
}
Spring Boot Auto-Configuration
application.yml
aforo:
enabled: true # auto-config is off unless this is exactly "true"
api-key: ${AFORO_API_KEY}
base-url: https://ingest.aforo.ai
The auto-configuration wires an AforoClient bean and a request-end servlet filter that runs after the response is committed (no added latency). It resolves the customer from the Spring Security principal, then X-Customer-Id, then X-Api-Key; requests with none of those are skipped.
The same public repo carries metering clients beyond the four base SDKs above. Clone it — or use Download ZIP — and follow each package's README; every package ships its own install + usage steps.
Protocol SDKs
Meter at the protocol layer instead of per-HTTP-request. Each protocol ships for Node, Python, Go, and Java — e.g. aforo-metering-sdks/node-graphql, python-grpc. The exact call API is in each package's README.
ProtocolWhat it metersPackage suffix
GraphQLOperations (query/mutation/subscription) with complexity scoring{lang}-graphql
Where it runsAt the edge, after the responseIn-process, after the response (non-blocking buffer)
Customer identityVerified JWT claim / subscription idServer-trusted header or a value you pass to track()
Best forAPI products fronted by a gatewayInternal services, CLI tools, background jobs
PRO TIP
Run both where it fits: a gateway plugin meters the API traffic that passes through your gateway with no code change, and an SDK meters what the gateway never sees — internal services, background jobs, and custom metrics like token counts where the quantity isn't one-per-request.