DEV Community

purecast
purecast

Posted on

I Cut My AI API Bill by 95% — Let Me Show You Exactly How

I Cut My AI API Bill by 95% — Let Me Show You Exactly How

Let me paint you a picture. Six months ago, I was staring at my team's API bill and honestly feeling a little sick. We were burning through cash on GPT-4o for everything — every little classification task, every summary, every code snippet. It was the path of least resistance. OpenAI's API is just so easy to call, right? But "easy" doesn't mean "cheap." And when I finally did the math on what we were spending versus what we needed to spend, I realized we'd been leaving a giant pile of money on the table.

That moment kicked off months of tinkering, benchmarking, and arguing with my co-founder about whether we really "needed" the premium model for every single request. The good news? We eventually got our bill down by over 95%. And none of the techniques were exotic. They were just… obvious in hindsight. So let me save you the months I spent figuring this out. Here's how I'd do it all over again if I were starting from scratch.

The Awful Truth About AI API Costs

Here's the thing nobody warns you about when you first integrate an LLM into your product: the difference between picking the "right" model and picking the "convenient" model can be 40x or more. I'm not exaggerating. Look at this comparison I keep taped to my monitor:

If you're running a simple chat with GPT-4o at $10/M output tokens, you could swap in DeepSeek V4 Flash at $0.25/M. That's a 97.5% reduction for many tasks. Need a classifier? GPT-4o-mini runs $0.60/M, but Qwen3-8B does it for $0.01/M — a 98.3% drop. Translation with Qwen-MT-Turbo? $0.30/M instead of $10/M.

I keep a little cheat sheet in a Slack channel I call #money-saved. It's embarrassingly motivating.

Pick the Right Model for the Job (the 90% Lever)

The single biggest win you'll find comes from this: stop using one model for everything. I used to default to GPT-4o for literally every call because I didn't want to think about it. That was a $400/month mistake before I even knew it.

Here's how I think about model selection now:

  • Trivial stuff (parsing, yes/no questions, simple Q&A): ultra-cheap tier
  • Normal chat, summaries, light reasoning: standard tier
  • Code generation, multi-step reasoning: premium tier (but only when needed)

Let me show you how to wire this up. Here's a tiny routing table I keep in almost every project:

import openai

client = openai.OpenAI(
    api_key="YOUR_GLOBAL_API_KEY",
    base_url="https://global-apis.com/v1"
)

MODEL_MAP = {
    "chat": "deepseek-v4-flash",          # $0.25/M output
    "code": "deepseek-coder",          # $0.25/M output
    "simple": "Qwen/Qwen3-8B",         # $0.01/M output
    "reasoning": "deepseek-reasoner",   # $2.50/M output
}

def route_to_model(user_input):
    task = classify_complexity(user_input)
    return MODEL_MAP[task]

