Skip to main content

AI Accountant

A Python library for reading AI session usage and calculating token costs, with a bundled local accounting CLI. Licensed under Apache 2.0.

The distribution name is ai-accountant; the primary import is ai_accountant. Release versions are derived from Git tags. Builds from a source distribution retain that version without needing Git. Install this checkout for development:

uv pip install .
from ai_accountant import SessionReader, ModelPrice, calculate_cost

reader = SessionReader()
# sources = reader.discover("codex", home="/path/to/.codex")
# session = reader.parse(sources[0], client="codex")

price = ModelPrice("example", "model", {"input": "2", "output": "10"}, "example")
cost = calculate_cost({"input_tokens": 1000, "output_tokens": 100}, price)
print(cost.amount_usd)  # Decimal('0.003')

Library

Session reading

SessionReader discovers local Codex, Claude Code and Machina accounting sources. The client is explicit; discovery and parsing only read files when called.

from ai_accountant import SessionReader

reader = SessionReader()
for path in reader.discover("codex"):  # or "claude" / "machina"
    session = reader.parse(path, client="codex")
    print(session.session_id, session.models, session.warnings)
    for event in session.events:
        print(event["provider"], event["model"], event["tokens"])

Pass home="/path/to/store" to discover to override the client's default location. A parsed session contains metadata, per-model totals, normalized usage observations, warnings and Codex tool statistics. It does not contain a full conversation replay. SQLite indexing and reports belong to the CLI.

Normalized input_tokens includes fresh input, cache reads and cache writes; cached_input_tokens and cache_write_input_tokens are subsets. Five-minute and one-hour write counts are subsets of cache writes. Reasoning is included in output. Use usage_complete and reasoning_known to interpret missing evidence; an omitted category represented as zero is not proof of measured zero. Source-specific parsing deduplicates usage snapshots and excludes inherited Codex fork counters. Provider and model identities are preserved.

Provider pricing

default_resolver() supplies OpenAI, Anthropic, DeepSeek, Z.ai, OpenRouter, Gemini, DashScope, xAI, Moonshot/Kimi, MiniMax, Groq, Mistral, DeepInfra and Copilot model prices, plus Copilot/Neuralwatt billing-unit/subscription conversions, with the full frozen release catalogue already loaded. Search and quote work offline immediately. Construction reads packaged JSON; import, construction and lookup perform no networking. Refresh explicitly for newer rates. Model providers read their own pricing pages or APIs first; community catalogue rows only fill exact IDs the provider source does not price, and stay labelled. Copilot model rates remain community-sourced. Provider identity, provenance, retrieval times and per-source coverage receipts are retained.

import asyncio
from ai_accountant import default_resolver

prices = default_resolver()
results = asyncio.run(prices.refresh("anthropic"))
for result in results:
    print(result.status, result.error)
for price in prices.search("*opus*", provider="anthropic"):
    print(price.qualified_id, price.rates)

Prices use Decimal USD per million tokens. Refresh failures retain the last good snapshot. A host owns refresh scheduling, persistence and its event history. calculate_cost accepts normalized counts and a ModelPrice; pass usage_complete=event["usage_complete"] when pricing parsed observations. Missing prices or consumed rates produce an unknown cost, never an assumed zero. Context-tier pricing needs the per-request context size. These quotes estimate API cost, not subscription quota usage or reconstructed historical invoices. Text prices remain usable when source rows also describe audio/tools/services; excluded dimensions are retained in the price metadata. Nonzero unsupported usage stays unknown. The calculator supports separate 12h cache writes and reasoning rates, plus validated bounded context-range tables. Keep the complete canonical ModelPrice.to_dict() when passing quotes between programs.

Custom provider and session-parser implementations can be registered explicitly. Installed plugin discovery is opt-in. For API signatures and docstrings:

from ai_accountant import SessionReader, PricingResolver, calculate_cost

help(SessionReader)
help(PricingResolver)
help(calculate_cost)

The source distribution includes detailed references at docs/session-library.md, docs/pricing-library.md and docs/campaign-classification.md, with maintainer release instructions in docs/releasing.md. They are archive paths, not hosted documentation links.

CLI

