How I Cut My AI API Costs by 95% — An Indie Hacker's Guide
ok so real talk — last month I opened my OpenAI bill and almost spit out my coffee. I was burning through a small fortune every week, and I had no idea where the money was going. Like, I literally thought I was being smart by just "using AI for stuff." Turns out I was basically lighting cash on fire.
heres the thing nobody tells you when you start building with AI APIs: the default models everyone reaches for are insanely expensive for like 90% of the things you're actually doing. And the worst part? You dont even notice because the responses look fine. You only notice when the invoice arrives.
I spent the last few weeks tearing apart my entire setup, swapping models, adding caches, compressing prompts, all of it. And honestly, I gotta say, the results were kind of ridiculous. I went from spending roughly $420/month down to about $28/month. Same product, same quality, just smarter choices.
This is me sharing what I learned, the actual code I use, and the dumb mistakes I made along the way. If youre an indie hacker or solo dev shipping AI features, pull up a chair.
First, The Uncomfortable Truth About Model Pricing
pretty much every dev I know defaults to GPT-4o. Including me, until like three weeks ago. And yeah, GPT-4o is GREAT. Its also $10/M output tokens. Which sounds cheap until you realize a "moderate" chatbot does millions of tokens a month.
The problem isnt GPT-4o itself. The problem is using it for things like... classifying whether a user message is a refund request. Or summarizing a paragraph. Or translating "hello" to Spanish. Thats like hiring a Michelin-star chef to make you toast.
Heres the table I wish someone had shoved in my face six months ago:
| Task | Expensive Choice | Smart Choice | 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% |
Look at that classification row. $0.60/M vs $0.01/M. SIXTY TIMES cheaper. For what is, lets be honest, a trivial task that a tiny model can crush.
I switched my classifier over in about twenty minutes. Saved like $80 the first week.
Strategy 1: Stop Being Lazy About Model Selection
This is the biggest lever. Like, ALL of the other strategies combined dont move the needle as much as just picking a cheaper model for the right task.
Heres what I do now. I keep a little map in my code:
MODEL_MAP = {
"chat": "deepseek-v4-flash", # $0.25/M
"code": "deepseek-coder", # $0.25/M
"simple": "Qwen/Qwen3-8B", # $0.01/M
"reasoning": "deepseek-reasoner", # $2.50/M
}
Then before I send anything to a model, I run a quick classifier on the prompt itself to figure out what kind of task it is. Routing simple stuff to Qwen3-8B, sending real reasoning work to deepseek-reasoner, and using deepseek-v4-flash for general chat.
You know what the wild part is? Users literally cannot tell the difference for most queries. I A/B tested this on my own product for two weeks. Completion rates were within 1% of each other.
heres how it looks in practice using Global API (which is what I switched to, more on that later):
import requests
API_BASE = "https://global-apis.com/v1"
def chat(user_input, task_type):
model = MODEL_MAP[task_type]
resp = requests.post(
f"{API_BASE}/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": model,
"messages": [{"role": "user", "content": user_input}]
}
)
return resp.json()
Thats it. Thats the whole strategy. And it accounts for the bulk of my savings.
Strategy 2: Cascade Routing — The Cheap Stuff First
ok this one is genuinely fun to build. The idea is: dont assume every request needs your best model. Try the cheap one first, check if the response is good enough, and ONLY escalate if it isnt.
I run a customer support bot for a niche SaaS I operate, and maybe 80% of incoming messages are literally the same five questions asked in slightly different ways. "How do I reset my password." "Where do I find my API key." "Can I get a refund." Stuff like that.
So why in the world would I send those to a $2.50/M reasoning model? I wouldnt. Heres my actual routing function:
def smart_generate(prompt, max_budget=0.50):
"""Try cheap first, escalate if quality insufficient"""
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)
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)
return call_model("deepseek-reasoner", prompt) # 5% of requests
The quality_check function is its own rabbit hole — I use a tiny embedding model to compare the response against a couple of "good answer" examples. Works surprisingly well for the obvious stuff.
That support bot? Used to cost $420/month. Now costs $28. Same uptime, same customer satisfaction scores (I checked). The math on that is honestly hilarious.
Strategy 3: Cache Everything That Moves
This one is the most "duh" strategy in the list, but its wild how many people skip it. If a user asks the same question twice, youre paying for it twice. Why?
I added a simple MD5-based cache and it was like free money:
import hashlib, json, 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 = requests.post(
f"{API_BASE}/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"model": model, "messages": messages}
).json()
cache[key] = {"response": response, "time": time.time()}
return response
For FAQ bots and documentation lookup features, cache hit rates of 50-80% are totally normal. That means HALF your traffic is free.
I even cache semantically similar queries using embeddings now, but thats a whole separate post. Start with exact-match first, its already a huge win.
Strategy 4: Stop Sending Wall-of-Text Prompts
I used to have these massive system prompts. Like, thousands of tokens of "you are a helpful assistant that..." boilerplate. And every single request would include the whole thing.
Then I ran the numbers and nearly cried.
A 2,000-token system prompt costs real money. At DeepSeek V4 Flash rates ($0.25/M input), thats like $0.0005 per request. Tiny, right? But at 10,000 requests a day? Thats $5/day JUST for the system prompt. $150/month. For words.
So I started compressing:
def compress_prompt(text, target_ratio=0.5):
"""Compress long prompts before sending"""
if len(text) < 500:
return text # Already short
# Use a cheap model to summarize the context
summary = call_model("Qwen/Qwen3-8B",
f"Summarize this in {int(len(text)*target_ratio)} chars: {text}"
)
return summary
The original article pointed out that compressing a 2,000-token prompt down to 400 tokens saves $0.024/request. At 10K requests/day thats $240/day, or $87,600/year. Thats not a typo. Eighty-seven thousand dollars. For just ONE optimization.
I run my system prompts through the compressor once at startup, cache the result, and never pay the full price again.
Strategy 5: Batch Your Stuff Together
This one is so simple it feels like cheating. Instead of sending 100 separate requests, send 1 request with 100 items in it.
The original article showed this pattern:
# Before: 3 separate calls (3× input tokens)
for question in questions:
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": question}]
)
# After: 1 batch call (shared context)
You save on overhead tokens, you save on connection time, and most models handle batched inputs really well. I use this for things like processing customer feedback in bulk, generating SEO descriptions for a list of pages, summarizing a list of articles — anywhere Im doing the same task on a list.
Honestly, I gotta say, this one alone saved me about 15% on my monthly bill once I started doing it consistently.
Strategy 6: Set Realistic max_tokens (The Free Win)
heres something I overlooked for WAY too long. Most models have a default max_tokens setting thats higher than what you actually need. If youre classifying a message as "refund" or "not refund", you dont need 4,000 tokens of output. You need like 5.
I set per-task max_tokens limits and the output cost just... fell off a cliff.
For classification: max_tokens=10
For chat replies: max_tokens=500
For code generation: max_tokens=2000
Sounds trivial. Adds up fast when youre doing thousands of requests.
Strategy 7: Fine-Tune a Small Model for YOUR Specific Task
This is the more advanced move and I dont do it for everything, but for my highest-volume task (intent classification for the support bot), I fine-tuned a small Qwen model on about 500 examples of past support tickets.
Cost to fine-tune: like $5 one-time
Top comments (0)