DEV Community

fiercedash
fiercedash

Posted on

I Cut My AI API Bill 95% From Scratch: What Nobody Tells You

I Cut My AI API Bill 95% From Scratch: What Nobody Tells You

okay so real talk — i was burning cash on AI APIs like it was going out of style. like literally watching my Stripe dashboard go "oops" every monday morning. and the WORST part? i didn't even realize how badly i was getting hosed until i sat down and actually did the math.

heres the thing nobody tells you when you start building with LLMs: the cost difference between using the "good" model and the "good enough" model is honestly insane. we're talking 40x to 1000x differences in price for tasks that produce nearly identical outputs.

i've spent the last few months rebuilding my stack to be way smarter about this. cut my monthly AI bill from like $1,400 down to under $80. NOT by sacrificing quality. not by switching to some sketchy offshore provider. just by being smarter about which model i call, when i call it, and how often.

let me walk you through exactly what i did. honestly, i gotta say, this stuff is pretty much free money if you're not already doing it.


Why I Was Burning Cash (And Probably You Too)

most indie hackers i talk to have the exact same problem. you start a project, you grab GPT-4o because its the default, you wire everything up, you ship, and then a month later you check your usage and have a small heart attack.

thats basically what happened to me. i built a customer support chatbot for my SaaS thing. nothing crazy. maybe 30,000 conversations a month. GPT-4o for everything because i didn't want to think about it. my bill? $420/month just for that one feature.

honestly i felt kinda dumb when i realized how fixable this was. pretty much all of these optimizations took me a weekend to implement. i'm not joking — like two days of work to cut my bill by 93%.


Strategy 1: Stop Using The Expensive Model For Everything

this is the BIG one. this is where like 90% of your savings come from. seriously. just stop reaching for the shiny model every single time.

heres the mental model i use now: think of models like employees. you wouldn't hire a senior architect to sweep the floor right? same logic. GPT-4o is your architect. Qwen3-8B is your intern. sometimes you need the architect. most of the time the intern is fine.

heres the pricing reality that blew my mind when i first saw it laid out:

  • Simple chat stuff? GPT-4o costs $10/M output tokens. DeepSeek V4 Flash? $0.25/M. thats a 97.5% reduction for what is, frankly, the same answer in 90% of cases.
  • Classification tasks? GPT-4o-mini is $0.60/M. Qwen3-8B is $0.01/M. like 60x cheaper.
  • Code generation? GPT-4o at $10/M vs DeepSeek Coder at $0.25/M. same deal.
  • Summarization? GPT-4o at $10/M vs Qwen3-32B at $0.28/M. yep.
  • Translation? GPT-4o at $10/M vs Qwen-MT-Turbo at $0.30/M. still huge.

i had a whole table in my notes app before i started coding. like literally i sat down and asked "for this specific task, whats the dumbest model that can still do it well?" and most of the time the answer was something wildly cheap.

heres the routing logic i use now. its not fancy but it works:

from openai import OpenAI

client = 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 pick_model(user_input):
    text = user_input.lower()
    if any(word in text for word in ["code", "function", "debug", "implement"]):
        return MODEL_MAP["code"]
    if any(word in text for word in ["prove", "analyze deeply", "step by step reasoning"]):
        return MODEL_MAP["reasoning"]
    if len(text) < 200:  # short stuff
        return MODEL_MAP["simple"]
    return MODEL_MAP["chat"]

user_input = "whats the weather like in tokyo?"
model = pick_model(user_input)

response = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": user_input}]
)
print(response.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

thats it. thats literally it. you route based on what the task actually needs and you save a stupid amount of money.


Strategy 2: The Tiered Escalation Pattern

okay so strategy 1 is "pick the right model upfront." strategy 2 is "start with the cheapest model and only escalate if you have to."

honestly, this is my favorite pattern. i call it the "intern first, escalate to senior" pattern. and i use it EVERYWHERE now.

heres the flow: try the ultra-cheap model first. check if the output is good enough. if yes, ship it. if no, try the mid-tier. if thats not good enough either, THEN bring out the big guns.

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

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

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

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

heres the real result from my chatbot. before: $420/month. after i implemented this with routing 85% of queries through Qwen3-8B: $28/month. thats a 93% reduction. and my user satisfaction scores? literally unchanged.

the trick is the quality check function. you need some way to know if the response is good enough. for me, i use a combination of:

  • length checks (if its suspiciously short, probably wrong)
  • a tiny classifier that scores relevance
  • for high-stakes stuff, i literally just sample 5% of "cheap" responses and have a human verify them weekly

Strategy 3: Caching Is Free Money

i cannot stress this enough. caching is FREE MONEY. if you're not caching identical or near-identical requests you're leaving savings on the table like its christmas morning.

think about it. in any real app you have a long tail of repeated questions. FAQ stuff. "how do i reset my password." "what are your hours." "where is the pricing page." these get asked HUNDREDS of times. and every single time you're paying to generate the same answer.

heres a simple implementation that took me like 20 minutes:

import hashlib
import json
import time

cache = {}

def cached_chat(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

real numbers from my app: FAQ-style queries get a 50-80% cache hit rate. thats HUGE. like basically half my bill just disappeared when i added this.

for the fancy version, look into semantic caching — where you cache based on MEANING not exact text. so "whats your refund policy" and "how do i get a refund" both hit the same cache entry. but honestly, even the dumb hash-based version above pays for itself in like an hour.

i'm using Redis for mine but honestly even a dict in memory works for smaller apps.


Strategy 4: Compress Your Prompts

this one is sneaky. you might not realize how much you're spending on input tokens because, like, the focus is always on output. but if you're stuffing massive system prompts or huge context into every request, you're paying for it EVERY SINGLE TIME.

example: i had a 2,000 token system prompt. it had product info, persona instructions, examples, all sorts of stuff. some of it necessary. a lot of it NOT necessary for every single query.

so i wrote a compressor. it uses the cheapest model (Qwen3-8B at $0.01/M) to summarize long context before sending it to the bigger model:

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

    # Use a cheap model to summarize the context
    summary = client.chat.completions.create(
        model="Qwen/Qwen3-8B",
        messages=[{
            "role": "user",
            "content": f"Summarize this in {int(len(text)*target_ratio)} chars, keep key facts: {text}"
        }]
    )
    return summary.choices[0].message.content
Enter fullscreen mode Exit fullscreen mode

the math here is wild. a 2,000-token system prompt compressed to 400 tokens saves $0.024 per request on DeepSeek V4 Flash. sounds small right? WRONG. at 10,000 requests per day thats $240/day. thats $87,600/year. per request savings of 2.4 cents. PER. REQUEST.

i went through my codebase and found like 6 places where i was shipping massive prompts that didn't need to be massive. consolidated them, compressed them, and boom. savings everywhere.


Strategy 5: Batch Your Requests

okay this one is obvious in hindsight but i was NOT doing it. batching = instead of making 10 separate API calls, you make ONE call with all 10 requests bundled together.

the cost savings come from the fact that your system prompt / context overhead gets paid ONCE instead of 10 times. if your system prompt is 500 tokens, you save 4,500 tokens per batch of 10. thats not nothing.

heres the before/after in my own code:

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

# After: 1 batch call (shared system prompt, single overhead)
combined = "\n".join([f"{i+1}. {q}" for i, q in enumerate(questions)])
response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[
        {"role": "system", "content": "Answer each numbered question. Format: 1. <answer>\n2. <answer>"},
        {"role": "user", "content": combined}
    ]
)
# Then parse out individual answers
Enter fullscreen mode Exit fullscreen mode

savings on this are typically 10-20%. not the biggest win on this list but its basically free to implement so why not.

where it gets interesting is when you batch across USERS. like, instead of processing each user's request the second it comes in, you collect them for a few seconds and batch them together. trade-off is latency. but for non-realtime stuff? huge win.


Strategy 6: Set Spending Limits Or Go Broke Trying

okay this isn't really a cost optimization strategy but honestly i gotta mention it because it saved my ass more than once. set hard spending limits on your API account. like ACTUAL limits that cut things off if you exceed them.

why? because LLMs are sneaky. one bad prompt with a runaway agent and suddenly you've spent $500 in an hour. i've heard horror stories. don't be a horror story.

i use Global API for most of my stuff now and they have spending caps built into the dashboard which is genuinely nice. set my monthly limit to like $200 and if anything weird happens, it just stops. peace of mind is worth a lot.

also: set up alerts. anything over $X in a day should ping your phone. anything over $Y in an hour should DEFINITELY ping your phone. paranoia is healthy in this space.


Strategy 7: Audit Regularly Because Drift Is Real

okay last one. and this is more of a meta-strategy but its important. you gotta audit your usage regularly.

Top comments (0)