Caching
llm-usage-metrics uses caching to keep runs fast, stable offline, and predictable.
Why caching exists
Section titled “Why caching exists”- reduce repeated network calls (pricing + update checks)
- avoid reparsing unchanged source files on every run
- keep deterministic behavior when the network is unavailable
Cache layers
Section titled “Cache layers”| Cache | Purpose | Default TTL | Location |
|---|---|---|---|
| update-check cache | avoids querying npm on every startup | 1 hour | <platform-cache-root>/llm-usage-metrics/update-check.json |
| pricing cache | stores normalized LiteLLM pricing data | 24 hours | <platform-cache-root>/llm-usage-metrics/litellm-pricing-cache.json |
| event store | stores parsed file diagnostics/events and retained departed-file history | no TTL | <platform-data-root>/llm-usage-metrics/events.db |
| machine caches | one event store per other machine, filled by sync |
refreshed by reports after 10 minutes | <platform-data-root>/llm-usage-metrics/machines/<name>.db |
On Linux with no XDG_CACHE_HOME, <platform-cache-root> defaults to ~/.cache. The event store is a ledger rather than a cache, so it lives under <platform-data-root>: ~/.local/share when XDG_DATA_HOME is unset (Linux and macOS), %LOCALAPPDATA% on Windows. A ledger left in the cache directory by an older version is copied there once, on the next report; the old file stays until you delete it.
How each cache works
Section titled “How each cache works”1) Update-check cache
Section titled “1) Update-check cache”- read cached npm version if still fresh
- otherwise perform a bounded fetch (default 1s timeout) for the latest version so the update hint stays consistent across commands
- on fetch failure, fallback to previous cached version when available
- optional session-scoped mode creates a per-shell cache file
- skipped entirely for
--help,--version,npx, and direct source/development runs
Relevant config keys/env vars:
LLM_USAGE_SKIP_UPDATE_CHECKLLM_USAGE_UPDATE_CACHE_SCOPE(globalorsession)LLM_USAGE_UPDATE_CACHE_SESSION_KEYupdate.cacheTtlMsupdate.fetchTimeoutMs
2) Pricing cache
Section titled “2) Pricing cache”- tries fresh cache first
- if cache is stale and network is enabled, fetches LiteLLM pricing and rewrites cache
- if network fails, falls back to stale cache when possible
- for the default LiteLLM URL, falls back to the bundled snapshot when cache and network are unavailable
- with
--pricing-offline, skips network and uses fresh cache, stale cache, or the bundled snapshot
Relevant options/config:
--pricing-offline--pricing-urlpricing.cacheTtlMspricing.fetchTimeoutMs
3) Event store
Section titled “3) Event store”- enabled by default
- key is
(source, file path) - validity requires matching dependency fingerprints
- stores parse diagnostics and normalized events, and for claude, codex and pi each file’s tool calls and messages (never message text), which
toolsandsession --timelineread - dependency fingerprints include the primary file and any adapter-declared sidecar inputs; for SQLite sources that means the
-waland-journalfiles, not-shm, which every read rewrites - fingerprints also include a parser version, so upgrading to a release with parser fixes re-parses stored files once
- persisted as SQLite with per-file replace-on-change writes, batched into one transaction per source
- with
--since, file-per-session sources skip files whose newestmtime(including sidecars) is more than a day before the window; with the store on, only files the store already holds are skipped, so the ledger still ingests older files - reports include usage from files that no longer exist on disk by default;
--no-historyleaves it out.toolsandsession --timelineinclude those files’ tool calls and messages, except for files that departed before the store kept them - files only ever read through a directory flag (
--claude-dir ./export,--source-dir) are cached but never served as history by a report on the usual directories; once such a report finds the file in the usual location, it is history like any other file. Files stored this way by older versions are not recognized;prune --departed-beforecan delete them, but it also selects departed files from the usual directories, so preview without--applyfirst - moved, renamed, and copied files are suppressed by content hash to avoid double counting; a departed file that only partly overlaps counted usage is served without the overlapping events
llm-usage prunepreviews or deletes departed-file rows explicitly- first run after a store schema upgrade migrates the database in place and may take a few seconds
- deleting the database file deletes retained local history and starts a new ledger on the next run; a ledger once copied from the old cache-directory location is not copied again (a
legacy-ledger-copiedmarker beside the ledger records the copy), so delete that old file yourself to reclaim its space - legacy JSON cache shards are ignored and safe to delete manually
Prune is dry-run by default:
llm-usage prune --suppressedSelectors:
--suppressedselects departed files that--historyalready suppresses as moved or duplicated. Applying this selector does not change report output. Only live files a report has read in their current state vouch for a departed copy, so run a report first after files change or an upgrade.--departed-before YYYY-MM-DDselects departed files whose newest event timestamp is strictly older than that UTC date. Applying this selector permanently deletes retained history for those files.
Apply selected candidates:
llm-usage prune --suppressed --applyllm-usage prune --departed-before 2026-01-01 --applyAfter an applying run, the CLI deletes selected rows, runs SQLite VACUUM, and reports reclaimed database/WAL bytes.
Relevant env vars:
LLM_USAGE_EVENT_STORE(0disables it)LLM_USAGE_EVENT_STORE_PATHparseMaxParallelLLM_USAGE_PARSE_WORKERSLLM_USAGE_PARSE_WORKER_MIN_BYTES
Tuning examples
Section titled “Tuning examples”Avoid network pricing fetches:
llm-usage monthly --pricing-offlineIncrease parsing throughput:
parseMaxParallel = 16parseWorkers = 4Shorten pricing cache TTL to 2 hours:
[pricing]cacheTtlMs = 7200000Troubleshooting cache behavior
Section titled “Troubleshooting cache behavior”- When pricing uses the bundled snapshot,
stderrshows its snapshot date; run once without--pricing-offlineto refresh the cache. - Custom
--pricing-urlvalues use their matching cache or network source and do not fall back to the bundled default snapshot. - For stale reports after source file changes, verify source files updated
mtime. Deleting<platform-data-root>/llm-usage-metrics/events.dbstarts a fresh ledger, but it also deletes retained history used by--history. - To force update checks every run, set
update.cacheTtlMs = 0in the config file.