A Python CLI run with uv. It reads local Codex rollouts, Claude Code transcripts, and Machina turns, then reports token usage per model and estimated USD cost. SQLite indexing makes combined history queryable. Codex remains the default source so existing commands keep their behavior.

The installable accountant_pricing compatibility facade provides provider plugins, live price acquisition, an in-memory resolver and pure Decimal calculation for other programs. Consumers own their usage histories; SQLite snapshot persistence belongs to this CLI.

uv run accountant.py --list
uv run accountant.py SESSION_UUID
uv run accountant.py SESSION_UUID --parent-only
uv run accountant.py --latest
uv run accountant.py 'C:\path\to\rollout.jsonl' --prices prices.json
uv run accountant.py --all --prices prices.json
uv run accountant.py --resets --prices prices.json
uv run accountant.py --reset latest --prices prices.json
uv run accountant.py --reset 1791580844 --window primary --json
uv run accountant.py --client claude --list
uv run accountant.py --client machina --all --json
uv run accountant.py --client all --all --json

Use --client codex|claude|machina|all to choose a source. --all aggregates the clients selected by --client. Codex history defaults to $CODEX_HOME or ~/.codex; Claude uses CLAUDE_CONFIG_DIR or ~/.claude; Machina uses MACHINA_HOME or ~/.machina. Override those locations with --codex-home, --claude-home, and --machina-home. Claude and Machina session IDs are prefixed with claude: and machina: in combined reports and the database; Codex UUIDs remain unchanged. --latest picks the most recently modified file among selected clients, and --list includes each session's client.

--reset accepts latest, a Unix reset deadline, or an ISO timestamp with a timezone. --window secondary selects the secondary quota. Window lengths come from the logs; primary does not necessarily mean a five-hour window. --latest selects the most recently modified file, including archived and subagent sessions. Use a UUID or path when selecting a particular conversation. Unique UUID prefixes work too. --codex-home PATH supports other installations or exported histories. Session reports include recursively linked subagent rollouts by default, even when archived. --parent-only excludes them. JSON includes a per-session breakdown. Only explicit parent-thread links are followed; forks are separate conversations. Agents with no parent link cannot be attributed to a conversation.

Codex session reports also tally tool requests by name and recorded completed tool actions by type (commands, file changes, web extensions, and MCP calls). These are two overlapping views: an exec request can run several underlying tools, so do not add the two totals. Outputs and collaboration UI echoes are excluded; repeated call/action IDs and copied pre-creation fork history are not counted again. Counts include recorded failed actions and requests without outputs. Shell commands can perform multiple file reads or searches; individual file operations and hidden tool reviews cannot be reliably inferred from these counts. Run --sync to populate tool statistics in an older index. An index without these statistics reports them as unavailable rather than zero. Claude and Machina report tool statistics as unavailable. Aggregate token and quota reports do not include tool counts.

SQLite

uv run accountant.py --sync
uv run accountant.py SESSION_UUID --db accountant.sqlite3
uv run accountant.py --all --db accountant.sqlite3 --prices prices.json
uv run accountant.py --resets --db accountant.sqlite3 --prices prices.json
uv run accountant.py --reset latest --db accountant.sqlite3 --prices prices.json
uv run accountant.py --query 'SELECT * FROM model_usage ORDER BY total_tokens DESC'
uv run accountant.py --query 'SELECT * FROM reset_usage ORDER BY resets_at, model'

Run --sync to migrate older indexes before using session or aggregate reports; read-only SQL remains available on old schemas. Sync reparses available sources when the parser version changes, and backfills missing Codex tool statistics. Sources that are no longer available retain their indexed data; migrated legacy events remain conservatively marked incomplete until reimported.

Select clients with --client when syncing. Sync again to import new or changed logs. Unchanged files are skipped using size and modification time; changed files are replaced transactionally. Archiving a session updates its path without duplicating usage. Sessions whose logs are later removed remain in the database. The database contains usage metadata, not prompts or responses. --db PATH overrides the database location. Aggregate commands with --db read the indexed snapshot; commands without it read the logs directly. --sync --rebuild refreshes only the selected client index; use --client all --sync --rebuild to rebuild every client.

