This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 1.1.2 instead.
Reason given by maintainers: No executionStatus and miscounts rejected events. Use the latest version.
aforo-metering
Track API usage events from any Python service and let Aforo handle buffering, batching, and retry — plus drop-in middleware for FastAPI, Django, and Flask that meters every request without touching your handlers.
Version: 1.0.0 · Apache-2.0 · Changelog · User guide
Install
Intended public install:
pip install aforo-metering
# framework extras (pick what you use):
pip install "aforo-metering[fastapi]"
pip install "aforo-metering[django]"
pip install "aforo-metering[flask]"
Not yet on PyPI — install from source for now. Straight from GitHub:
pip install "git+https://github.com/aforoai/SDKs.git#subdirectory=aforo-metering-sdks/python"
Or clone the repo and install this package in editable mode, for local development:
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-metering-sdks/python # the folder holding pyproject.toml
pip install -e .
# with a framework extra:
pip install -e ".[fastapi]"
The only hard dependency is httpx>=0.25. Framework packages (fastapi/starlette, django, flask) are pulled in by the matching extra — they're not required for the bare client.
Quickstart
Best when you control the call site and want to emit one event per billable action. AforoClient enqueues into a ring buffer and a background daemon thread flushes batches; you never block on the network.
import os
from aforo import AforoClient
client = AforoClient(api_key=os.environ["AFORO_API_KEY"], product_type="API")
client.track(
customer_id="cust_1", # who is billed
metric_name="api_calls", # what you're metering
quantity=1,
)
# Per-event productType override + optional top-level ingest fields:
client.track(customer_id="cust_1", metric_name="agent_runs", product_type="AI_AGENT",
extra_fields={"agentId": "agent_7", "sessionId": "sess_42"})
# Force a synchronous flush when you need delivery confirmed:
result = client.flush() # FlushResult(sent=..., failed=...)
# Graceful shutdown drains the buffer. Also registered via atexit,
# so a clean interpreter exit flushes for you.
client.shutdown()
Events POST to https://api.aforo.ai/v1/ingest/batch with X-API-Key: <api_key>. The client appends /v1/ingest/batch to base_url, so set base_url to the host only.
Tenant scope comes from the API key — there is no
tenant_idargument on this SDK.customer_idis the entity you bill within that tenant. Never feedcustomer_idfrom a client-settable request header you don't trust.
Configuration
Pass these as keyword args to AforoClient(...), or build an AforoOptions and pass options=.
| Option | Type | Default | What it does |
|---|---|---|---|
api_key |
str |
— (required) | Aforo API key, sent as X-API-Key on every batch. |
base_url |
str |
https://api.aforo.ai |
Ingestor host. /v1/ingest/batch is appended automatically. |
product_type |
str |
"API" |
Top-level productType on every event (API, AGENTIC_API, AI_AGENT, MCP_SERVER, GRPC_API, GRAPHQL_API, WEBSOCKET_API, MQTT_BROKER); required by the production ingestor. Trimmed + upper-cased; override per event with track(product_type=...). |
flush_count |
int |
50 |
Buffered events that trigger a flush. Also the max batch size per request (clamped to 1..1000, the ingestor's limit). |
flush_interval |
float |
5.0 |
Seconds between background timer flushes. |
max_queue_size |
int |
10000 |
Ring-buffer capacity. On overflow the oldest event is dropped. |
max_retries |
int |
3 |
Retries on 5xx / 408 / 429 with exponential backoff. |
retry_base_s |
float |
1.0 |
Base delay for backoff (retry_base_s * 2**attempt). |
timeout |
float |
10.0 |
Per-request HTTP timeout in seconds. |
shutdown_timeout |
float |
5.0 |
Graceful-shutdown drain budget. |
heartbeat_interval |
float |
30.0 |
Seconds between session heartbeats (see start_session). |
track() raises ValueError for a blank customer_id / metric_name or quantity <= 0 — the ingestor rejects such an event, and one invalid event fails the whole batch.
Retry rules, fixed in the transport and not configurable beyond the values above: retry on 5xx, 408, 429; honor Retry-After on 429; never retry other 4xx (the batch is dropped and counted as failed).
Framework middleware
Each adapter constructs its own AforoClient and emits one event per request.
- Metric:
metric_name— a fixed name or a callable; default"api_calls"(aforo.DEFAULT_METRIC_NAME). The metric must exist in your tenant's Aforo catalog: the ingestor rejects an unknown metric, and because it validates a batch as a whole, one rejected event fails every event in that batch. - Customer:
customer_id— a fixed id or a callable; default is theX-Customer-Idheader (Django triesrequest.user.idfirst). The caller'sX-Api-Keyis never used — it is a secret, not a customer id. A request with no resolvable customer ID is not metered. - Product type:
product_type(Flask kwarg /AFORO_PRODUCT_TYPEconfig, DjangoAFORO_PRODUCT_TYPEsetting, FastAPI kwarg) — default"API". - Every event carries top-level
endpointPath(path without query string, max 512 chars),httpMethod,statusCodeandresponseTimeMs. Aquantityresolving to<= 0is not metered. - CORS preflights (
OPTIONS) are never metered.
# FastAPI / Starlette -- callables receive the ASGI scope
from aforo.middleware.fastapi import AforoMeteringMiddleware
app.add_middleware(AforoMeteringMiddleware, api_key=os.environ["AFORO_API_KEY"],
metric_name="api_calls", product_type="API")
# Flask -- metric_name(request, response), customer_id(request); or AFORO_METRIC_NAME / AFORO_CUSTOMER_ID config
from aforo.middleware.flask import AforoMetering
AforoMetering(app, api_key=os.environ["AFORO_API_KEY"], metric_name="api_calls",
customer_id=lambda req: req.headers.get("X-Customer-Id"))
# Django settings.py -- AFORO_METRIC_NAME: str or callable(request, response); AFORO_CUSTOMER_ID: str or callable(request)
MIDDLEWARE = [..., "aforo.middleware.django.AforoMeteringMiddleware"]
AFORO_API_KEY = os.environ["AFORO_API_KEY"]
AFORO_METRIC_NAME = "api_calls"
AFORO_PRODUCT_TYPE = "API"
MiddlewareOptions adds product_type, metric_name, quantity, customer_id, metadata (callables or constants), plus exclude_paths and exclude_status_codes. See the user guide for the full table.
Walk me through it
The end-to-end path — install → configure → first metered event → confirm it landed in Aforo — is in USER_GUIDE.md.
What this doesn't cover
This SDK only emits usage events. It does not read entitlements, enforce quotas, or block requests — middleware always returns the original response, and metering failures are swallowed so they can't break your request path. Rate plans, pricing, and which metric_name values map to billable lines are configured in the Aforo console, not here. Broker- and gateway-side metering (Kong, EMQ X, etc.) live in their own plugins, not in this client.
Metadata
Release files for aforo-metering 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aforo_metering-1.0.0.tar.gz | 31.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aforo_metering-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 57.1 kB
Release files / aforo_metering-1.0.0.tar.gz
| Download URL | aforo_metering-1.0.0.tar.gz |
|---|---|
| Size | 31.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eb3990613e1ee51a2b8ded607225af79db998d74ba17970fb2a152c2ddccde58
|
|
BLAKE2b-256 checksum How to use checksums |
6df249a623da617eb8cad38eb6b401f00dd33a79a76bd74e3cd7dbe342c43bea
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logRelease files / aforo_metering-1.0.0-py3-none-any.whl
| Download URL | aforo_metering-1.0.0-py3-none-any.whl |
|---|---|
| Size | 25.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
41feb55d253e2b657d229d9fb9c4401d87bf81053df80a2cad3ad66f01e7da37
|
|
BLAKE2b-256 checksum How to use checksums |
2c568c475ccf35ca5ff985448f9fb80cc2ee7d5eb9a523fe6067e077a39f4988
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency log