The number that lied: rebuilding a usage meter that actually helps
DEV Community

The number that lied: rebuilding a usage meter that actually helps

Short version

I run Claude Code on a flat monthly subscription, not pay-as-you-go. For months that plan never gave me a reason to look closely at it, so I stopped looking. Then an overnight automation burned through a week's worth of it in two days, the account locked with four days still to go, and I bought my way out at list price. That cost enough to make me build a small usage meter that now tells me, in plain language, whether I can spend more or need to slow down. The rest of this post is the four attempts it took to get that meter right, and why the wrong ones were each wrong in an instructive way.

The actual story

I run about ten projects at once, mostly alone. Client work, my own products, email, all of it moves through Claude Code sessions, one per project, in their own terminal tabs. Keeping track of what needs attention across ten folders got old, so I built a small side project, a "master supervisor" that walks every project's todo queue and picks off whatever it can do without me. I put it on a loop so it would work through the night. It worked beautifully. For two nights.

On the third day I opened my laptop and Claude Code answered every prompt with the same message: I had hit my weekly limit. Not low, not close. Hit. And the account told me the reset was four days out.

A word on that limit

Claude Code's subscription plans started out metered by month. Anthropic added a weekly limit on top of that later, presumably to stop exactly the kind of burst I'd just produced. I knew the monthly number existed somewhere. I did not know there was a weekly one running underneath it, because in months of normal use I had never come close to either, save one two-hour lockout I shrugged off as a fluke. So I never looked at the usage page on claude.ai. Not once a week, not once a month. I just worked, the way you don't check a fuel gauge on a car that's never run dry. The overnight loop didn't create the blind spot, it just found it.

The panic math

Four days without Claude Code is not an inconvenience for me, it is most of my income-producing time gone. I have client deadlines, my own projects mid-build, an inbox that does not pause because my AI subscription did. So I did the obvious thing: I bought extra tokens to keep working, at list price, no subscription discount attached. I was careful. I mostly used the cheaper model, kept sessions short, only touched what was actually urgent. Four days later I had spent 200 euros keeping a fraction of my normal pace alive. That number is what made me stop and actually understand what had happened, instead of just being angry at it.

The overnight loop was part of the story, and a separate lesson on its own (wrong model for the job, no cost brake on autonomous work). What stuck with me longer was something dumber: I had no way of seeing this coming, and I hadn't built myself a reason to look. There was no version of the truth in front of me that would have told me, on day two, "you are burning through a week's plan in two days." So I built one.

It took four attempts to get right, and the interesting part is not the tool, it is the four ways I got it wrong first.

Attempt one: a number I made up

The first version showed a weekly dollar total against a weekly dollar limit. I computed it myself from local session logs, tokens times model price, summed up. The problem: a subscription plan like mine is not metered in dollars at all. It is metered in percent of a weekly allowance. The dollar limit I was comparing against did not exist anywhere except in my own estimate, calibrated once against a single observed lockout, first at $12,000, later "corrected" to $4,690. I only noticed because the numbers stopped agreeing with reality.

The lesson is not that the figure was imprecise. It was invented, and it looked exactly like a measurement. A made-up number is worse than no number, because you trust it.

What it actually looked like at the bottom of the terminal, calm green, entirely fictional:

$4,690 weekly budget ยท $612 spent (13%) ยท well within budget

Attempt two: the right number, still useless

So I found the real source. Anthropic does not expose a usage API for subscription accounts, only for pay-as-you-go console accounts, but the same endpoint the usage page on claude.ai calls itself is reachable with a session cookie. No browser automation needed, no window has to stay open, just a decrypted cookie read once every fifteen minutes by a background job.

Now the status line showed something true:

Plan: 12% -> Mon 13:00

And the first question back to myself was the right one: what does 12% even mean? I did not know when the week had started. That is the actual problem with a lone percentage. 12% on day one means "way too fast." 12% on the last day means "you just threw away 88% of what you paid for." Same number, opposite meaning. A percentage without a position in time answers nothing.

Attempt three: reach, not a gauge

What I actually needed to know was not "how much is gone" but "how long will this last." A fuel gauge in liters is technically correct and useless while driving; nobody converts that in their head at 120 km/h. You want range.

The display now says something like this, on a calm Tuesday:

Plan: lasts 4.2 more days ยท resets in 3 days ยท on pace, room for more

The same three numbers carry three different colors depending on what they add up to.

Comfortably ahead, Monday morning, plenty of room:

Plan: lasts 6.8 more days ยท resets in 5 days ยท well ahead, spend freely

Cutting it close, the kind of line that used to just say a lonely percentage:

Plan: lasts 2.1 more days ยท resets in 3 days ยท tight, ease off if you can

And past the point of pretending it's fine:

Plan: lasts 1.4 more days ยท resets in 3 days ยท over pace, slow down now

Three numbers, all relative to right now: how long the remaining budget lasts at the current rate, when it resets, and what that means. The middle number, the reset, is what makes the first one readable at all.

Two facts about that reset turned out to matter, and I got both wrong before I checked the actual docs instead of guessing from the API field name. The field is called seven_day, which reads like a rolling window, so I assumed usage from a week ago would keep expiring as new usage came in. Wrong. It is a fixed weekly clock per account, same day, same hour, every time, in my case Monday 13:00 local.

