Skip to main content
Lesson 10 of 20 · Production, Evals, and Optimization

Optimizing Costs at Scale

Lesson 10 of 20 Updated July 28, 2026 Advanced
On this page
  1. Cost as an Engineering Constraint
  2. Knowing Where the Money Goes
  3. Four Levers for Reduction
  4. Getting Warned Before the Bill
  5. Key Takeaways

Cost as an Engineering Constraint

A poorly optimized prompt costing $0.002 per call is negligible during development. At one million calls, it costs $2,000—and it will keep costing you that every month until someone rewrites it. This shift in scale turns cost into an engineering constraint: you don’t solve it by negotiating a contract, you solve it by changing code.

Visibility is the prerequisite for any optimization. A monthly bill tells you how much you spent, not where. That’s why this lesson starts with instrumentation before covering reduction levers.

Knowing Where the Money Goes

Four elements determine the cost of a call, and they don’t behave the same way. Input tokens, which aggregate the system prompt, conversation history, and user message, grow with session duration. Output tokens—the generated response—are billed significantly higher than input. Cached input tokens receive roughly a 50% discount off the input rate, making their tracking essential to understanding any bill. Finally, the model itself: GPT-5.6 Luna is far cheaper than GPT-5.6 Sol, and this factor dominates all others.

Tracking means converting every usage object returned by the API into a dollar amount, applying the correct rate, and isolating the cached portion.

import openai
from dataclasses import dataclass, field

client = openai.OpenAI()

# Rates per million tokens (July 2026) — cached_input is indicative (~50% of input),
# adjust according to the official pricing grid
RATES = {
    "gpt-5.6-luna": {"input": 0.20, "output": 1.20, "cached_input": 0.02},
    "gpt-5.6-terra": {"input": 2.00, "output": 12.00, "cached_input": 0.20},
    "gpt-5.6-sol": {"input": 5.00, "output": 30.00, "cached_input": 0.50},
}

@dataclass
class CostTracker:
    """Tracks costs in real time."""
    calls: list[dict] = field(default_factory=list)

    def record(self, model: str, usage: dict) -> float:
        input_tokens = usage.input_tokens
        cached_tokens = getattr(
            usage.input_tokens_details, "cached_tokens", 0
        )
        output_tokens = usage.output_tokens
        uncached_input_tokens = input_tokens - cached_tokens

        rate = RATES.get(model, RATES["gpt-5.6-terra"])
        cost = (
            (uncached_input_tokens / 1_000_000) * rate["input"]
            + (cached_tokens / 1_000_000) * rate["cached_input"]
            + (output_tokens / 1_000_000) * rate["output"]
        )

        self.calls.append({
            "model": model,
            "input_tokens": input_tokens,
            "cached_tokens": cached_tokens,
            "output_tokens": output_tokens,
            "cost": cost,
        })
        return cost

    @property
    def total_cost(self) -> float:
        return sum(a["cost"] for a in self.calls)

    def report(self) -> str:
        if not self.calls:
            return "No calls recorded."
        return (
            f"Calls: {len(self.calls)} | "
            f"Total cost: ${self.total_cost:.4f} | "
            f"Average cost: ${self.total_cost / len(self.calls):.6f}"
        )

tracker = CostTracker()

The RATES table requires constant attention: it becomes outdated as soon as the official pricing grid changes. Treat it as configuration data to verify, not as a program constant, and make average cost per call a metric you review weekly.

Four Levers for Reduction

The first lever, and by far the most profitable, is avoiding paying for a high-end model when the task doesn’t require it. Explicit routing based on complexity prevents the default drift where all traffic ends up on the most expensive model simply because it appeared in the first copied example.

async def economic_router(prompt: str, complexity: str) -> str:
    """Routes to the cheapest capable model."""
    if complexity == "simple":
        model = "gpt-5.6-luna"
    elif complexity == "medium":
        model = "gpt-5.6-terra"
    else:
        model = "gpt-5.6-sol"

    response = client.responses.create(model=model, input=prompt)
    tracker.record(model, response.usage)
    return response.output_text

The second lever goes further when complexity isn’t known in advance. Instead of classifying the request, the cascade tries the cheapest model first and escalates only if the response fails a quality check. The check used here is deliberately rudimentary—a minimum length and the absence of an ignorance admission—and this is the point you’ll need to refine: a cascade that escalates too often becomes more expensive than a direct call to the right model.

async def model_cascade(prompt: str) -> str:
    """Tries the cheapest model first."""
    models = ["gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol"]

    for model in models:
        response = client.responses.create(
            model=model,
            input=prompt,
            max_output_tokens=500,
        )
        tracker.record(model, response.usage)

        # Check quality (simple heuristic)
        text = response.output_text
        if len(text) > 50 and "i don't know" not in text.lower():
            return text

    return text  # Last result even if imperfect

The third lever targets input tokens, which grow silently in any conversational application: by the twentieth turn, you’re repaying for the previous nineteen with every message. Compressing history by keeping the first message (which holds context) and the last (which holds the question), then filling the remaining budget with the most recent exchanges, preserves essential coherence at a fraction of the cost.

def compress_history(
    messages: list[dict],
    max_tokens: int = 4000,
) -> list[dict]:
    """Keeps the most recent messages within budget."""
    # Always keep the first (context) and last (question)
    if len(messages) <= 2:
        return messages

    first = messages[0]
    last = messages[-1]
    middle = messages[1:-1]

    # Estimate tokens (approximation: 1 token ≈ 4 characters)
    fixed_tokens = (len(first["content"]) + len(last["content"])) // 4
    middle_budget = max_tokens - fixed_tokens

    # Keep the most recent messages
    result = []
    tokens_used = 0
    for msg in reversed(middle):
        msg_tokens = len(msg["content"]) // 4
        if tokens_used + msg_tokens > middle_budget:
            break
        result.insert(0, msg)
        tokens_used += msg_tokens

    return [first] + result + [last]

The fourth lever requires no algorithmic finesse, only discipline: the Batch API offers a 50% reduction, and you just need to identify what can wait. Let the task itself carry urgency from its creation, rather than deducing it afterward—this is the only way to prevent everything from defaulting to the real-time queue.

def classify_urgency(tasks: list[dict]) -> dict:
    """Separates tasks into real-time vs batch."""
    real_time = []
    batch = []

    for task in tasks:
        if task.get("urgency") == "immediate":
            real_time.append(task)
        else:
            batch.append(task)

    return {
        "real_time": real_time,
        "batch": batch,
        "estimated_savings": len(batch) * 0.001,  # Example
    }

Getting Warned Before the Bill

All these optimizations can be undone by a single unlucky deployment: an unbounded retry loop, a tripled system prompt, a model changed by mistake. A cost alert is the safety net that warns you within hours rather than at month-end. Note the intermediate threshold at 80%: it turns a brutal alarm into an early signal, giving you time to act before exceeding the limit.

class CostAlert:
    """Triggers alerts when costs exceed a threshold."""

    def __init__(self, daily_threshold: float = 50.0):
        self.threshold = daily_threshold
        self.tracker = CostTracker()

    def check(self) -> str | None:
        if self.tracker.total_cost > self.threshold:
            return (
                f"ALERT: daily cost ${self.tracker.total_cost:.2f} "
                f"exceeds threshold of ${self.threshold:.2f}"
            )
        if self.tracker.total_cost > self.threshold * 0.8:
            return (
                f"WARNING: cost at {self.tracker.total_cost / self.threshold:.0%} "
                f"of daily threshold"
            )
        return None

Key Takeaways

  • Track your costs in real time using response.usage
  • Route to the cheapest model capable of the task
  • Use the Batch API (-50%) for anything not requiring real-time processing
  • Reduce input tokens by compressing history
  • Set up cost alerts to avoid surprises