DEV Community

loyaldash
loyaldash

Posted on

How I Cut Our AI API Bill by 95% — A CTO's 2026 Playbook

Look, how I Cut Our AI API Bill by 95% — A CTO's 2026 Playbook

I'll be honest with you — I almost killed my last startup over an API bill.

It was Q3 2025, and our LLM-powered analytics tool was finally gaining traction. We had landed two mid-market customers, usage was climbing, and I felt pretty good about the runway. Then I opened our infrastructure dashboard on a Monday morning and saw the number: $11,400. For one week.

I closed my laptop, made coffee, and sat with it for a while. That single weekly bill wiped out our monthly burn buffer. I had built a "production-ready" product without ever seriously asking the architecture question that should have come first: what does this cost at scale?

Three months later, after a complete teardown and rebuild of how we approach LLM costs, our weekly bill sits at $620. Same product, same customers, more usage. That's a 95% reduction. Here's exactly how we got there, including the code we use in production every day.

Why This Matters More Than You Think

Most engineering teams I talk to are running their LLM stack the way I was running mine: pick the most convenient frontier model, route everything through it, hope the bill doesn't get weird. It works in the early days because nobody has enough usage to notice. Then it doesn't.

The dirty secret is that frontier models like GPT-4o at $10/M output tokens are priced for occasional use, not for being the default backbone of a SaaS product. If you build your architecture around them, you've already lost the cost game before you've started.

My mental model now: every LLM call is a procurement decision. Every decision has an ROI angle. And every dependency is a vendor lock-in risk I want to manage.

The Architecture-First Reframe

Instead of thinking about "which optimizations should I add," I now think about cost as a primary architectural concern, alongside latency, reliability, and developer experience. That shift changes everything.

Our current production stack has seven layers of cost defense. Each one is independently valuable, but they compound. Here's the order I'd build them, and why.

1. Routing and Tiered Fallbacks (The Biggest Win)

The single highest-ROI change we made was routing. The concept is simple: don't let one model answer every request. Use a cheap model first, and only escalate when you genuinely need the smarter one.

In practice, this looks like a tiered waterfall:

from openai import OpenAI

client = OpenAI(
    base_url="https://global-apis.com/v1",
    api_key=os.environ["GLOBAL_API_KEY"]
)

def routed_generate(prompt: str, budget_tier: str = "economy"):
    """Three-tier routing. 80%+ of requests never leave tier 1."""

    cheap_resp = client.chat.completions.create(
        model="Qwen/Qwen3-8B",
        messages=[{"role": "user", "content": prompt}]
    )
    if quality_score(cheap_resp.choices[0].message.content) >= 0.8:
        return cheap_resp.choices[0].message.content

    # Tier 2 — $0.25/M tokens
    mid_resp = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=[{"role": "user", "content": prompt}]
    )
    if quality_score(mid_resp.choices[0].message.content) >= 0.9:
        return mid_resp.choices[0].message.content

    # Tier 3 — $2.50/M tokens, only when reasoning is genuinely required
    premium = client.chat.completions.create(
        model="deepseek-reasoner",
        messages=[{"role": "user", "content": prompt}]
    )
    return premium.choices[0].message.content
Enter fullscreen mode Exit fullscreen mode

In our actual workload, about 83% of requests terminate at the first tier. Another 12% get handled at the second tier. Only 5% ever hit the premium reasoning model. That's the lever that took us from $11,400/week to roughly $1,800/week, even before any of the other strategies below.

The reason this works is that most "AI" features are not actually frontier-model problems. Classifying intent, summarizing a few paragraphs, extracting structured fields, translating standard text — none of that needs a $10/M model. But routing architecture is meaningless if your provider can't serve the full menu. That's why we route everything through a single endpoint that exposes all the models we need, instead of juggling seven vendor SDKs and seven billing relationships.

2. Model Selection by Task Type (90% on Day One)

Routing is about handling variance within a workload. Model selection is about picking the right default for each kind of job.

Here's the table I wish someone had handed me six months earlier:

Job Type Default (Expensive) What We Use Savings
Simple chat GPT-4o ($10/M) DeepSeek V4 Flash ($0.25/M) 97.5%
Classification GPT-4o-mini ($0.60/M) Qwen3-8B ($0.01/M) 98.3%
Code generation GPT-4o ($10/M) DeepSeek Coder ($0.25/M) 97.5%
Summarization GPT-4o ($10/M) Qwen3-32B ($0.28/M) 97.2%
Translation GPT-4o ($10/M) Qwen-MT-Turbo ($0.30/M) 97%

The thing I want to emphasize is that this isn't a downgrade in user experience. For most of these task types, the smaller specialized models are genuinely better. Qwen-MT-Turbo was trained for translation. DeepSeek Coder was trained for code. They outperform GPT-4o on their target tasks at a fraction of the cost. We've been measuring this with blind evaluations on our own datasets.

When you set this up in code, treat the model as a config decision per task, not a per-call decision:

TASK_MODEL_MAP = {
    "chat": "deepseek-v4-flash",
    "code": "deepseek-coder",
    "classify": "Qwen/Qwen3-8B",
    "summarize": "Qwen/Qwen3-32B",
    "translate": "qwen-mt-turbo",
    "reason": "deepseek-reasoner",
}

def run_task(task: str, user_input: str):
    model = TASK_MODEL_MAP[task]
    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": user_input}]
    )
    return resp.choices[0].message.content
Enter fullscreen mode Exit fullscreen mode