And more importantly: nothing carries over. Anthropic's own words, "you get your full weekly amount in each cycle." A quiet week does not bank you anything for the next one.

That second fact rewired how I thought about the whole thing. If unused budget expires, then staying comfortably under the limit is not success, it is money paid for and not collected. A plan running at half capacity all month is the same as throwing away half the subscription price. Every version of this meter I had built so far only warned in one direction, too fast. None of them ever said the other true thing: too slow, you're leaving money on the table.

Attempt four: doing the math properly

The reach number is a projection: percent used divided by hours elapsed in the cycle gives a rate, the remainder divided by that rate gives the reach. Undamped, this breaks immediately. 20% used on day one projects to 140% for the week, so the meter would say "slow down" on a Monday morning that was simply a busy day, the kind any normal week absorbs by Friday.

My first fix was a damping factor, chosen by feel, heavier early in the week. I made the numbers up the same way I'd made up the dollar limit two attempts earlier, and I noticed it this time before shipping it, because I had learned to distrust round numbers I couldn't explain.

Usage over an observation window is a counting process, and counting processes have an actual statistical treatment: with a Jeffreys prior the posterior rate distribution is Gamma(x + 0.5, t), and its relative spread shrinks with 1/sqrt(x). That is precisely the intuition "early in the week the number swings more" made mathematically exact, not eyeballed.

So instead of judging the point estimate, the meter judges the optimistic edge of the credible interval: only warn once even the generous case doesn't add up. The tolerance falls out of that on its own, no calibration required:

Used Tolerance
5% 6.8x
10% 2.6x
20% 1.8x
50% 1.4x
90% 1.27x

Monday is naturally forgiving, Friday is naturally strict, and nothing about that is a knob I set.

What a systematic test found that thinking never would

I stopped trusting my own read of "does this look right" and instead generated every combination of cycle-day and usage-level, 133 cases, and checked each one against a single question: does the number agree with the color.

It found two things I would not have caught by staring at it.

First, the damping I'd just derived could flip the actual verdict, not just soften it. At 75% used on day five, 40 hours of projected reach against 48 hours to reset means it genuinely does not add up, and the meter still said "on pace" because the statistical tolerance pushed the ratio just over 1. Uncertainty is allowed to soften how loud a warning is. It is never allowed to flip whether there is one. Green now only fires when the math actually works out, full stop, tolerance or not.

Second, the fix for that created its own false alarm on the other side, triggering in the first hours of a cycle where there simply isn't enough data yet to say anything. 10% used by Monday evening was showing orange. The guard against false negatives now only engages after 24 hours of observation, before that the recommendation is capped at "on pace" and nothing stronger, because the data genuinely doesn't support a stronger claim yet.

Neither of those two bugs would have shown up from normal use. They only surfaced because I forced every corner of the state space through the same question.

The second pass, a day later

That first test suite checked one thing: given valid numbers, does the meter say the right thing. A second pass a day later asked something I hadn't thought to ask: what does it say when the numbers themselves are missing or stale.

Four more bugs, all from the same family. The meter kept computing as if its inputs were fine, even when they weren't, and it kept landing on something reassuring.

Broken input What it showed What that hides
Reset time already in the past "lasts 7 more days, resets in -180 min" The cycle already reset; the stored percentage is stale, and the projection ran on it anyway
Data 72 hours old Full reach number, "72h old" tacked on the end This is the exact shape of the original failure: a calm number sitting on top of a locked account
Usage field missing entirely "Fresh plan, resets in None" An outage rendered identically to a brand new week
Spend limit already below actual spend "-132 EUR this month" The hard lockout state, displayed as a negative number instead of a state

That third row, on screen, looked exactly as confident as a real reading:

Plan: lasts 7.0 more days ยท resets in None ยท on pace, room for more

All four now collapse to one honest line: the meter does not know right now, instead of guessing quietly.

Plan: data unavailable ยท last read failed, not guessing

And the background job that refreshes this every fifteen minutes reports a stale read on its own, past six hours, without being asked, because a meter that has stopped moving needs to be louder than one that is simply wrong. A frozen number looks exactly like a real one. A number you distrust at least gets checked.

Where that leaves me

I still don't know if the four-day lockout was purely the overnight loop, or partly a mismatch between which model does which job across ten projects, or both. That part is unresolved and I'm not going to pretend otherwise here. What I do know is that the meter I have now would have told me on day two, in plain language, that I was burning the week's plan at a pace that wouldn't reach Friday. Not a percentage I'd have to do arithmetic on mid-task. A sentence.

The broader thing I keep relearning, this time from a usage dashboard instead of from code: a plausible number with no source behind it is more dangerous than an obvious gap, because the gap gets questioned and the plausible number doesn't. And a number that goes quiet when its inputs disappear is worse than one that is simply wrong, because silence reads as agreement.

If you're running any kind of budget meter, subscription, cloud spend, rate limit, whatever, worth asking it the same question I eventually asked mine: what does it say when the data it needs isn't there. Mine used to answer with something calm. That was the actual bug.

Comments

No comments yet. Start the discussion.