model = route_to_model("What's the capital of France?")
response = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": "What's the capital of France?"}]
)
print(response.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

Notice the base URL is global-apis.com/v1? That's the trick. One API key, dozens of models. I don't have to juggle five different accounts. Worth checking out if you're routing between providers.

The Tiered Routing Pattern: Like Escalation, But Cheaper

After model selection, this is my favorite technique. It's based on the observation that most LLM tasks don't actually need the smartest model. They just need a model that's "good enough." So why not try cheap first, and only escalate if you really need to?

Picture a funnel. Cheap model at the top. If the answer looks good, ship it. If not, escalate up one tier. If still no good, escalate again.

def smart_generate(prompt, max_budget=0.50):
    """Try cheap first, escalate only if quality is insufficient"""

    # Tier 1: Ultra-budget at $0.01/M
    resp = call_model("Qwen/Qwen3-8B", prompt)
    if quality_check(resp) >= 0.8:
        return resp  # ~80% of requests handled here

    # Tier 2: Standard at $0.25/M
    resp = call_model("deepseek-v4-flash", prompt)
    if quality_check(resp) >= 0.9:
        return resp  # ~15% of requests

    # Tier 3: Premium at $0.78-$2.50/M
    return call_model("deepseek-reasoner", prompt)  # ~5% of requests
Enter fullscreen mode Exit fullscreen mode

The numbers in the comments are realistic — that's how traffic typically distributes once you set this up. I worked with a small SaaS company on this exact pattern last quarter. They ran a customer support chatbot that was costing them $420/month because every query hit GPT-4o. We added tiered routing, pushed 85% of their queries through Qwen3-8B at $0.01/M, and their bill dropped to $28/month. Same product. Same users. Just smarter routing.

The hardest part is building the quality_check function. Mine usually looks for things like response length, presence of refusal phrases, or a simple heuristic like "does it actually answer the question." You can also use a tiny model as a judge. Whatever floats your boat.

Don't Pay Twice for the Same Answer (Caching)

Here's something that wasn't obvious to me at first: a surprising number of LLM calls are duplicates. FAQ bots, documentation lookups, repeated greetings — all of these have natural cache hit rates of 50% to 80%. I didn't believe it until I instrumented it. The numbers don't lie.

Caching is one of those techniques that feels like cheating because it's so simple. Hash the input, check the cache, return the cached response if it's still fresh. That's it. Here's the pattern I use:

import hashlib, json, time

cache = {}

def cached_chat(client, model, messages, ttl=3600):
    key = hashlib.md5(
        json.dumps({"model": model, "messages": messages}).encode()
    ).hexdigest()

    if key in cache:
        entry = cache[key]
        if time.time() - entry["time"] < ttl:
            return entry["response"]  # Cache hit — $0 cost

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

In production I usually swap this for Redis — way more reliable than an in-process dict — but the pattern doesn't change. Honestly, I've seen caching alone cut bills by 20% to 50% in production systems. There's no reason to skip it.

Compress Your Prompts (15-30% Per Request)

This one took me longer to appreciate. Every token you send costs money. And a lot of prompts are bloated with repeated instructions, lengthy examples, or stack traces that could be a fraction of the size.

Let me give you a real example. We had a system prompt that was about 2,000 tokens long. It was full of context, examples, persona details — all the usual stuff. After compression, it was 400 tokens. Same effective behavior. Saved $0.024 per request on DeepSeek V4 Flash. Sounds tiny, but we were processing 10,000 requests a day. That's $240 per day, or roughly $87,600 per year.

You don't even need a fancy algorithm. Just use a cheap model to summarize your own context:

def compress_prompt(text, target_ratio=0.5):
    """Compress long prompts before sending"""
    if len(text) < 500:
        return text  # Already short

    summary = call_model("Qwen/Qwen3-8B",
        f"Summarize this in {int(len(text)*target_ratio)} chars: {text}"
    )
    return summary
Enter fullscreen mode Exit fullscreen mode

Run this offline, cache the compressed prompt, and serve the compressed version in production. You'll see 15% to 30% savings on input tokens, and honestly the model responses don't change much.

Batch When You Can (10-20%)

The last trick in my toolbox isn't as glamorous, but it adds up. If you've got ten questions that don't depend on each other, batch them into a single call instead of firing off ten round-trips. You save on overhead, you cut token usage, and many providers actually price batch calls lower.

Here's the before-and-after that finally sold a skeptical teammate of mine:

# Before: 3 separate calls (3x the overhead, 3x the latency)
for question in questions:
    response = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=[{"role": "user", "content": question}]
    )

# After: 1 batch call, structured response
batch_prompt = "\n".join(
    f"{i+1}. {q}" for i, q in enumerate(questions)
)
response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[{
        "role": "user",
        "content": f"Answer each numbered question:\n{batch_prompt}\n\nFormat: 1. ... 2. ... 3. ..."
    }]
)
Enter fullscreen mode Exit fullscreen mode

The savings here vary wildly — I've seen 10% on simple workloads, 20% on heavier ones. The bigger win is often latency, but costs follow.

Putting It All Together

Here's the mental model I use. Whenever I'm about to ship a feature that calls an LLM, I run through this checklist:

  1. Can a cheap model handle this? Use Qwen3-8B at $0.01/M whenever possible.
  2. Can I add tiered routing? Cheap first, premium only when needed.
  3. Can I cache the response? Especially for repeated queries.
  4. Is the prompt as small as possible? Compress long context.
  5. Can I batch multiple requests? Combine where dependencies allow.

When you stack all five, the savings compound. Model selection alone usually gets you 90%. Tiered routing on top of that pushes you to 95%. Throw in caching and prompt compression and you're suddenly operating at a fraction of what you were spending before.

Honestly, my proudest moment was when my co-founder asked "are we sure we're not over-spending on AI?" and I could confidently point at our cost dashboard. We've got a Grafana panel for it now. Very nerdy. Very satisfying.

Where Global API Fits In

If you're juggling multiple providers — and after reading this, maybe you are — give Global API a look. It's the routing layer I keep mentioning, and it's kind of perfect for this kind of multi-model setup. One API key, one base URL (global-apis.com/v1), access to everything from the cheap Qwen models up to the deepseek-reasoner for the gnarly stuff. I switched over a few months back when I got tired of managing separate accounts and keys for every model. It just makes the whole "tiered routing" approach easier to actually implement.

Not affiliated, not getting paid to say that — I just keep finding myself recommending it to other devs who are in the same boat I was in. If you want to try one routing pattern and access all of these models through a single endpoint, it's worth checking out.

Final Thoughts

Here's what I'd say to anyone reading this who's about to dismiss it as "we already do model selection, kind of": you probably don't. I thought I did too. I was using GPT-4o-mini as my "cheap" option until I realized Qwen3-8B at $0.01/M was 60x cheaper and handled 80% of my classification tasks identically. The gap between "I picked a not-expensive model" and "I picked the cheapest model that actually works" is where the real money lives.

Start with one strategy. I'd begin with the model selection table — it's the highest leverage change. Then layer on caching once you're comfortable. By the third technique, you'll be hooked on the savings dashboard. Trust me on that one.

Go make your AI bill boring again

Top comments (0)