Tables: sessions (including the source client), usage_events (usage increments, provider, source-event identity, completeness flags, cache TTL counts, and raw rate-limit JSON), and rate_limits (primary/secondary observations), plus session_links (parent, fork, and source metadata), and tool_statistics (Codex tool counts). Views: model_usage, reset_usage, and session_roots, which maps each session to itself and its recursively linked descendants. The recursive view guards against cycles. --query opens SQLite read-only and prints JSON. For example, aggregate a root session and all its descendants by model:

SELECT u.model, SUM(u.input_tokens + u.output_tokens) AS total_tokens
FROM session_roots r
JOIN usage_events u ON u.session_id = r.session_id
WHERE r.root_session_id = 'SESSION_UUID'
GROUP BY u.model
ORDER BY total_tokens DESC;

Other queries:

SELECT u.model, SUM(u.input_tokens + u.output_tokens) AS total_tokens
FROM usage_events u
WHERE julianday(u.timestamp) >= julianday('2026-10-01T00:00:00Z')
  AND julianday(u.timestamp) < julianday('2026-10-08T00:00:00Z')
GROUP BY u.model;

Prices

Reports discover reference prices through the library's registered provider fetchers. Missing/stale catalogues are refreshed with a 24-hour TTL; successful snapshots are retained in --db (default accountant.sqlite3). This works from an installed wheel, which includes a complete generated prices.json catalogue. Fresh installations work offline before any database or successful refresh exists. Listing sessions, syncing usage and SQL queries do not fetch prices.

An exact model ID with one matching provider catalogue is priced automatically. When several providers publish the ID, the session's client decides: Codex sessions use OpenAI and Copilot sessions use Copilot. Otherwise the model's publisher (for example claude-* → Anthropic, glm-* → Z.ai) is used and marked best_guess; collisions between resellers/routers with no publisher candidate stay unpriced. --price-provider PROVIDER overrides all of this. Reports disclose the chosen provider/source and the candidates per model. This is an API-equivalent comparison; it does not infer which provider actually served the request. Aggregate reports use base-context rates.

--offline uses bundled prices and newer saved snapshots without fetching or creating a database. Older caches never replace newer bundled prices. Failed refreshes retain last-good prices and emit warnings. The library itself still performs networking only when its host explicitly calls refresh.

The generated root prices.json maps qualified provider/model IDs to rates, context tiers, provenance and retrieval times. Its _fallbacks section retains supplied internal/reference prices for IDs absent from the catalogues. A checked, byte-identical copy in accountant_pricing/prices.json is bundled in both release formats. Fallbacks never override catalogue matches or resolve ambiguity. The _offers section also freezes official Alibaba, Gemini and Z.ai price profiles with regional/service/date conditions and image/audio/storage units. Library consumers can discover these with search_offers and calculate explicit billable quantities with calculate_offer_cost; the CLI's existing token reports use the default token projections. The source archive's docs/pricing-library.md documents the profile API. Native profile results preserve the provider's billing denomination, including AIC, kWh and monetary currencies. Explicit source-backed conversion profiles can show a monetary comparison alongside the native charge; subscription-allocation values remain separate from overage tariffs and actual spend. The legacy CLI's token-cost reports retain their USD comparison contract. Use --prices FILE for explicit exact-model overrides (which may accompany a provider selection); rates are USD per million tokens. For example:

{
  "gpt-6.1-sol": {"input": 2.0, "cached_input": 0.1, "output": 10.0},
  "gpt-6-luna": {"input": 0.1, "cached_input": 0.01, "output": 0.5}
}

Explicit refresh/history commands remain available:

uv run accountant.py --refresh-prices --db accountant.sqlite3
uv run accountant.py --refresh-prices --price-provider openai --force-prices
uv run accountant.py --price-history --price-provider openai
uv run accountant.py --all --db accountant.sqlite3 --price-provider openai --json
uv run accountant.py --all --db accountant.sqlite3 --offline --json

The source archive's docs/pricing-library.md details source coverage, plugin extension, TTL, failure retention and snapshot history. Snapshot reports use standard base-context API reference rates and include provenance/assumptions. These are current estimates rather than reconstructed invoices.

