When to Use SDKs# SDKs are the code-first integration path. Use them when:
No gateway present
Internal microservices, background jobs, or CLI tools that bypass the API gateway
Granular feature gating
Application-level entitlement checks that need access to business context (user role, session state)
Custom metering logic
Billing for computed metrics (tokens processed, MB stored, minutes elapsed) not visible at the gateway
Hybrid enforcement
Gateway handles basic rate limiting; SDK handles fine-grained entitlement and margin checks inside the app
Node.js# npm install @aforo/metering Express Middleware (5 lines) const { AforoClient } = require('@aforo/metering');
const aforo = new AforoClient({
apiKey: process.env.AFORO_API_KEY,
tenantId: process.env.AFORO_TENANT_ID,
});
// Wrap any Express route — metering is automatic
app.use('/api', aforo.expressMiddleware({
metricId: 'api_calls',
tenantIdHeader: 'x-tenant-id',
})); Manual Metering // Meter a custom event (e.g., AI tokens consumed)
await aforo.meter({
metricId: 'ai_tokens',
quantity: completionTokens + promptTokens,
properties: { model: 'gpt-4o', endpoint: '/v1/chat' },
});
// Check entitlements before executing expensive logic
const access = await aforo.canAccess('advanced_search');
if (!access.allowed) {
return res.status(403).json({ error: 'Plan upgrade required' });
}
// access.remaining_quota → 8420 Python# pip install aforo-metering FastAPI Decorator from aforo_metering import AforoClient, aforo_middleware
aforo = AforoClient(
api_key=os.environ["AFORO_API_KEY"],
tenant_id=os.environ["AFORO_TENANT_ID"],
)
# Decorator — automatically meters every request
@app.get("/api/search")
@aforo.wrap_handler(metric_id="api_calls")
async def search(query: str):
return {"results": await do_search(query)} Django Middleware MIDDLEWARE = [
...
'aforo_metering.DjangoMiddleware',
]
AFORO_CONFIG = {
'api_key': os.environ['AFORO_API_KEY'],
'tenant_id_header': 'X-Tenant-Id',
'default_metric': 'api_calls',
} go get github.com/aforo/metering-go http.Handler Wrapper package main
import (
"net/http"
aforo "github.com/aforo/metering-go"
)
func main() {
client := aforo.NewClient(aforo.Config{
APIKey: os.Getenv("AFORO_API_KEY"),
TenantID: os.Getenv("AFORO_TENANT_ID"),
})
// Wrap any http.Handler — zero dependencies
mux := http.NewServeMux()
mux.Handle("/api/", client.Middleware("api_calls", yourHandler))
http.ListenAndServe(":8080", mux)
} Java / Spring Boot# <dependency>
<groupId>com.aforo</groupId>
<artifactId>metering-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency> Auto-Configuration aforo:
api-key: ${AFORO_API_KEY}
tenant-id-header: X-Tenant-Id
default-metric: api_calls
batch-size: 100
flush-interval-ms: 5000 @RestController
public class SearchController {
@Autowired
private AforoClient aforo;
@GetMapping("/api/search")
public ResponseEntity<?> search(@RequestHeader("X-Tenant-Id") String tenantId) {
// Entitlement check
EntitlementResponse access = aforo.canAccess(tenantId, "advanced_search");
if (!access.isAllowed()) {
return ResponseEntity.status(403).body("Plan upgrade required");
}
// Business logic...
SearchResult result = doSearch();
// Meter custom usage
aforo.meter(tenantId, "search_queries", 1, Map.of("type", "advanced"));
return ResponseEntity.ok(result);
}
} SDK vs. Gateway: When to Use Which# Criteria Gateway Plugin SDK
Code changes Zero Minimal (middleware/decorator)
Metering granularity Request-level Custom (tokens, bytes, compute time)
Entitlement latency <5ms (edge cache) <10ms (Redis or in-memory)
Best for API products with gateway Internal services, CLI tools, background jobs
Margin Guard Full L1-L3 L1 (alert) + L2 (in-app throttle)
Most enterprises use both . The gateway plugin handles 90% of API traffic automatically. SDKs handle the remaining 10% — internal services, background processing, and custom metering for AI workloads where token counts matter more than request counts.
Previous: Java SDK Next: MCP Servers