This is also where vendor lock-in avoidance pays off. Because all of these models are served through one unified API, switching a default is a config change, not a re-architecture. When Qwen3-8B got cheaper, or when a new model launched, I could A/B test it in a day.

3. Caching, Because Most Queries Aren't Unique

Here's a number that surprised me: in our production traffic, 47% of requests are cache hits. Not because we built some clever semantic cache, but because real users ask the same questions, hit the same edge cases, and trigger the same fallback paths over and over.

A simple exact-match cache is the cheapest optimization you'll ever deploy:

import hashlib, json, time

_cache = {}

def cached_chat(model: str, messages: list, ttl: int = 3600):
    key = hashlib.md5(
        json.dumps({"model": model, "messages": messages}, sort_keys=True).encode()
    ).hexdigest()

    entry = _cache.get(key)
    if entry and time.time() - entry["ts"] < ttl:
        return entry["response"]  # zero tokens billed

    resp = client.chat.completions.create(model=model, messages=messages)
    _cache[key] = {"response": resp, "ts": time.time()}
    return resp
Enter fullscreen mode Exit fullscreen mode

For our support chatbot, this pushed the cache hit rate to 78% on common FAQ traffic. Combined with the tiered routing from earlier, that chatbot went from $420/month to $28/month — a 93% reduction with no quality regression I could detect.

If you want to go further, semantic caching (cache by embedding similarity rather than exact string match) buys you another 5-15% on top. But start with exact match. It's a one-hour implementation and the ROI is immediate.

4. Prompt Compression (The Hidden Multiplier)

This is the one most teams forget, because it doesn't feel like an optimization. It feels like an inconvenience. But at scale, every token you can chop off your input compounds.

The math on a real example: we had a 2,000-token system prompt for a doc-analysis feature. Compressing it to 400 tokens saves roughly $0.024 per request on DeepSeek V4 Flash. We run about 10,000 of those requests a day. That's $240/day, or $87,600 a year, on a single feature, from a prompt refactor.

Our implementation just uses a cheap model to summarize context before it goes into the expensive call:

def compress(text: str, target_chars: int) -> str:
    if len(text) < 500:
        return text

    summary = client.chat.completions.create(
        model="Qwen/Qwen3-8B",  # $0.01/M — we use the cheapest possible model
        messages=[{
            "role": "user",
            "content": f"Summarize this in under {target_chars} chars, preserving key facts:\n\n{text}"
        }]
    )
    return summary.choices[0].message.content
Enter fullscreen mode Exit fullscreen mode

Yes, the compression call itself costs money. But $0.01/M is so cheap that it's a rounding error next to the savings on the downstream call.

The other 80/20 here is just discipline. Strip whitespace. Remove examples that aren't load-bearing. Kill "please" and "could you" from system prompts. We've seen 15-30% input token reduction from a single afternoon of prompt hygiene.

5. Batch Processing for Predictable Workloads

If you have any workload where latency isn't critical — overnight report generation, batch enrichment, bulk classification — batching is a 10-20% win almost for free.

# Before: 50 separate calls
for item in items:
    client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=[{"role": "user", "content": item}]
    )

# After: 1 batch call
batch_prompt = "\n---\n".join(f"[{i}] {item}" for i, item in enumerate(items))
batch_prompt += "\n\nReturn one answer per item in order, separated by ---"

resp = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[{"role": "user", "content": batch_prompt}]
)
Enter fullscreen mode Exit fullscreen mode

You pay input tokens once for the shared prefix, the model amortizes attention across items, and you cut your network overhead. For our weekly digest job (about 1,200 items per run), this saved us about 14% on what was already a cheap call.

6. The Vendor Lock-in Question

I want to call this out separately because nobody in our space talks about it.

If your entire LLM stack is built on a single provider's SDK, a single provider's auth, and a single provider's model IDs, you have a strategic problem. That provider can raise prices tomorrow, deprecate a model you depend on, or have a multi-day outage. Any of these can break your business in ways that have nothing to do with engineering quality.

The way I think about this: my LLM provider should be swappable in a week, not a quarter. That means:

  • One base URL, not seven SDKs
  • OpenAI-compatible interface (every serious provider supports this now)
  • Model names as config, not hardcoded
  • All prompts and routing logic in code I own

This is a big part of why I route everything through a unified endpoint that exposes every model I care about. The abstraction layer is the moat against vendor risk. I'm not picking one model and praying — I'm running an internal marketplace.

7. Measurement (The Layer That Makes Everything Else Work)

You cannot optimise what you don't measure. We log, per request:

  • Model used
  • Tokens in / tokens out
  • Latency
  • Quality score
  • Cost in dollars (calculated from current pricing)

We review this dashboard weekly. Twice we've caught a model silently raising prices in a quarterly update; once we caught a routing bug sending 8% of traffic to premium when it shouldn't have. Without the dashboard, those problems would have lived for months.

What I'd Build Differently If I Started Today

If I were starting a new AI product today, I'd build all seven of these layers before the first paying customer. Not because the cost matters at zero revenue, but because the architectural decisions compound. Building routing in from day one is two days of work. Retrofitting it after you've shipped a single-model monolith is a quarter-long migration that nobody on your team wants to own.

The other thing I'd do differently: I'd stop treating model selection as an engineering decision and start treating it as a product decision. Your model's cost is your product's gross margin. The cheaper your inference stack, the more aggressive you can be on pricing, the longer your runway, the more you can spend on growth.

The Numbers, One More

Top comments (0)