Skip to content

Cache responses for the future #1245

Closed as not planned
Closed as not planned

Description

@caillou

What do you want to change?

When I run the following, I only get responses for the current and part of the last month.

It would be nice to cache past days, and use that cache once the api stops delivering the data.

npx ccusage@latest monthly --compact -s 2026-01-01

Why?

I'd like to see how my overall usage of tokens changes over long periods of time.

How? (optional)

No response

Activity

  1. pullfrog commented on Jun 10, 2026

    @pullfrog
    Contributor
  2. github-actions commented on Jun 10, 2026

    @github-actions
    Contributor

    This issue was auto-closed. Issues from new contributors are auto-closed by default.

    Maintainers review auto-closed issues and reopen worthwhile ones. Issues that do not meet the quality bar in CONTRIBUTING.md may not be reopened or receive a reply.

    Keep the issue short, concrete, and written in your own voice.

    If a maintainer replies lgtmi, your future issues will stay open. If a maintainer replies lgtm, your future issues and PRs will stay open.

    See CONTRIBUTING.md.

  3. ryoppippi commented on Jun 10, 2026

    @ryoppippi
    Member

    @caillou
    claude code deletes logs in 28 days by default. check out cc's docs and ccusage.com

  4. caillou commented on Jun 10, 2026

    @caillou
    Author

    @ryoppippi thanks for your reply.

    I read the docs, I might have missed something important in there.

    The question I am asking is if may implement a type of cashing. Basically having ccusage write the values it gets into a cache, and have the option to recall these values from cache.

    I fully understand if this is not the kind of feature we want in ccusage.

  5. ryoppippi commented on Jun 11, 2026

    @ryoppippi
    Member

    does ccusge takes time to you? now it's really fast right?

  6. caillou commented on Jun 11, 2026

    @caillou
    Author

    @ryoppippi I don’t think I expressed my requirement well enough.

    The purpose of the cash is not to speed things up, but to keep historic data, which is not provided by the Anthropic API.

  7. Buster210 commented on Jun 12, 2026

    @Buster210

    @ryoppippi

    Hit the same thing — lost usage due to the rotation back on June 9, so I built a small on-disk cache and have been running it as my daily driver since. To actually verify it holds, I deleted some of my logs by hand and re-ran both side by side.

    Stock ccusage — only what's left on disk after the delete ($108.56):

    Image

    My cached build — same machine, kept the deleted days ($465.17):

    Image

    Same live data on both sides; the cache just adds back the Claude days whose logs were gone. Logic: once the logs are deleted that history is normally unrecoverable, but the cache still has it.

    Speed bonus: stock run (57.5s here), while a warm cache serves precomputed rows straight from cache (0.88s) — ~65× faster on wall-clock. The first/cold run that populates the cache is ~stock speed since it computes and writes; the win is on every run after.

    It's a SQLite cache that stores each log's result once, keyed by file (no double-counting), live logs always win, and there's a --live-only flag to ignore it. One honest catch: once a log is gone its cached cost can't be re-verified, so the cache only ever adds — it never overrides live data.

    Happy to put up a draft PR or a quick design note if you're open to it.

  8. Spenhouet commented on Jun 17, 2026

    @Spenhouet

    I keep hitting this on ccusage monthly over time: older months keep shrinking on every run. I dug into why and I think this issue is worth doing — here's the evidence and a concrete design.

    Root cause (confirmed)

    Claude Code deletes session transcripts under ~/.claude/projects/**/*.jsonl once they're older than cleanupPeriodDays (default 30) on startup. That's not ccusage — it's upstream behavior, and it's well documented now:

    ccusage fully rescans and recomputes from the surviving JSONL files on every run and persists nothing between runs, so once Claude prunes a file, the cost/tokens it contributed silently disappear from daily/weekly/monthly. As @Buster210 showed above, that can be the difference between ~$108 and ~$465.

    Proposed design

    An on-disk cache for the date-bucketed reports (daily/weekly/monthly) that only ever adds back history — it never overrides live data:

    • After the live scan produces per-day summaries (before --since/--until filtering), merge them with a cached copy keyed by (project, date).
    • For each day, keep the higher-cost of {live, cached} so a day whose logs were partially or fully pruned can't shrink. Live wins on ties (fresh data).
    • Persist the merged per-day summaries back. Days that still have logs get refreshed; days whose logs are gone are served from cache.
    • Scope the cache by timezone + cost mode so -z/--mode variants don't mix.
    • Cache file lives outside the paths Claude prunes (e.g. <claude-dir>/ccusage/), so it survives cleanup.
    • Add --no-cache to bypass read+write for a pure-live run.

    Scope note: session/blocks need per-entry data, so they'd stay live-only; the cache covers the date-bucketed reports where the shrinking is visible.

    I've already prototyped this against the Rust CLI and have it working. Happy to open a PR if a maintainer is open to it (I know the gate requires lgtm first).

  9. Buster210 commented on Jun 18, 2026

    @Buster210

    @Spenhouet good write-up. Same root cause for me, the 28-day jsonl cleanup is what ate my June 9 history.

    The main difference is where we cache. You're caching the date-bucketed summaries keyed by (project, date) and keeping the higher of {live, cached}. I went lower and cache each source log's result keyed by file, deduped on the billable-call id so nothing double counts. It's more storage, but because it's at the log level it also covers session and blocks, not just daily/weekly/monthly.

    The part I spent the most time on was making sure it doesn't lie. For days whose logs still exist, it reprices on read so a warm run is identical to stock. I actually deleted a few of my own logs by hand and ran both builds side by side to confirm the live numbers still lined up. They did.

    For days that are already gone, the cost falls back to a small spend ledger that keeps the value from the last run before the file disappeared. I froze that on purpose so a later price-table change can't rewrite spend I already paid. There's a test pinning it (ledger_entry_cost_immutable_after_pricing_change) so it won't silently drift.

    Cache db sits in ~/.cache/ccusage, outside what Claude prunes, so cleanup leaves it alone. Warm runs are about a second here vs ~57s cold. --live-only skips it, clear-cache wipes it.

    Either way the rule is the same: it only adds history back, never shrinks a day. Been running it as my daily driver for a week. Fine to align on whichever level a maintainer prefers.

  10. github-actions commented on Aug 28, 2026

    @github-actions
    Contributor

    Pullfrog triage: priority:medium

    A maintainer explicitly requested an implementation attempt. Clear, repository-scoped enhancement request with concrete motivation and constructive follow-up designs. It is not spam, invalid, a duplicate, explicitly wontfix, or out of scope.

    Decision: kept open; Pullfrog will attempt a focused implementation PR.

  11. ryoppippi commented on Aug 30, 2026

    @ryoppippi
    Member

    Closing as not planned for now. A response cache needs an explicit retention, invalidation, privacy, and stale-data contract before it can be added safely; the issue does not define those boundaries. The current statusline process-leak work is tracked separately in #1596.

  12. Spenhouet commented on Aug 31, 2026

    @Spenhouet

    @ryoppippi I'm not really getting what the problem is on implementing caching. Without this ccusage is unusable over time. I'm using my own version of it with caching for a while now. Was just hoping you would finally add it. You closed this ticket due to some additional details not being defined but why not just raise these here so we can define them? Why closing this issue? And why not just implement it with what ever addition requirement you have in mind?

  13. ryoppippi commented on Aug 31, 2026

    @ryoppippi
    Member

    Historical audit: this discussion was auto-closed by the legacy contributor gate. That closure did not assess technical importance.

    Audit result: excluded from the current maintenance scope. This request depends on an unsupported external source, unavailable schema or fixture, platform or release infrastructure the project cannot reliably maintain, an operational service, or an unsafe or underspecified design. It is not a candidate for automatic reopening or implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority:mediumA normal bug or meaningful improvement.triage:excludedExcluded from the current maintenance scope.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions