AI Cost Optimization: Cutting Your LLM Bill by 70%
Back to all articles
Cost Optimization
16 min read11 min read

AI Cost Optimization: Cutting Your LLM Bill by 70%

Practical strategies to reduce AI costs without sacrificing quality. Covers caching, batching, model selection, and prompt optimization.

Debasish Maji
Debasish Maji
AI Engineering Lead
April 26, 2026
Cost OptimizationLLMInfrastructureEfficiency

The Cost Crisis

Your AI prototype cost $50/month. Your production system costs $50,000/month. What happened?

LLM costs scale faster than most teams expect. Without optimization, AI can become economically unsustainable. This post documents how we cut our LLM bill by 70% while maintaining quality.

•••

Understanding Your Costs

Cost Breakdown

ComponentTypical ShareOptimization Potential
LLM API calls60-80%High
Embedding generation10-20%High
Vector database5-10%Medium
Compute/infrastructure5-15%Medium

Cost Drivers

DriverImpactControllable
Model choice10-100x differenceYes
Prompt lengthLinear with tokensYes
Output lengthLinear with tokensPartially
Request volumeLinearPartially
Retry rateMultiplierYes

Cost Per Request Analysis

ModelInput (1K tokens)Output (1K tokens)Typical Request
GPT-4$0.03$0.06$0.15
GPT-3.5$0.0005$0.0015$0.003
Claude Opus$0.015$0.075$0.12
Claude Haiku$0.00025$0.00125$0.002
Llama 70B (self-hosted)~$0.001~$0.002$0.005
•••

Strategy 1: Intelligent Model Routing

The Routing Approach

Not every query needs GPT-4. Route based on complexity:

Query TypePercentageModelCost Impact
Simple50%GPT-3.5/Haiku98% savings
Medium35%Claude Sonnet70% savings
Complex15%GPT-4/OpusBaseline
Diagram
flowchart LR A[User Query] --> B{Complexity<br/>Classifier} B -->|Simple| C[GPT-3.5<br/>$0.002] B -->|Medium| D[Claude Sonnet<br/>$0.015] B -->|Complex| E[GPT-4<br/>$0.12] C --> F[Response] D --> F E --> F style C fill:#22c55e,color:#fff style D fill:#f59e0b,color:#fff style E fill:#ef4444,color:#fff

Router Implementation

ApproachAccuracyLatencyCost
Keyword rules70%0msFree
Small classifier85%10msMinimal
LLM classifier95%200msLow
Hybrid92%15msMinimal
Python
from enum import Enum
from typing import Tuple

class QueryComplexity(Enum):
    SIMPLE = "simple"
    MEDIUM = "medium"
    COMPLEX = "complex"

class ModelRouter:
    def __init__(self):
        self.models = {
            QueryComplexity.SIMPLE: "gpt-3.5-turbo",
            QueryComplexity.MEDIUM: "claude-3-sonnet",
            QueryComplexity.COMPLEX: "gpt-4-turbo",
        }
        
        # Keywords that indicate complexity
        self.complex_indicators = [
            "analyze", "compare", "synthesize", "evaluate",
            "code review", "architecture", "design system"
        ]
        self.simple_indicators = [
            "what is", "define", "list", "when was",
            "how many", "yes or no"
        ]
    
    def classify(self, query: str) -> Tuple[QueryComplexity, str]:
        query_lower = query.lower()
        
        # Rule-based fast path
        if any(ind in query_lower for ind in self.simple_indicators):
            if len(query) < 100:
                return QueryComplexity.SIMPLE, self.models[QueryComplexity.SIMPLE]
        
        if any(ind in query_lower for ind in self.complex_indicators):
            return QueryComplexity.COMPLEX, self.models[QueryComplexity.COMPLEX]
        
        # Fallback: Use token count as heuristic
        token_estimate = len(query.split())
        if token_estimate < 20:
            return QueryComplexity.SIMPLE, self.models[QueryComplexity.SIMPLE]
        elif token_estimate < 100:
            return QueryComplexity.MEDIUM, self.models[QueryComplexity.MEDIUM]
        else:
            return QueryComplexity.COMPLEX, self.models[QueryComplexity.COMPLEX]
    
    async def route_and_call(self, query: str, messages: list) -> dict:
        complexity, model = self.classify(query)
        
        response = await self.llm_client.chat(
            model=model,
            messages=messages
        )
        
        # Track for analytics
        self.metrics.record_routing(complexity, model, response.usage)
        
        return {
            "response": response.content,
            "model_used": model,
            "complexity": complexity.value,
            "cost": self.calculate_cost(model, response.usage)
        }

Routing Results

MetricBefore RoutingAfter RoutingImprovement
Average cost/request$0.12$0.03571% reduction
Quality score92%91%-1% (acceptable)
p95 latency5.2s3.1s40% faster
•••

Strategy 2: Aggressive Caching

What to Cache

ContentCache StrategyHit RateSavings
Identical queriesExact match15-25%100% per hit
Similar queriesSemantic (0.95+)25-40%100% per hit
EmbeddingsPermanent90%+100% per hit
Partial resultsComponent cache30-50%50-80% per hit
Diagram
flowchart TB A[User Query] --> B{Exact Match<br/>Cache?} B -->|Hit| C[Return Cached] B -->|Miss| D{Semantic<br/>Cache?} D -->|Similar Found| E{Similarity<br/>Above 0.95?} D -->|No Match| F[Call LLM] E -->|Yes| C E -->|No| F F --> G[Store Response] G --> H[Store Embedding] H --> I[Return Response] style C fill:#22c55e,color:#fff style F fill:#f59e0b,color:#fff

Semantic Caching

Similarity ThresholdHit RateQuality Risk
0.9910%None
0.9725%Minimal
0.9540%Low
0.9060%Medium
Python
import hashlib
import numpy as np
from typing import Optional, Tuple

class SemanticCache:
    def __init__(self, redis_client, vector_db, embedding_model):
        self.redis = redis_client
        self.vector_db = vector_db
        self.embedder = embedding_model
        self.similarity_threshold = 0.95
        self.ttl_seconds = 86400  # 24 hours
    
    async def get(self, query: str) -> Optional[Tuple[str, float]]:
        # Layer 1: Exact match (fastest)
        cache_key = self._hash_query(query)
        exact_hit = await self.redis.get(cache_key)
        if exact_hit:
            return exact_hit, 1.0
        
        # Layer 2: Semantic match (slower but more hits)
        query_embedding = await self.embedder.embed(query)
        
        results = await self.vector_db.search(
            vector=query_embedding,
            top_k=1,
            include_metadata=True
        )
        
        if results and results[0].score >= self.similarity_threshold:
            cached_response = results[0].metadata["response"]
            # Warm exact cache for next time
            await self.redis.setex(cache_key, self.ttl_seconds, cached_response)
            return cached_response, results[0].score
        
        return None
    
    async def set(self, query: str, response: str):
        # Store in exact cache
        cache_key = self._hash_query(query)
        await self.redis.setex(cache_key, self.ttl_seconds, response)
        
        # Store in semantic cache
        query_embedding = await self.embedder.embed(query)
        await self.vector_db.upsert(
            id=cache_key,
            vector=query_embedding,
            metadata={"query": query, "response": response}
        )
    
    def _hash_query(self, query: str) -> str:
        normalized = query.lower().strip()
        return hashlib.sha256(normalized.encode()).hexdigest()[:16]


# Usage in API endpoint
cache = SemanticCache(redis, pinecone, openai_embedder)

async def chat_with_cache(query: str) -> dict:
    # Check cache first
    cached = await cache.get(query)
    if cached:
        response, similarity = cached
        return {
            "response": response,
            "cached": True,
            "similarity": similarity,
            "cost": 0
        }
    
    # Cache miss - call LLM
    response = await llm.chat(query)
    
    # Store for future
    await cache.set(query, response.content)
    
    return {
        "response": response.content,
        "cached": False,
        "cost": response.usage.total_cost
    }

Cache Architecture

LayerTTLStorageCost
In-memory5 minRedis$50/month
Semantic24 hoursVector DB$100/month
Persistent7 daysPostgreSQL$50/month

Caching Results

MetricBeforeAfterImprovement
Cache hit rate0%42%-
API calls100K/day58K/day42% reduction
Monthly cost$15,000$8,70042% savings
•••

Strategy 3: Prompt Optimization

Token Reduction Techniques

TechniqueToken SavingsQuality Impact
Remove redundant instructions10-20%None
Shorten examples20-30%Minimal
Use abbreviations in system prompt5-10%None
Dynamic context inclusion30-50%None
Compression prompts40-60%Low risk

Before and After

Prompt ComponentBeforeAfterSavings
System prompt800 tokens400 tokens50%
Examples1200 tokens600 tokens50%
Context2000 tokens1000 tokens50%
Total4000 tokens2000 tokens50%

Output Control

TechniqueImplementationSavings
Max tokensHard limitPrevents runaway
Concise instructions"Be brief"20-30%
Structured outputJSON schema10-20%
Stop sequencesEarly terminationVariable
•••

Strategy 4: Batch Processing

When to Batch

ScenarioBatch?Benefit
Real-time chatNoLatency critical
Document processingYes20-30% savings
AnalyticsYes30-40% savings
Nightly jobsYesOff-peak pricing

Batch Efficiency

Batch SizeThroughputCost per Item
1BaselineBaseline
108x80% of baseline
5030x70% of baseline
10050x65% of baseline
•••

Strategy 5: Self-Hosting Economics

Break-Even Analysis

Monthly VolumeAPI CostSelf-Hosted CostWinner
10M tokens$500$2,500API
50M tokens$2,500$2,500Tie
100M tokens$5,000$2,500Self-hosted
500M tokens$25,000$3,000Self-hosted

Self-Hosting Costs

ComponentMonthly CostNotes
GPU instance (A100)$2,000-3,000Primary cost
Storage$100-200Model weights
Networking$50-100Egress
Operations$500-1,000Engineering time

Hybrid Approach

WorkloadDeploymentReasoning
High volume, simpleSelf-hostedCost efficiency
Low volume, complexAPICapability
Spiky trafficAPIElasticity
Sensitive dataSelf-hostedPrivacy
•••

Strategy 6: Request Optimization

Reducing Unnecessary Requests

OptimizationSavingsImplementation
Debouncing20-40%Wait for user pause
Deduplication10-20%Hash recent requests
Prefetching5-10%Predict next query
Cancellation10-15%Cancel abandoned

Retry Optimization

IssueBad PracticeGood PracticeSavings
Rate limitsImmediate retryExponential backoff30%
TimeoutsFull retryResume/checkpoint50%
ErrorsRetry same modelFallback model20%
•••

Strategy 7: Architecture Patterns

Cost-Efficient Architectures

PatternDescriptionSavings
Cache-firstCheck cache before API30-50%
Tiered processingSimple to complex escalation40-60%
Lazy evaluationGenerate on demand20-30%
Pre-computationOffline processingVariable

Tiered Processing Flow

StepActionCost
1Check cacheFree
2Try small model$0.002
3Validate quality$0.001
4Escalate if needed$0.10
Average-$0.025
•••

Implementation Roadmap

Quick Wins (Week 1)

ActionEffortSavings
Add caching layerLow20-30%
Optimize promptsLow10-20%
Set output limitsLow5-10%

Medium Term (Month 1)

ActionEffortSavings
Implement routingMedium30-50%
Semantic cachingMedium10-20%
Batch processingMedium10-15%

Long Term (Quarter 1)

ActionEffortSavings
Self-hosting evaluationHigh30-50%
Custom fine-tuningHigh20-40%
Architecture redesignHighVariable
•••

Monitoring Costs

Key Metrics

MetricTargetAlert
Cost per requestTrack trend20% increase
Cache hit rateOver 30%Under 20%
Routing accuracyOver 90%Under 85%
Token efficiencyImprovingDegrading

Cost Attribution

DimensionWhy Track
By featureIdentify expensive features
By user segmentUnderstand usage patterns
By modelOptimize routing
By timeIdentify spikes
•••

Key Takeaways

  1. 1Model routing is the biggest lever - Using the right model for each query can save 60-70%.
  1. 2Caching compounds - Exact + semantic caching can eliminate 40%+ of API calls.
  1. 3Prompts are money - Every token costs. Optimize ruthlessly.
  1. 4Self-hosting makes sense at scale - Above 50M tokens/month, run the numbers.
  1. 5Measure everything - You cannot optimize what you do not measure.
  1. 6Quality trade-offs exist - 1-2% quality reduction for 70% cost reduction is often worth it.

Cost optimization is not a one-time project. It is an ongoing discipline. Build cost awareness into your development process from day one.

Found this helpful?

Share it with others who might benefit

TweetShare

Related articles