Skip to main content

meterline

Part of meterline. meterline helps people who run or pay for AI tools in an organisation see and compare usage and cost across providers: it reads each provider's usage API and writes the data in one common record format.

The common record format for AI usage data: record types, validation, and JSONL and Parquet writers. No network and no provider knowledge. Provider connectors, such as meterline-anthropic, emit these records.

Python 3.10 or later. No runtime dependencies; pyarrow is an optional extra for Parquet.

Status: the v2 record format, its validation and its writers are built, and the connectors, the simulator and gold use them. See Known limits.

pip install meterline                  # core
pip install "meterline[parquet]"       # adds pyarrow

From a clone of the repository (for the tests and development):

pip install -e packages/meterline                  # core
pip install -e "packages/meterline[parquet]"       # adds pyarrow
pip install -e "packages/meterline[dev]"           # adds the test tools

To try the format without a provider, generate invented records with the simulator and read them back:

pip install -e packages/meterline-sim
meterline-sim generate --preset tiny --seed 7 --out sim-out
python -c "import glob; from meterline import UsageRecord, read_jsonl; f = sorted(glob.glob('sim-out/*/records/usage/*.jsonl'))[0]; print(len(read_jsonl(f, UsageRecord)), 'usage records in', f)"

Records

Five record classes, each with the key that identifies a record across batches:

Class Record key (used for revisions)
UsageRecord provider, provider_account_id, scope, grain, period_start, actor_kind, actor_id, product, model
ActivityRecord provider, provider_account_id, scope, grain, period_start, actor_kind, actor_id, product
SeatSnapshot provider, provider_account_id, scope, period_start
SeatAssignment provider, provider_account_id, scope, period_start, actor_id
OrgTotal provider, provider_account_id, scope, grain, period_start, product, model

All are frozen, keyword-only dataclasses; record_version is always 2. Dates are date (UTC, period_end inclusive), timestamps are timezone-aware datetime, amounts are Decimal or None. Every field, with its type, unit, nullability and meaning, is in the record reference; the code is src/meterline/records.py. The format is specified in section 3 of the v2 spec.

Use

from datetime import date
from meterline import UsageRecord, batch_failures, latest_per_key, read_jsonl, validate, write_jsonl

validate(record, today=date.today())          # raises RecordError on any rule of spec 3.1 and 3.7 (grain, token sums, cost basis, future dates, negative numbers, ...)
batch_failures(records, today)                # [] or messages: record rules plus duplicate keys and mixed org-total rows
write_jsonl(records, "records/usage/batch.jsonl")   # sorted by key, keys sorted, LF line ends: deterministic bytes
rows = read_jsonl("records/usage/batch.jsonl", UsageRecord)
newest = latest_per_key(rows_from_all_batches)      # {key: record}, latest batch_id wins

Import from the package root: every public name is in meterline.__all__. Two submodule routes are also kept stable, because the connectors import from them: meterline.records (the five record classes, validate, batch_failures, latest_per_key) and meterline.writers (read_jsonl, write_jsonl, write_parquet). A name with a leading underscore is internal.

  • to_dict() / from_dict() convert to and from plain dicts. Money is written as a string with at least two decimals (Decimal("412.800000") becomes "412.80"); quantities and decimal extras as strings without trailing zeros ("12.5"). Timestamps are written as UTC with +00:00; from_dict requires a timezone. A nullable field that is missing reads as None; a line with no record_version is a v1 line and is refused.
  • convert_v1(line, kind) and convert_v1_batch(path) turn v1 lines (all Anthropic's) into the v2 lines meterline-anthropic writes; the v1 file is not changed. To upgrade a data directory, move the v1 files out of records/<kind>/ and rename each .v2.jsonl to <batch_id>.jsonl; a connector reads every *.jsonl there as v2.
  • actor_cost_failures(usage, org_totals) is the check that one actor's cost on a day never exceeds the organization's, applied only within one provider, org, scope, grain, day, cost_basis and currency, never counting end_user records.
  • latest_per_key takes the record with the greatest batch_id. Batch ids sort chronologically (20261009T060000Z). On a tie, the later item wins.
  • write_parquet(records, path, cls=None) writes Decimal columns as decimal128(38, 12); the quantities and extras maps become Arrow maps (extras values as decimals). Pass cls to take the schema from an empty list. Without pyarrow it raises a RuntimeError that names the parquet extra.

Known limits

The limits of the core package, grouped by kind.

Reading records. from_dict, and so read_jsonl, converts integer and boolean fields with Python's int() and bool() and does not reject other JSON types: a string "false" in a boolean field reads as true. Files written by meterline never hold such values; a hand-edited file or one from another writer can.

Parquet. Decimals are written as decimal128(38, 12). The format keeps at most 12 decimals in a money or quantity figure (meterline.MAX_DECIMALS): validate refuses more, so a record that validates always writes. The JSON lines file holds every digit it was given.

Revisions. The format has no deletion marker. A record key that a source stops reporting keeps its last record as the latest, so a reader cannot tell a value that was removed from one that did not change (the LiteLLM README gives an example).

Public interface

What later releases keep working, under the compatibility policy in docs/records.md:

  • the record format: every field in docs/records.md, and its JSON Schemas in docs/schemas/;
  • the names in meterline.__all__, importable from the package root; the names the connectors import from meterline.records, meterline.writers and meterline.http stay importable from those three modules too. meterline.http holds the HTTP plumbing every connector shares (request throttle, retry back-off, Retry-After reading and capping, secret masking); it imports no HTTP library, so the core package keeps no dependencies.

Everything else is internal and may change in any release: every other module, and any name with a leading underscore.

Tests

python -m pytest -q packages/meterline

Metadata

Release files for meterline 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for meterline 0.2.0
File Size Uploaded Provenance Actions
meterline-0.2.0.tar.gz 66.1 kB With provenance Details for meterline-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for meterline 0.2.0
File Interpreter ABI Platform Actions
meterline-0.2.0-py3-none-any.whl Python 3 none any Details for meterline-0.2.0-py3-none-any.whl

Total release size: 107.5 kB

Release files / meterline-0.2.0.tar.gz

Download URL meterline-0.2.0.tar.gz
Size 66.1 kB
Tags Source
SHA-256 checksum
How to use checksums
4b96af34eb8afa0c53f4fe5ff888aeca9fe1f10727ea1e58dfc6f0ba2b33b26b
BLAKE2b-256 checksum
How to use checksums
56ed9c38a0b98da8e28146556fd98e1fc12a626f672d0abc24c00e7bce998449
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Release files / meterline-0.2.0-py3-none-any.whl

Download URL meterline-0.2.0-py3-none-any.whl
Size 41.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e18ab96440b3424c2c10f23ae81fc3012363d30d987f3bf1729fa571ac839ed0
BLAKE2b-256 checksum
How to use checksums
01e95bb8a043a7674de0275141e094aeedb545840ab97beae31428138c4d8851
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 1, 2026.

Transparency log

Security for meterline 0.2.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for meterline-0.2.0.tar.gz

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 Nov 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page