What do you do when the only thing that is really wrong is the number on the cloud bill? I've been there! Somebody somewhere expected production to spend $25K a month on Anthropic inference, and we just spent $36,500 last month. Usage is up. Everyone in engineering is pretty sure things are working in the application as designed. But the budget is blown and nobody's happy.
Not every cloud cost problem is a bug.
Frugal Fixes are built to find and fix cloud waste that fits a common cost antipattern: a retry loop hammering an API, a Lambda sleeping on the clock, a per-order write to S3 that should have been a batch. Recognizable cost traps with recognizable fixes.
This post is about the other kind of cloud waste. There is no cost trap, no human error, no line of code that makes you wince. Its just software doing its thing. Software built on cloud services that bill for usage. Engineers and their coding agents can still bring those costs down, but the code on its own won't tell them what to change. They need production cost and usage data to do it.
To show what I mean, let's take a look at how a coding agent responds when we ask it to reduce costs. We will do every step twice: in the first case we will arm Claude Code with Frugal's cost and usage data, and in the second case we'll see what happens with Claude working with the code alone. We hit this same situation in Frugal's own production services, and a later post will cover how we used Frugal to bring those costs down.
The application that is spending 30% too much
Acme Expenses is an expense management backend: corporate cards, a receipt inbox, expense reports, a help center.

It calls Anthropic from eight places, all through one client wrapped with the Frugal SDK:
from anthropic import Anthropic
from frugal_metrics import wrap_anthropic
client = wrap_anthropic(Anthropic())
That one line code change is what gives Frugal per-call-site token counts, latency, duplicate-request detection and prompt analysis for everything below.
From the code, we can get the list of all the call sites that contribute to our Anthropic bill:
| Feature | Call site | Model | What it does |
|---|---|---|---|
| Help Answers | answer_help_question |
Sonnet 5 | Answers help-center questions from the top five matching articles |
| Receipt Triage | triage_inbound_email |
Haiku 4.5 | Classifies email forwarded to the receipt inbox and pulls out merchant, total and date |
| Transaction Categorization | categorize_transaction |
Haiku 4.5 | Assigns an expense category to each card transaction as it posts |
| Monthly Summary | summarize_monthly_spend |
Sonnet 5 | Writes each account's monthly spend summary |
| Report Titles | suggest_report_title |
Haiku 4.5 | Suggests a title for an expense report |
| Violation Notes | draft_violation_note |
Sonnet 5 | Drafts the note sent when an expense breaks policy |
| Audit Narrative | generate_audit_narrative |
Opus 5 | Writes the narrative for an audit package, with adaptive thinking, high effort, 16K max tokens and a 30K-token prompt |
| Vendor Names | normalize_vendor_name |
Haiku 4.5 | Turns a card descriptor into a clean vendor name |
If you were handed this list and the 30% cost reduction target, which call site would you look at first? If you said Audit Narrative, on Opus with a 30,000-token prompt and a thinking budget, you're in good company. Looks like trouble. But hold that thought.
The rest of this post walks through four decisions Claude made using data provided by Frugal: which call sites deserve an engineer's time, and then what to do about each of the three that do.
Spend time where the money is
The first question is the same one we always start with: where is the money going?
Finance says our Anthropic bill for acme-expenses is about $36.5k a month and needs to come down by at least 30%. Before anyone changes code, I want to know which AI call sites are worth an engineer's time and which we should leave alone. Use Frugal to work it out and give me a short, ranked recommendation.
Here's what Frugal returns:
AI call sites by cost, 2026-08-24 -> 2026-09-22 (30 days). Total $36,527.00.
# call site model calls/day cost (30d) share
1 support/help_answers.py:26 answer_help_question claude-sonnet-5 63,333 $13,870.00 38.0%
2 inbox/email_triage.py:32 triage_inbound_email claude-haiku-4-5 36,667 $11,440.00 31.3%
3 ledger/categorize.py:47 categorize_transaction claude-haiku-4-5 206,667 $7,626.00 20.9%
4 insights/monthly_summary.py:24 summarize_monthly_spend claude-sonnet-5 1,400 $1,344.00 3.7%
5 reports/titles.py:11 suggest_report_title claude-haiku-4-5 80,000 $960.00 2.6%
6 policy/violation_notes.py:41 draft_violation_note claude-sonnet-5 3,167 $627.00 1.7%
7 audit/narrative.py:20 generate_audit_narrative claude-opus-5 37 $412.50 1.1%
8 vendors/normalize.py:7 normalize_vendor_name claude-haiku-4-5 30,000 $247.50 0.7%
Top 3 call sites: $32,936.00 (90.2% of spend).
Three call sites are 90% of the bill. Audit Narrative (generate_audit_narrative), the scary-looking Opus one, runs 37 times a day and costs $412 a month. You could delete it outright and not get 2% of the way to the target.