The following describes the retained _fallbacks section, rather than the fetched catalogue. OpenAI fallback rates were supplied on 2026-10-04 and were not independently fetched during onboarding. GPT-5.5 Pro's "no discount" is represented by a cached-input rate equal to its input rate. The user supplied gpt-5.6-luna rates matching gpt-6-luna, and gpt-5.6-terra input rates matching gpt-6.1-sol with output at $12 per million tokens. The user supplied free pricing for codex-auto-review; all its token rates are zero. No other aliases are inferred. DeepSeek V4 Flash and Pro have explicit bare and deepseek/-prefixed entries. Their current official prices were retrieved via the DeepSeek homepage on 2026-10-12. The flat configuration uses peak rates: input/cache/output $0.30/$0.006/$1.20 for Flash and $1.32/$0.044/$3.96 for Pro, per million tokens. Off-peak rates are half and retained as metadata; the loader does not apply them. Peak hours are weekdays 01:00–04:00 and 06:00–10:00 UTC excluding Chinese public holidays. The official page explicitly bills the retired V4 Flash alias at current V4.1 Flash rates; Pro currently identifies V4-Pro-0813. These are current direct-provider estimates applied to history, not reconstructed invoices. Codex DeepSeek records report provider cog; its actual charges are not established.

The observed claude-opus-4-8 model has official Anthropic rates retrieved on 2026-10-12: input/cache-read/output $5/$0.50/$25 per million, with separate five-minute and one-hour cache-write rates $6.25/$10. Unclassified cache writes have no assumed rate. The user reports using alternative endpoints rather than Anthropic; for these logs the Opus rate is a hypothetical comparison. The CLI still prices exact model labels and does not validate the serving endpoint or actual underlying model.

The observed glm-5.2 model has official Z.ai rates retrieved on 2026-10-12: input/cache-read/output $1.40/$0.26/$4.40 per million. Cache storage is temporarily free on that page; it is not mapped to a token cache-write rate. These are standard API estimates, not Coding Plan subscription charges. Incomplete GLM usage remains unpriced even with known model rates.

Optional cache_write supplies a generic cache-write rate. cache_write_5m and cache_write_1h can price the two TTL buckets separately; each overrides the generic rate for its bucket. Unclassified writes still need cache_write. Unknown models or missing applicable rates yield an unknown total, with a priced subtotal. Exact names avoid guessing prices for aliases. Estimates apply the supplied reference rates to all history; they do not infer service-tier multipliers, historical price changes, long-context rates, subscription charges, or tool fees.

Cost = (uncached input × input rate + cached input × cached rate + cache-write input × cache-write rate + output × output rate) / 1,000,000. Cached and cache-write tokens are treated as subsets of input. Cache-write TTL counts are subsets of total cache writes. Reports show cached_percent as cached input divided by inclusive input, and reasoning_output_tokens: null when a client does not report reasoning counts. Incomplete usage is retained and warned about; its cost is not treated as zero. A model with any incomplete usage is left out of the priced subtotal as a whole. Provider names are stored only when a log reports them. Rates come from exact catalogue matches or the disclosed override/fallback. Claude Code client identity does not imply an Anthropic model or endpoint. Older Machina turn totals with ambiguous input/cache semantics stay visible as observed values, carry an incomplete flag, and do not produce a cached percentage or cost. Current canonical Machina request rounds preserve direct input, cache reads, cache writes, and output separately. Machina session.yaml metadata is read with PyYAML's safe loader when PyYAML is installed; without it, the reader uses transcript-directory identity and still accounts for current canonical request rounds. To enable YAML metadata for one run, use uv run --with pyyaml accountant.py --client machina --latest.

CLI aggregates and prices group by exact model name across selected clients and providers. SQL model_usage separates client/provider/model; reset_usage also separates the quota identity. Supply rates only when the model-wide pricing assumption is appropriate. Codex quota resets are recorded; other clients currently have no quota observations and appear as unassigned usage.

