This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 1.2.2 instead.
Reason given by maintainers: No executionStatus and miscounts rejected events. Use the latest version.
aforo-grpc-metering
Meter every gRPC call with a server interceptor — one Aforo event per RPC, with the gRPC status mapped to a readable label, call type, and duration. Streaming RPCs are metered with one explicit record() call.
Version: 1.0.0 · Apache-2.0 · Changelog · User guide
Install
Intended public install:
pip install aforo-grpc-metering
pip install "aforo-grpc-metering[httpx]" # or [aiohttp] — faster HTTP flush than stdlib urllib
Not yet on PyPI — install from source for now:
git clone https://github.com/aforoai/SDKs.git
cd SDKs/aforo-metering-sdks/python-grpc # folder holding setup.py
pip install -e .
pip install -e ".[httpx]" # or [aiohttp]
The one required dependency is grpcio>=1.50.
Quickstart — unary interceptor
Best when your service is mostly unary RPCs and you want per-call billing with no handler changes.
import os, grpc
from concurrent import futures
from aforo_grpc_metering import AforoGrpcBilling, AforoGrpcInterceptor
billing = AforoGrpcBilling(
tenant_id="tenant_acme",
product_id="prod_grpc_user_svc",
api_key=os.environ["AFORO_API_KEY"],
ingestor_url="https://api.aforo.ai",
service_name="acme.v1.UserService",
)
server = grpc.server(
futures.ThreadPoolExecutor(max_workers=10),
interceptors=[AforoGrpcInterceptor(billing)],
)
# add_UserServiceServicer_to_server(servicer, server)
server.add_insecure_port("[::]:50051")
server.start()
server.wait_for_termination()
Every unary RPC is now metered — one grpc_api.rpc_calls event with grpcStatusCode, grpcCallType=UNARY, and executionDurationMs, POSTed to https://api.aforo.ai/v1/ingest/batch with X-API-Key: <api_key> and X-Tenant-Id: <tenant_id>.
⚠ Events are sent to the ingestor's
/v1/ingest/batchpath as{"events": [...]}, at most 1000 events per request (larger buffers are split). Setingestor_urlto the host only — the SDK appends the path.
The interceptor auto-wraps unary RPCs only. For server-stream / client-stream / bidi, call
billing.record(...)yourself at the end of the handler (see the user guide).tenant_idis fixed from config; the default extractor readsx-customer-idfrom invocation metadata, and calls with no resolvable customer ID are not metered.
Configuration
Constructor arguments for AforoGrpcBilling(...):
| Option | Type | Default | What it does |
|---|---|---|---|
tenant_id |
str |
— (required) | Aforo tenant; sent as X-Tenant-Id. |
product_id |
str |
— (required) | Product the RPCs bill against. |
api_key |
str |
— (required) | Aforo API key, sent to the ingestor as X-API-Key. |
ingestor_url |
str |
— (required) | Host; /v1/ingest/batch is appended. |
service_name |
str |
— (required) | Fully-qualified gRPC service; stamped as grpcService. |
flush_interval_sec |
float |
5.0 |
Background flush cadence (daemon thread from construction). |
flush_count |
int |
50 |
Buffer size that triggers an immediate flush. |
on_error |
Callable[[Exception], None]? |
logs | Called on permanent batch failure, and with the ingestor's errors[].message when it rejects events. |
product_type |
str |
"GRPC_API" |
Top-level productType sent on every event (trimmed and upper-cased; values the SDK does not know are passed through). Override per event with record(..., product_type=...). |
customer_id_extractor |
Callable[[Any], str?]? |
reads x-customer-id from metadata |
Resolve the billed customer from the gRPC context. |
Status mapping: GRPC_STATUS_LABELS maps numeric codes to descriptor labels (e.g. OK, NOT_FOUND, UNAVAILABLE); the interceptor records the label as grpcStatusCode. Retry is fixed at 3 attempts (1s / 2s backoff between them); 408 and 5xx are retried, 429 waits for Retry-After (capped at 60 s), and any other 4xx is not retried.
Walk me through it
Install → add the interceptor → call an RPC → confirm the event in Aforo, plus the streaming pattern, is in USER_GUIDE.md.
What this doesn't cover
The interceptor only auto-meters unary RPCs — streaming RPCs need a manual record() (the SDK can't know when a stream ends or how many messages flowed). It meters per-call, not per-message, unless you pass message_count/data_bytes to record(). It doesn't enforce quotas or abort RPCs. Pricing and metric mapping live in the Aforo console.
Metadata
Release files for aforo-grpc-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_grpc_metering-1.0.0.tar.gz | 16.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aforo_grpc_metering-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.2 kB
Release files / aforo_grpc_metering-1.0.0.tar.gz
| Download URL | aforo_grpc_metering-1.0.0.tar.gz |
|---|---|
| Size | 16.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45aa3559c9b0fcb5609d7e5b0a84db0aabfd04d182b295f7bb4499c67183dd9c
|
|
BLAKE2b-256 checksum How to use checksums |
0fcd61b5ee56a0521f2df20b07e16b741511c82b14cce12ef5d9eb447191f108
|
| 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_grpc_metering-1.0.0-py3-none-any.whl
| Download URL | aforo_grpc_metering-1.0.0-py3-none-any.whl |
|---|---|
| Size | 9.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4ee4cabd6d2736e375a33547c0d017b5b02ae2d6edbefb57012a0823bfc84e95
|
|
BLAKE2b-256 checksum How to use checksums |
cf36324ea61bb620f1ac2d75d594d4a1899459a687e571a8d50256dd3ce38326
|
| 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