With Frugal's cost context in hand, Claude's recommendation was short: work on the top two first, since together they're 69% of the bill; treat the third as a design question for later; leave the bottom four ($2,247 a month between them) alone. That last part matters as much as the first. Four call sites that nobody needs to open, test, or argue about in code review.
For comparison, I asked the same question with Frugal disconnected. Working from the code alone, Claude put four other call sites ahead of Help Answers (answer_help_question), ranking it last of the five it thought were worth working on, with the note "only worth doing if Frugal shows real volume." Help Answers is the single most expensive call site, at 38% of the bill. The coding agent was doing the best it could with the insufficient information it had. The code shows what each call sends, not how often it runs, and it said so. But a team working from that ranking would have spent its first sprint on the wrong things.
Key insight from Frugal
Three of the eight call sites are 90% of the bill, and the one that looks most expensive in the code is 1.1%. Ranking by production cost first is what kept the work on the call sites that matter.
Caching repeated questions in Help Answers
Help Answers (answer_help_question) is the most expensive call site: $13,870 a month on Sonnet 5.
def answer_help_question(question: str, locale: str = "en-US") -> HelpAnswer:
articles = search_help_articles(question, locale=locale, limit=5)
context = "\n\n".join(f"[{a.id}] {a.title}\n{a.body}" for a in articles)
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": f"Help articles:\n\n{context}\n\nQuestion: {question}"}],
)
The code is fine. It looks up the five help articles that best match the user's question and asks Sonnet to answer from them. Frugal's detail for this call site shows what that costs:
Cost: $13,870.00 · rank 1 of 8 · 38.0% of Anthropic spend
input $9,120.00 65.8% 4.56B tokens
output $4,750.00 34.2% 475.0M tokens
Volume: 1,900,000 calls (63,333/day)
Frugal SDK metrics
frugal.gen_ai.duplicate_requests repeat_seen 1,216,000 of 1,900,000 (64%) · window 3600s
frugal.gen_ai.noise_ratio p50 0.03 · p95 0.06
frugal.gen_ai.output_cap_utilization p50 0.24 · p95 0.52
frugal.gen_ai.cache_prefix_stable system 100% · user 36%
frugal.gen_ai.duplicate_requests is measured on every call. The SDK hashes the whole request (model, system prompt, messages, parameters) and checks whether it has seen that exact request in the last hour. For this call site, 64% of requests are byte-for-byte repeats of one sent less than an hour earlier. The code takes free text, so there's no way to know that from reading it. Maybe the help widget suggests common questions, maybe everyone hits the same onboarding snag. The data doesn't tell us why but does tell us what we can do about it.
Claude went through the metrics one at a time and used each to rule something in or out:
| Signal | Value | What it means |
|---|---|---|
duplicate_requests |
64% repeated within 1h | Main lever. Identical request, identical answer. |
cache_prefix_stable |
system 100%, user 36% | Prompt caching barely helps: the system prompt is too short to cache, and the articles vary. |
noise_ratio |
p50 0.03 | Articles are already clean text. Nothing to strip. |
output_cap_utilization |
p50 0.24 | Lowering max_tokens wouldn't change spend. |
The plan: a shared response cache in front of the call, keyed on a hash of the full request, with a one-hour TTL to match the window Frugal measured. Because the retrieved articles are part of the key, editing an article naturally invalidates the cached answer. A skipped call saves both its input and output tokens, so at a 64% hit rate that's about $8,900 a month. The repeat questions also get answered in milliseconds instead of three seconds.