Interpretation

  • Codex metadata source: "vscode" does not establish VS Code use. Local rollouts with that marker also record originator: "codex-tui" or "JetBrains.IntelliJ IDEA". Report source and originator as recorded metadata; neither proves deliberate editor use or agent activity.

  • Cumulative counter differences avoid counting duplicate notifications. Model attribution follows the latest turn_context. Decreasing counters start a new segment and generate a warning. Missing models stay unknown.

  • Reset reports assign each increment to the reset deadline attached to its event. Deadlines within 60 seconds are grouped for the same limit ID and window length to absorb observed clock jitter. SQL reset_usage preserves exact deadlines.

  • Percentages are account quota observations, not per-model token percentages. Reports show first, last, and maximum observed percentages; percentages are never added. Times are recorded event timestamps and reset deadlines in UTC, not elapsed work time. Usage without a recorded reset is reported separately.

  • Results cover available logs only. Missing history, usage imported by a fork, inherited cumulative baselines, and consumption from other devices may affect completeness. A large cumulative increment is attributed to its current event; absent intermediate records cannot reconstruct its earlier timing or model. For forks with creation metadata, copied events before creation establish the counter baseline but do not contribute usage to the fork. A counter emitted at creation before local turn/request activity also establishes the inherited baseline. Real usage and tools at creation after activity starts remain counted. Parser version changes trigger reimport on the next sync, including this fix. The first session metadata record identifies the rollout; copied parent metadata does not replace it.

  • Active rollouts can have a partially written final line. Invalid lines are skipped with warnings and can be recovered on the next sync when the file changes.

Verification

uv run python -m unittest discover -s tests -v

If your environment restricts the default uv cache directory, add --cache-dir .uv-cache immediately after uv.

The CLI needs no third-party dependencies for token accounting and can also run directly with Python 3.11 or later:

python3 accountant.py --help
python3 -m unittest discover -s tests -v

GitHub Actions runs the tests on Python 3.11 and 3.13. Commit uv.lock with changes to project dependencies. Local environments, SQLite indexes, session logs, and generated historical usage reports are excluded from version control; prices.json is generated through provider acquisition and bundled for offline use, with manual rates isolated in _fallbacks. Online reports can write price snapshots to the selected database but never rewrite usage events. Use --offline for reports without fetching or snapshot writes.

Development and local artifacts

The source distribution includes docs/README.md (architecture) and docs/source-map.md for ownership, adapter contracts and index lifecycle.

The onboarding copied the seed index, its backup, historical-models.json, claude-cache-report.json, claude-cache-by-session.csv and diagnose-network.ps1 into this workspace as ignored local files. They are snapshots and diagnostics, not shared project inputs. The seed originals were preserved. Environments and caches are regenerated locally rather than copied.

Metadata

Release files for ai-accountant 0.0.4

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

Source distribution (sdist)

Source distribution for ai-accountant 0.0.4
File Size Uploaded Provenance Actions
ai_accountant-0.0.4.tar.gz 362.8 kB With provenance Details for ai_accountant-0.0.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ai-accountant 0.0.4
File Interpreter ABI Platform Actions
ai_accountant-0.0.4-py3-none-any.whl Python 3 none any Details for ai_accountant-0.0.4-py3-none-any.whl

Total release size: 577.6 kB

Release files / ai_accountant-0.0.4.tar.gz

Download URL ai_accountant-0.0.4.tar.gz
Size 362.8 kB
Tags Source
SHA-256 checksum
How to use checksums
5ffc8c98f359725d4d2aff8d05feb93da9743ca4528fb4e609b1f75bd3c19fce
BLAKE2b-256 checksum
How to use checksums
a9082ae9bf71b35b2882334039333f8629f7de60056489f65a6b5c2aa2cb3867
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 Oct 30, 2026.

Transparency log

Release files / ai_accountant-0.0.4-py3-none-any.whl

Download URL ai_accountant-0.0.4-py3-none-any.whl
Size 214.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8efb416fd7fc508d65b7f97035d176adab4016419dfc207a03dbb97588d46167
BLAKE2b-256 checksum
How to use checksums
656d92403354978bae284601667010dee25369cdf75732ddd55996f03266c7df
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 Oct 30, 2026.

Transparency log

Security for ai-accountant 0.0.4

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 ai_accountant-0.0.4.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 Oct 30, 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.

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