Without Frugal, Claude's recommendation was to switch the model to Haiku. That's a reasonable instinct, but it's a quality trade you'd have to test, it saves less, and it applies the same fix to all the traffic when two-thirds of it doesn't need the model at all. It did mention a response cache, as something "only worth building if many questions repeat exactly." That's the right question. Frugal is where the answer lives.
Key insight from Frugal
64% of help questions are exact repeats of one asked in the last hour (frugal.gen_ai.duplicate_requests). Nothing in the code shows that, and it turns a response cache from a hunch into the obvious first move.
Stripping noise from Receipt Triage input
Receipt Triage (triage_inbound_email) is second: $11,440 a month on Haiku 4.5, which is already the cheapest model. Users forward receipts to an inbox address, and each email gets classified and its fields extracted.
def triage_inbound_email(message: EmailMessage) -> TriageResult:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=400,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": f"{email_headers(message)}\n\n{email_body_text(message)}"}],
)
The body comes from a helper that does the sensible thing:
def email_body_text(message: EmailMessage) -> str:
"""Body of the message, preferring the plain-text alternative."""
part = message.get_body(preferencelist=("plain", "html"))
return part.get_content() if part is not None else ""
Prefer plain text, fall back to HTML. Whether that fallback matters depends entirely on what people forward, and that isn't in the repo. Here's Frugal:
Cost: $11,440.00 · rank 2 of 8 · 31.3% of Anthropic spend
input $10,780.00 94.2% 10.78B tokens
output $660.00 5.8% 132.0M tokens
Per call: 9,800 input · 120 output tokens
Frugal SDK metrics
frugal.gen_ai.duplicate_requests repeat_seen 44,000 of 1,100,000 (4%) · window 3600s
frugal.gen_ai.noise_ratio p50 0.61 · p95 0.83
94% of the cost is input: 9,800 tokens in to get 120 tokens of JSON out. And frugal.gen_ai.noise_ratio, which measures how much of the input is HTML tags, base64 runs and long stretches of whitespace, sits at 0.61 for the median call and 0.83 at p95. Most of the receipts people forward are HTML-only emails from merchants, full of markup and inline images, so the fallback isn't the edge case. It's most of the traffic, and most of what this call site pays for is stuff the model can't use to find a merchant or a total.
With that, the plan came straight out of the data. Prompt caching is out: the only stable block is a 70-token system prompt. Changing the model is out, since it's already Haiku. Trimming output is out, at 5.8% of cost. Deduplication is real but small: 4% repeats, about $450 a month. Preprocessing is the lever. Convert HTML to text while keeping table rows together so totals stay next to their labels, drop base64 runs and data: URIs, collapse whitespace, cap the length. That's an estimated $5,000 to $7,000 a month. It also gave the change a way to check itself: after it ships, the median noise ratio should drop to near zero and input tokens per call should fall from 9,800 to somewhere around 3,000 to 4,000.

Without Frugal, Claude followed the helper, noticed the HTML fallback and suggested the same kind of cleanup. But it could only offer it as a hypothesis: raw HTML is "typically 5–20× as many tokens as the visible text," and "this is based on reading the code, not on usage data." Step one of its plan was to go measure the call site in Frugal. That's the honest version of working without the data. You get a plausible idea and a to-do item to find out whether it's worth doing.
Key insight from Frugal
94% of this call site's cost is input, and 61% of that input is markup, base64 and whitespace (frugal.gen_ai.noise_ratio). That made preprocessing the lever and put a dollar figure on it before any code changed.
Moving Transaction Categorization to code first
Transaction Categorization (categorize_transaction) is third: $7,626 a month. It runs on every card transaction as it posts.
Cost: $7,626.00 · rank 3 of 8 · 20.9% of Anthropic spend
input $6,324.00 82.9% 6.32B tokens
output $1,302.00 17.1% 260.4M tokens
Volume: 6,200,000 calls (206,667/day)
Per call: 1,020 input · 42 output tokens · $0.00123/call
Frugal SDK metrics
frugal.gen_ai.duplicate_requests repeat_seen 124,000 of 6,200,000 (2%) · window 3600s
frugal.gen_ai.noise_ratio p50 0.01 · p95 0.02
frugal.gen_ai.output_cap_utilization p50 0.31 · p95 0.39
frugal.gen_ai.cache_prefix_stable system 100% · user 0%
Monthly cost: 2026-04 $6,268 · 2026-05 $6,519 · 2026-06 $6,779 · 2026-07 $7,051 · 2026-08 $7,333 · last 30d $7,626
This is the one where nothing is wrong. Only 2% of requests repeat, the input is clean, the output is a few dozen tokens, and it already runs on the cheapest model. The system prompt is identical on every call, but at about 1,000 tokens it's under Haiku's minimum cacheable length, so prompt caching would do nothing. Every quick fix is ruled out by the data, which is useful in itself, because it stops you from spending a week on one.
Claude's first idea was a good one that also came straight from the metrics: since roughly 940 of the 1,020 input tokens are that identical system prompt, send transactions in small groups per company so the prompt is paid once per group. That's about $5,000 a month, in exchange for a delay of up to half a minute before a category appears.
But look at it from a budget point of view. $7,600 a month, growing about 4% a month, is over $100,000 over the next year on one function that assigns expense categories. That's enough to justify a real piece of engineering. So I asked for one:
$7.6k a month, growing 4% a month, is over $90k a year on this one call site, with nothing wasteful to fix. That justifies a real engineering investment. I'd rather refactor categorization so deterministic code does most of the work and the model only handles what genuinely needs judgement. Use the fixture to work out how far that can go and what share of calls would still need the model.
The repo has an eval fixture of 4,000 categorized production transactions. Claude replayed it in date order, learning only from transactions that had already posted, and measured how much each deterministic layer could decide. The biggest layer uses the merchant category code (MCC), the four-digit code card networks attach to every merchant to say what kind of business it is, such as an airline, a hotel or a restaurant:

Each deterministic layer agreed with the final category at least 99.3% of the time.
The model keeps the cases that need judgement. Is dinner at a steakhouse a travel meal, a team dinner or client entertainment? That depends on the memo and the context, and that's exactly where a model earns its fee. A charge from a merchant coded as an airline is an airline ticket.
Using Frugal's per-call cost, Claude projected the call site going from $7,626 a month to about $920 in the central case, or about $2,400 if it turns out conservative, with most transactions categorized in milliseconds instead of most of a second. It was also candid about the weak spots in its own numbers: the fixture labels are mostly the model's own past answers, the memos in the fixture are tidier than real ones, and companies with custom rules weren't covered. So the plan starts with shadow mode, running the rules alongside the model and logging disagreements, and turns layers on one at a time while Frugal watches the call volume drop.
This is the kind of change no cost trap will ever flag, because there's nothing to flag. It takes an engineer deciding it's worth doing, and a number big enough to justify it.
Key insight from Frugal
Every waste signal is flat (2% repeats, 1% noise, 42 output tokens per call), but the call site costs $7,626 a month and grows 4% a month. The data rules out the quick fixes and makes the case for a real refactor.
What the data did
Put the four together and a pattern shows up. The code told Claude what each call site does. Frugal told it what that costs, how often it runs, and what the traffic looks like:
- Where to look: three of eight call sites were 90% of the bill, and the scariest-looking one was nearly free.
- What to do: a 64% repeat rate turned "maybe cache some answers?" into the obvious first move. A 0.61 noise ratio made preprocessing the lever, and sized it.
- What not to do: prompt caching, model swaps and output trimming were ruled out call site by call site, before anyone wrote them.
- Whether it's worth it: $7,600 a month with no waste is still a business case for a refactor.
- How to tell if it worked: every plan ended with the Frugal metric that should move once the change ships.
Cost work competes for the same engineers
If the right changes are this clear, why wait for finance to ask? Mostly because each one takes engineering time, and while coding agents are making changes faster to write, they do the same for everything else competing for that week. Someone still has to judge whether a one-hour cache is acceptable for Help Answers, or when the categorization rules have run in shadow mode long enough to trust. Production cost data is what lets this work compete on its merits. It steers effort away from changes that were never going to move the bill, and it puts a number on the ones that will: about $9,000 a month for the cache, $6,000 for the email cleanup and $6,700 for the refactor.
Not every cost problem is a bug. Some are just software doing its job at a price nobody planned for. Engineers and their coding agents can still bring those costs down, as long as they have the cost and usage data to point them at the right problem.
Here I am talking about it and how the new and improved Frugal MCP brings the data to your ChatGPT, Copilot, Cursor or Claude: https://www.youtube.com/watch?v=IrRaJiZQM1I
Looking for help with cost optimizations like these? Book a Demo to see Frugal in action.