Part of Language AI Handbook
Covers LLM cost management with token-based cost modeling, optimization techniques like caching and quantization, cost allocation.
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Cost Management
Deploying language models at scale is expensive. A single GPU instance hosting a large model can cost several dollars per hour, and a production system serving thousands of users may require dozens of such instances. Without deliberate cost management, teams often discover that inference costs dwarf development expenses, and the economics of an otherwise promising application collapse under real-world load.
Cost management for LLM deployments covers four interconnected concerns. Cost modeling builds a quantitative understanding of where money goes and what drives spending. Cost optimization reduces spending through architectural choices, hardware selection, and inference techniques. Cost allocation answers the question of which users, teams, or features are responsible for which costs. And cost monitoring creates the visibility needed to detect anomalies, enforce budgets, and continuously improve.
These concerns do not exist in isolation. A meaningful cost optimization initiative requires first understanding the cost model. Effective allocation requires instrumentation that also enables monitoring. This chapter walks through each area in depth, building from the fundamentals of LLM economics to practical techniques you can apply to real production systems. It builds on the inference optimization techniques covered in earlier chapters, including KV caching, quantization, continuous batching, and speculative decoding. Those techniques reduce the per-request cost; this chapter shows how to measure, attribute, and manage that cost systematically.
Cost Modeling
Understanding what drives LLM serving costs is the foundation of effective cost management. Without a clear cost model, optimization efforts are guesswork and budget forecasting is unreliable. A good cost model makes visible the relationship between workload characteristics, infrastructure choices, and spending.
The Economics of LLM Inference
LLM inference costs are fundamentally driven by three resources: compute, memory, and network. Of these, compute and memory are the primary cost drivers for most deployments.
Compute is consumed during the forward pass of the model. For each token generated, the model must perform a matrix multiplication through every layer. The cost is roughly proportional to the number of parameters in the model and the number of tokens processed. This is why larger models cost more to run and why longer conversations are more expensive than short ones.
Memory is consumed by two distinct components. The first is static: the model weights. A 7 billion parameter model in 16-bit precision requires approximately 14 GB of GPU memory just to store the weights, before any request is processed. This fixed overhead means that memory cost must be amortized across requests. The second component is dynamic: the KV cache. As sequences grow longer, the KV cache grows proportionally, consuming memory that could otherwise accommodate additional concurrent requests.
Network costs include both data transfer fees (egress charges from cloud providers) and the cost of communication in distributed multi-GPU deployments. For most deployments, network costs are secondary to compute and memory, but they become significant for high-volume streaming applications or systems with frequent model updates.
The cost of a single inference request can be decomposed into a prefill component and a decode component. The prefill phase, which processes the input prompt, is compute-bound: it performs dense matrix multiplications across all prompt tokens simultaneously. The decode phase, which generates output tokens one at a time, is memory-bandwidth-bound: it reads the full model weights and KV cache for each generated token. This distinction matters for cost modeling because the two phases have different cost characteristics and respond to different optimization strategies.
Token-Based Cost Model
The most useful framework for thinking about LLM costs is a token-based model. Rather than reasoning about GPU-hours directly, we can express cost in terms of input tokens and output tokens, since these are the quantities that applications control.
The cost per token depends on the model architecture, the hardware it runs on, and the infrastructure configuration. We can derive this from first principles:
where:
- : cost per output token in dollars
- : hourly infrastructure cost in dollars (GPU instance cost, networking, storage)
- : tokens generated per hour at the observed throughput
This formula highlights the fundamental lever in cost optimization: to reduce cost per token, you must either reduce (cheaper infrastructure) or increase (higher throughput). Every optimization technique in the inference stack, from quantization to continuous batching, aims to increase throughput for a given hardware budget.
For a production deployment, we can expand this into a total cost model that accounts for the mixed nature of real workloads:
where:
- : total number of requests
- : cost per input token
- : cost per output token
- : average input token length per request
- : average output token length per request
Output tokens are typically more expensive than input tokens because they require sequential decode steps while input tokens can be processed in parallel. Commercial API providers often charge two to four times more per output token than per input token, showing this asymmetry.
Hardware Cost Components
When running your own infrastructure rather than using API providers, the cost model must account for the full stack of infrastructure costs:
- GPU instances: The dominant cost driver, typically accounting for 70-85% of total infrastructure spend. Cloud GPU pricing varies widely by GPU generation, region, and commitment type.
- Reserved vs. on-demand pricing: Reserved instances typically offer 40-70% discounts over on-demand pricing but require upfront commitment and reduce flexibility.
- Spot/preemptible instances: Can reduce costs by 60-80% but introduce interruption risk that must be managed through redundancy and checkpointing.
- Storage costs: Model weights, logs, and cached outputs. At a few cents per GB-month, storage is usually minor unless you store large volumes of request/response data.
- Networking costs: Egress charges from cloud providers for streaming responses to users. At typical token throughputs, this can become meaningful for high-volume deployments.
- Operational overhead: Engineering time for provisioning, monitoring, and maintaining the serving infrastructure. This is often underestimated and can exceed infrastructure costs for small teams.
Worked Example: Building a Cost Model
Let's build a concrete cost model for a hypothetical production deployment to see how these pieces fit together.
# Cost model for a production LLM deployment
# Infrastructure costs
gpu_hourly_cost_usd = 3.50 # A100 80GB on-demand pricing (approximate)
num_gpu_instances = 4 # Replicas for availability and throughput
total_hourly_cost = gpu_hourly_cost_usd * num_gpu_instances
# Workload parameters
avg_input_tokens = 512 # Average prompt length in tokens
avg_output_tokens = 256 # Average response length in tokens
peak_requests_per_hour = 10_000 # Request volume at peak
daily_requests = 150_000 # Total daily requests
# Throughput characteristics (from benchmarking)
throughput_tokens_per_second = (
1_800 # Total output tokens/sec across all instances
)
throughput_per_hour = throughput_tokens_per_second * 3600
# Derived cost metrics
cost_per_output_token = total_hourly_cost / throughput_per_hour
# Input tokens cost less (processed in parallel), use 0.3x multiplier as heuristic
cost_per_input_token = cost_per_output_token * 0.3
# Per-request cost
cost_per_request = (
cost_per_input_token * avg_input_tokens
+ cost_per_output_token * avg_output_tokens
)
# Daily cost
daily_cost = daily_requests * cost_per_request
monthly_cost = daily_cost * 30
# Cost breakdown
input_cost_fraction = (
cost_per_input_token * avg_input_tokens
) / cost_per_request
output_cost_fraction = (
cost_per_output_token * avg_output_tokens
) / cost_per_request=== LLM Deployment Cost Model === Infrastructure: GPU instances: 4 x $3.50/hr = $14.00/hr Total system throughput: 1,800 tokens/sec Per-token costs: Input tokens: $0.0006 per 1K tokens Output tokens: $0.0022 per 1K tokens Per-request cost: $0.0009 Input fraction: 37.5% (512 tokens) Output fraction: 62.5% (256 tokens) Projected costs: Daily (150,000 requests): $132.74 Monthly: $3,982
The cost breakdown reveals that output tokens dominate the request cost even though the input is twice as long. This is the key insight from the prefill/decode asymmetry: generating text is more expensive than reading it. Applications that can reduce output length, or that use output caching to serve repeated responses, achieve outsized cost savings.
GPU Utilization and Its Impact on Cost
A critical concept in LLM cost modeling is GPU utilization. Unlike CPU-based systems where utilization correlates directly with cost efficiency, GPU utilization for LLM inference requires careful interpretation.
Compute utilization measures what fraction of the GPU's arithmetic units are active. During the prefill phase, utilization can approach 80-90% as the GPU performs dense matrix multiplications. During the decode phase, utilization often drops to 10-30% because token-by-token generation is memory-bandwidth-bound, not compute-bound. A low compute utilization during decode is not necessarily inefficient; it reflects the fundamental nature of autoregressive generation.
Memory utilization measures GPU memory occupancy. High memory utilization is generally desirable because it means more concurrent requests can be served. However, utilization above 95% risks out-of-memory errors during sudden load spikes. A target of 85-90% memory utilization is typical for production systems.
Effective GPU utilization for cost purposes is better measured as request throughput divided by the theoretical maximum throughput. This captures the revenue-generating work the GPU is doing relative to its capacity.
# GPU utilization cost impact
scenarios = {
"Low utilization (30%)": {
"utilization": 0.30,
"note": "Typical for lightly loaded dev env",
},
"Medium utilization (60%)": {
"utilization": 0.60,
"note": "Acceptable for many deployments",
},
"High utilization (85%)": {
"utilization": 0.85,
"note": "Target for production efficiency",
},
"Peak utilization (95%)": {
"utilization": 0.95,
"note": "Maximum sustainable, risk of OOM",
},
}
# At 100% utilization, we assume max_throughput tokens/sec
max_throughput_per_gpu = (
450 # tokens/sec per A100 at full utilization (example)
)
fixed_hourly_cost_per_gpu = (
gpu_hourly_cost_usd # cost is the same regardless of utilization
)
for name, params in scenarios.items():
util = params["utilization"]
effective_throughput = max_throughput_per_gpu * util
effective_tph = effective_throughput * 3600
cpt = fixed_hourly_cost_per_gpu / effective_tph
params["effective_tph"] = effective_tph
params["cost_per_1k_tokens"] = cpt * 1000GPU Utilization Impact on Cost: Scenario Tokens/hr Cost/1K tokens ------------------------------------------------------------ Low utilization (30%) 486,000 $ 0.0072 Medium utilization (60%) 972,000 $ 0.0036 High utilization (85%) 1,377,000 $ 0.0025 Peak utilization (95%) 1,539,000 $ 0.0023
The relationship is direct: doubling GPU utilization halves the cost per token. This is why techniques that improve throughput, such as continuous batching and request queuing, are so economically valuable.

Cost Optimization
Cost optimization for LLM deployments draws on two categories of techniques: those that reduce the compute required per token (efficiency techniques) and those that avoid computation entirely (caching and routing techniques). The inference optimization chapters earlier in this handbook covered many of the underlying mechanisms; this section focuses on how to select and combine them for maximum cost impact.
Model Selection and Right-Sizing
The most impactful cost decision is model selection. Larger models are not always better for a given task, and the cost difference between model sizes can be enormous. A 7B parameter model costs roughly one-tenth as much to run as a 70B parameter model, while often achieving comparable quality for many practical tasks.
The right approach is to establish a quality baseline for your specific task, then find the smallest model that meets it. This process involves:
-
Define quality metrics: Establish task-specific evaluation criteria. For a customer support application, this might be response accuracy on a held-out test set of real queries. For code generation, it might be pass rate on a unit test suite.
-
Benchmark across model sizes: Test a range of models from small (3-7B) to large (70B+), including quantized variants of larger models. Quantization, covered in the inference optimization part of this handbook, can allow a 4-bit quantized 70B model to run on the same hardware as a 16-bit 13B model while achieving better quality.
-
Include latency in the trade-off: A smaller model that answers in 200ms may be preferable to a larger one that answers in 800ms even if the larger model is marginally more accurate, because users tolerate latency poorly.
-
Consider task-specific fine-tuning: A fine-tuned 7B model often outperforms a general-purpose 70B model on a specific narrow task. Fine-tuning adds upfront cost but can dramatically reduce ongoing serving cost.
Quantization for Cost Reduction
Quantization reduces model weight precision from 16-bit floating point to lower-precision formats (INT8, INT4), shrinking memory footprint and enabling higher throughput. The inference optimization chapters provide detailed coverage of quantization mechanics; here we focus on the cost impact.
The cost impact of quantization comes through two channels:
Increased concurrency: Smaller model weights leave more GPU memory for KV caches, allowing more simultaneous requests. If quantization reduces model size by 50%, the memory saved can potentially double the number of concurrent requests, roughly halving the cost per request.
Higher throughput on memory-bandwidth-bound workloads: The decode phase is memory-bandwidth-bound, meaning speed depends on how fast weights can be read from memory. INT4 weights are read four times faster than FP16 weights, directly translating to higher token throughput.
The trade-off is quality degradation. INT8 quantization typically preserves quality well, with less than 1% performance degradation on most benchmarks. INT4 quantization introduces more noticeable degradation, particularly for tasks requiring precise numerical reasoning or context-sensitive language understanding. Methods like GPTQ and AWQ, covered in the inference optimization chapters, minimize quality loss through calibration-aware quantization.
Batching and Request Scheduling
Continuous batching, covered in depth in the inference optimization part of this handbook, is one of the highest-impact cost optimizations available. By keeping the GPU occupied with multiple requests simultaneously, it dramatically improves utilization and reduces cost per token.
The key insight is that batching amortizes the cost of reading model weights across multiple requests. During a single forward pass, the GPU reads each weight matrix once but applies it to all requests in the batch. A batch of 16 requests reads weights once and processes 16 requests, while 16 sequential requests each read the full weights, performing 16 times more memory reads for the same work.
Effective batching requires careful configuration:
- Maximum batch size: Larger batches improve throughput but increase individual request latency (requests must wait to form a batch). The right setting depends on your latency budget.
- Batch timeout: How long to wait for a full batch before processing a partial one. Shorter timeouts improve latency at the cost of throughput efficiency.
- Token budget per batch: Total tokens (prompt + generated) that can be in flight simultaneously. This determines KV cache memory requirements.
Caching Strategies
Caching is among the most powerful cost optimization techniques because it eliminates computation entirely for repeated content.
Prompt prefix caching exploits the fact that many requests share a common prefix: a system prompt, few-shot examples, or a document being analyzed. If the KV cache computed for the prefix is stored and reused across requests, the prefill cost for that shared prefix is paid only once. For applications where a long system prompt is sent with every request, prefix caching can eliminate 30-80% of prefill compute.
Semantic caching goes further by caching complete responses and retrieving them for semantically similar queries. Rather than exact string matching, a small embedding model encodes each request and the cache is searched by cosine similarity. When a new request is within a similarity threshold of a cached request, the cached response is returned without any model inference.
Semantic caching is most effective for FAQ-style applications where users ask variations of the same questions. It is less effective for creative or open-ended tasks where similar questions warrant different responses. The similarity threshold requires careful calibration: too low and you return wrong answers for similar-but-distinct questions; too high and cache hit rates drop to negligible levels.
Output caching stores complete responses for exact or near-exact duplicate requests. This is appropriate for batch processing pipelines where the same document or query may appear multiple times, or for read-heavy knowledge base applications.
# Model the cost savings from caching
def compute_caching_savings(
daily_requests,
avg_input_tokens,
avg_output_tokens,
cost_per_input_1k,
cost_per_output_1k,
prefix_length_tokens,
prefix_cache_hit_rate,
semantic_cache_hit_rate,
):
"""Calculate cost savings from prefix and semantic caching."""
# Baseline cost without any caching
baseline_cost = daily_requests * (
(avg_input_tokens / 1000) * cost_per_input_1k
+ (avg_output_tokens / 1000) * cost_per_output_1k
)
# With semantic caching: cached requests cost only lookup, not inference
# Uncached requests have normal cost; cached requests save both input and output cost
semantic_uncached_fraction = 1 - semantic_cache_hit_rate
semantic_cost = daily_requests * (
semantic_uncached_fraction
* (
(avg_input_tokens / 1000) * cost_per_input_1k
+ (avg_output_tokens / 1000) * cost_per_output_1k
)
# Cached requests have negligible compute cost (just embedding lookup)
)
# With prefix caching: prefix portion of input is only processed once
# Non-prefix portion still processed for every request
non_prefix_input = avg_input_tokens - prefix_length_tokens
prefix_cache_cost = daily_requests * (
# Non-prefix input always costs full price
(non_prefix_input / 1000) * cost_per_input_1k
+
# Prefix is amortized (only paid for cache misses, once per unique prefix session)
(prefix_length_tokens / 1000)
* cost_per_input_1k
* (1 - prefix_cache_hit_rate)
+
# Output always costs full price
(avg_output_tokens / 1000) * cost_per_output_1k
)
return {
"baseline": baseline_cost,
"semantic_caching": semantic_cost,
"prefix_cache_cost": prefix_cache_cost,
"semantic_savings_pct": (1 - semantic_cost / baseline_cost) * 100,
"prefix_savings_pct": (1 - prefix_cache_cost / baseline_cost) * 100,
}
results = compute_caching_savings(
daily_requests=150_000,
avg_input_tokens=512,
avg_output_tokens=256,
cost_per_input_1k=0.0015,
cost_per_output_1k=0.005,
prefix_length_tokens=300, # 300-token system prompt
prefix_cache_hit_rate=0.90, # 90% of requests reuse prefix
semantic_cache_hit_rate=0.25, # 25% of requests are semantically cached
)Daily Cost Comparison (150K requests): Baseline (no caching): $307.20 Semantic caching (25% HR): $230.40 (saves 25.0%) Prefix caching (90% HR): $246.45 (saves 19.8%)
The savings from caching compound with other optimizations. A deployment with both semantic caching and prefix caching, combined with quantization and efficient batching, can achieve costs that are a fraction of a naive deployment.
Speculative Decoding
Speculative decoding, covered in the inference optimization chapters, uses a small draft model to propose token sequences that the target model verifies in parallel. When the draft model's predictions are accepted, multiple tokens are generated in a single forward pass of the target model, increasing effective throughput without degrading output quality.
The cost benefit of speculative decoding is conditional on the acceptance rate of draft tokens. Under the illustrative model below, which charges each drafted token 5% of a target-model forward pass, an 80% acceptance rate with drafts of length 4 yields about 2.80 times the baseline throughput. Exact gains depend on the draft model, hardware, and serving runtime.
Speculative decoding works best for tasks where output is predictable and the draft model can anticipate the target model's choices. It performs well on code generation and structured output tasks but provides smaller benefits for highly creative or open-ended generation where the target model's choices are less predictable.

Hardware Selection and Cloud Strategy
Infrastructure cost includes GPU type and the full spectrum of purchasing decisions.
GPU selection should be matched to model requirements. Memory-intensive operations during the prefill phase benefit from GPUs with high HBM bandwidth, while the memory-bandwidth-bound decode phase benefits from GPUs with high memory bandwidth relative to VRAM. Newer GPU generations (H100 vs. A100) offer significantly better price-performance for inference due to improved memory bandwidth and support for FP8 computation.
Spot instances offer 60-80% cost reductions for workloads that can tolerate interruption. This is practical for batch processing pipelines where jobs can be checkpointed and resumed, but requires careful architecture for latency-sensitive serving because a spot interruption would take an instance offline mid-request.
Reserved instances provide 40-70% discounts in exchange for one- or three-year commitments. A common strategy is to reserve capacity for baseline load and use on-demand instances for peak traffic, a pattern sometimes called "base plus burst."
Multi-cloud and spot diversification reduces exposure to any single provider's spot availability. Running across multiple availability zones or providers increases the likelihood that at least some spot capacity is available during high-demand periods.
Cost Allocation
Cost allocation answers the question: who spent what? This question matters for several reasons. In multi-tenant platforms, allocation enables chargeback to business units or customers. In product development, it reveals which features or use cases are expensive relative to their value. In budget planning, it provides the data needed to forecast future spending.
Allocation Architectures
Cost allocation in LLM systems requires instrumentation at the request level. Each request must be tagged with enough metadata to attribute its cost to the appropriate owner. The specific attributes depend on the organizational context:
- User or customer ID: For consumer-facing applications, associate each request with the user who initiated it.
- Feature or product area: Tag requests by the product feature that triggered them (e.g., "autocomplete", "summarization", "chat").
- Tenant or team: In internal platforms, associate requests with the team or cost center responsible.
- Request class: Distinguish between interactive (latency-sensitive) and batch (throughput-optimized) workloads, which may be priced differently.
The cost of each request is computed from its token counts and the current cost per token for the serving tier used. Token counts can be obtained either from the tokenizer at request time or from the inference framework's output, which typically reports prompt and completion token counts.
# Cost allocation system implementation
import time
from collections import defaultdict
from dataclasses import dataclass
@dataclass
class RequestRecord:
"""A single request record for cost attribution."""
request_id: str
user_id: str
feature: str
team: str
timestamp: float
prompt_tokens: int
completion_tokens: int
model_tier: str # "small", "medium", "large"
# Cost rates by model tier (USD per 1K tokens)
TIER_COSTS = {
"small": {"input": 0.0002, "output": 0.0005}, # 7B model
"medium": {"input": 0.0010, "output": 0.0020}, # 13B model
"large": {"input": 0.0015, "output": 0.0050}, # 70B model
}
def compute_request_cost(record: RequestRecord) -> float:
"""Compute the dollar cost of a single request."""
rates = TIER_COSTS[record.model_tier]
input_cost = (record.prompt_tokens / 1000) * rates["input"]
output_cost = (record.completion_tokens / 1000) * rates["output"]
return input_cost + output_cost
def aggregate_costs(records: list) -> dict:
"""Aggregate costs by user, feature, and team."""
by_user = defaultdict(float)
by_feature = defaultdict(float)
by_team = defaultdict(float)
by_tier = defaultdict(float)
for record in records:
cost = compute_request_cost(record)
by_user[record.user_id] += cost
by_feature[record.feature] += cost
by_team[record.team] += cost
by_tier[record.model_tier] += cost
return {
"by_user": dict(by_user),
"by_feature": dict(by_feature),
"by_team": dict(by_team),
"by_tier": dict(by_tier),
"total": sum(by_user.values()),
}
# Simulate request records
import random
random.seed(42)
users = [f"user_{i:04d}" for i in range(200)]
features = ["autocomplete", "summarization", "chat", "search", "translation"]
teams = ["product_a", "product_b", "infra", "research"]
tiers = ["small", "medium", "large"]
tier_weights = [0.6, 0.3, 0.1] # Most requests use smaller, cheaper models
simulated_records = []
for i in range(5000):
prompt_len = max(50, int(random.lognormvariate(6, 0.8)))
completion_len = max(20, int(random.lognormvariate(5, 0.7)))
tier = random.choices(tiers, weights=tier_weights)[0]
simulated_records.append(
RequestRecord(
request_id=f"req_{i:06d}",
user_id=random.choice(users),
feature=random.choice(features),
team=random.choice(teams),
timestamp=time.time() - random.uniform(0, 86400),
prompt_tokens=min(prompt_len, 4096),
completion_tokens=min(completion_len, 1024),
model_tier=tier,
)
)
allocation = aggregate_costs(simulated_records)Cost Allocation Report (5,000 requests) Total cost: $2.99 By Feature: chat $0.621 (20.7%) autocomplete $0.618 (20.6%) summarization $0.610 (20.4%) search $0.606 (20.2%) translation $0.540 (18.0%) By Team: research $0.806 (26.9%) product_a $0.762 (25.4%) infra $0.714 (23.9%) product_b $0.711 (23.8%) By Model Tier: medium $1.471 (49.1%) large $0.914 (30.5%) small $0.609 (20.3%)
Showback vs. Chargeback
There are two philosophies for how to use allocation data: showback and chargeback.
With showback, cost reports are informational. Teams can see what they are spending but costs are not transferred from a central budget. Showback is simpler to implement and avoids the organizational friction of reallocating money between teams. It works well for driving awareness and encouraging efficiency without creating financial incentives that might cause teams to under-invest in AI capabilities.
With chargeback, actual costs are transferred to consuming teams' budgets. This creates direct economic incentives for efficiency: teams that over-provision or use expensive models when cheaper ones would suffice bear the cost directly. Chargeback is better at aligning incentives but requires mature internal billing infrastructure and can create perverse incentives where teams avoid beneficial AI use to minimize their chargeback.
A common approach is to start with showback to build awareness and then introduce chargeback once teams understand the cost structure and have had time to optimize.
Per-User Cost Limits and Quotas
For consumer-facing applications, per-user cost controls prevent individual users from consuming disproportionate resources. This is particularly important for applications with open-ended generation where a single user can trigger very expensive requests.
The implementation involves maintaining a running cost counter per user and enforcing limits before processing requests:
# Per-user cost limiting with token budgets
class UserCostTracker:
"""Track and enforce per-user cost limits."""
def __init__(self, daily_limit_usd: float):
self.daily_limit_usd = daily_limit_usd
self.user_costs = defaultdict(float) # user_id -> daily cost so far
self.user_request_counts = defaultdict(int)
def check_and_deduct(self, user_id: str, estimated_cost: float) -> tuple:
"""
Check if user is within budget and deduct the estimated cost.
Returns (allowed: bool, remaining_budget: float).
"""
current_spend = self.user_costs[user_id]
remaining = self.daily_limit_usd - current_spend
if estimated_cost > remaining:
return False, remaining
self.user_costs[user_id] += estimated_cost
self.user_request_counts[user_id] += 1
return True, remaining - estimated_cost
def get_usage_report(self) -> dict:
"""Return usage stats for all users who made requests."""
return {
user_id: {
"total_cost": self.user_costs[user_id],
"requests": self.user_request_counts[user_id],
"utilization_pct": self.user_costs[user_id]
/ self.daily_limit_usd
* 100,
}
for user_id in self.user_costs
}
# Simulate per-user tracking with $0.50 daily limit
tracker = UserCostTracker(daily_limit_usd=0.50)
blocked_count = 0
processed_count = 0
for record in simulated_records[:500]:
cost = compute_request_cost(record)
allowed, remaining = tracker.check_and_deduct(record.user_id, cost)
if allowed:
processed_count += 1
else:
blocked_count += 1
usage = tracker.get_usage_report()Per-User Cost Limiting Results (500 requests, $0.50 daily limit): Requests processed: 500 Requests blocked: 0 (0.0%) Unique users tracked: 181 Top 5 spenders: user_0197: $0.0090 (6 requests, 1.8% of limit) user_0173: $0.0084 (5 requests, 1.7% of limit) user_0138: $0.0076 (6 requests, 1.5% of limit) user_0086: $0.0066 (8 requests, 1.3% of limit) user_0079: $0.0066 (4 requests, 1.3% of limit)

Cost Attribution in Multi-Tenant Systems
In platforms that serve multiple organizations (multi-tenant SaaS), cost attribution requires additional infrastructure:
Request tagging: Each API request must carry a tenant identifier, which is propagated through the inference pipeline and logged alongside resource usage metrics.
Tenant-level aggregation: Usage data must be aggregated per tenant at regular intervals (hourly or daily) for billing and reporting.
Overhead allocation: Infrastructure costs that cannot be directly attributed to individual requests (load balancers, monitoring, storage) must be allocated using a reasonable model, typically proportional to request volume or token consumption.
Cost vs. price: The cost incurred (what you pay for infrastructure) and the price charged (what you bill the tenant) are separate concerns. Pricing decisions involve margins, competitive positioning, and commitment discounts; cost tracking is a prerequisite for pricing, not a substitute.
Cost Monitoring
Cost monitoring creates continuous visibility into spending, enabling early detection of anomalies, enforcement of budgets, and tracking of optimization progress. Without monitoring, cost problems are often discovered only at month-end when the bill arrives.
Key Cost Metrics
An effective cost monitoring system tracks metrics at multiple granularities:
Token-level metrics (collected per request):
- Prompt token count and completion token count
- Request cost in dollars (computed from token counts and current rates)
- Cost efficiency ratio: output quality score divided by cost (requires quality evaluation)
Request-level metrics (aggregated over time windows):
- Requests per minute and tokens per minute, broken down by model tier
- Cost per request and cost per token, with trend tracking
- Cache hit rates for prefix caching and semantic caching
- Token rejection rates from per-user budget enforcement
Infrastructure metrics (from the hosting platform):
- GPU utilization (compute and memory), both instantaneous and averaged
- Throughput (tokens/sec) vs. theoretical maximum
- Cost per GPU-hour and effective cost per token
- Reserved instance utilization and on-demand overflow costs
Business metrics (connecting cost to value):
- Cost per active user per day
- Cost per successfully completed task (e.g., per support ticket resolved, per code review)
- Cost as a percentage of revenue or allocated budget
# Cost monitoring pipeline with anomaly detection
from collections import deque
import numpy as np
class CostMonitor:
"""
Rolling cost monitor with anomaly detection using
exponential weighted moving average (EWMA).
"""
def __init__(self, window_minutes: int = 60, alert_multiplier: float = 2.5):
self.window_minutes = window_minutes
self.alert_multiplier = alert_multiplier
# Store (timestamp, cost) tuples
self.cost_history = deque()
self.ewma_cost = None
self.ewma_alpha = 0.1 # Smoothing factor
def record(self, timestamp: float, cost: float):
"""Record a cost observation and update EWMA."""
self.cost_history.append((timestamp, cost))
# Remove observations outside the window
cutoff = timestamp - self.window_minutes * 60
while self.cost_history and self.cost_history[0][0] < cutoff:
self.cost_history.popleft()
# Update EWMA
if self.ewma_cost is None:
self.ewma_cost = cost
else:
self.ewma_cost = (
self.ewma_alpha * cost + (1 - self.ewma_alpha) * self.ewma_cost
)
def is_anomaly(self, cost: float) -> bool:
"""Compare a new observation with the EWMA of prior observations."""
if self.ewma_cost is None or self.ewma_cost == 0:
return False
return cost > self.alert_multiplier * self.ewma_cost
def window_total(self) -> float:
"""Total cost in the current window."""
return sum(cost for _, cost in self.cost_history)
def window_rate(self) -> float:
"""Estimated cost rate in USD per hour."""
if not self.cost_history:
return 0.0
span_seconds = self.cost_history[-1][0] - self.cost_history[0][0]
if span_seconds < 1:
return 0.0
return self.window_total() / span_seconds * 3600
# Simulate monitoring with an injected cost spike
monitor = CostMonitor(window_minutes=60, alert_multiplier=2.5)
alerts = []
timestamps = []
costs = []
ewma_values = []
alert_thresholds = []
rng = np.random.default_rng(seed=42)
base_time = time.time() - 7200 # Start 2 hours ago
for minute in range(120):
t = base_time + minute * 60
# Normal traffic with some noise
normal_cost = 0.05 + rng.normal(0, 0.01)
# Inject a cost spike at minute 75 (runaway batch job scenario)
if 75 <= minute <= 85:
request_cost = normal_cost * 8
else:
request_cost = max(0.001, normal_cost)
threshold_before_update = (
monitor.alert_multiplier * monitor.ewma_cost
if monitor.ewma_cost is not None
else np.nan
)
is_alert = monitor.is_anomaly(request_cost)
if is_alert:
alerts.append(minute)
monitor.record(t, request_cost)
timestamps.append(minute)
costs.append(request_cost)
ewma_values.append(monitor.ewma_cost)
alert_thresholds.append(threshold_before_update)Cost Monitoring Summary (120-minute window): Current window total: $6.73 Estimated rate: $6.73/hr Current EWMA: $0.0555/min Anomaly alerts fired at minutes: [75, 76, 77, 78] Total alerts: 4

Budget Enforcement
Cost monitoring without enforcement is incomplete. Budget enforcement closes the loop by taking action when spending approaches or exceeds thresholds.
A layered enforcement approach works well in practice:
Soft limits trigger alerts and notifications without blocking requests. When spending reaches 70% of a daily budget, an automated notification goes to the responsible team. At 90%, escalation occurs to engineering leadership. This approach preserves service availability while creating urgency for investigation.
Hard limits block new requests when a budget is exhausted. This is appropriate for non-critical, optional features but must be applied carefully to avoid breaking user-facing functionality. Hard limits are more commonly applied to per-user quotas than to organizational budgets.
Rate limiting caps the rate of spending rather than total spend. Rather than blocking all requests after a budget is hit, rate limiting ensures the system spends at most a certain amount per hour. This is more graceful for users, as it slows down expensive operations rather than blocking them entirely.
Graceful degradation automatically routes requests to cheaper model tiers when spending is high. A user who would normally receive a response from a 70B model might receive one from a 7B model when the budget is constrained. This requires that alternative tiers are available and that quality degradation is acceptable for the use case.
# Budget enforcement with graceful degradation
class BudgetEnforcer:
"""
Enforces spending budgets with graceful degradation.
Routes to cheaper tiers as budget utilization increases.
"""
def __init__(self, daily_budget_usd: float):
self.daily_budget_usd = daily_budget_usd
self.spent_today = 0.0
# Tier selection thresholds
self.tier_policy = [
# (max_utilization, allowed_tier, reason)
(0.70, "large", "Normal operation"),
(0.85, "medium", "Approaching budget, degrading to medium"),
(0.95, "small", "Near budget limit, degrading to small"),
(1.00, None, "Budget exhausted, requests blocked"),
]
def get_allowed_tier(self, requested_tier: str) -> tuple:
"""
Return the tier to use given current budget utilization.
Returns (actual_tier, reason) where actual_tier may be None (block).
"""
utilization = self.spent_today / self.daily_budget_usd
for max_util, allowed_tier, reason in self.tier_policy:
if utilization <= max_util:
if allowed_tier is None:
return None, reason
# Return cheapest of requested and allowed
tier_order = ["small", "medium", "large"]
effective_tier = tier_order[
min(
tier_order.index(requested_tier),
tier_order.index(allowed_tier),
)
]
return effective_tier, reason
return None, "Budget exhausted"
def record_spend(self, cost: float):
self.spent_today += cost
# Simulate requests over a day as budget fills
enforcer = BudgetEnforcer(daily_budget_usd=50.0)
tier_decisions = {"small": 0, "medium": 0, "large": 0, "blocked": 0}
rng2 = np.random.default_rng(seed=42)
for _ in range(2000):
requested = rng2.choice(["small", "medium", "large"], p=[0.5, 0.3, 0.2])
actual_tier, reason = enforcer.get_allowed_tier(requested)
if actual_tier is None:
tier_decisions["blocked"] += 1
else:
tier_decisions[actual_tier] += 1
# Simulate actual cost for this request
rates = TIER_COSTS[actual_tier]
prompt_t = int(rng2.lognormal(6, 0.8))
completion_t = int(rng2.lognormal(5, 0.7))
cost = (prompt_t / 1000) * rates["input"] + (
completion_t / 1000
) * rates["output"]
enforcer.record_spend(cost)Budget Enforcement Simulation (2,000 requests, $50 daily budget):
Budget spent: $1.45 (2.9% utilized)
Request routing decisions:
small 1002 requests (50.1%)
medium 605 requests (30.2%)
large 393 requests (19.7%)
blocked 0 requests (0.0%)Cost Dashboards and Reporting
Effective cost dashboards present costs at the right granularity for each audience. Engineers need per-request and per-feature breakdowns to debug cost regressions. Finance teams need daily and monthly totals with trend projections. Product managers need cost-per-outcome metrics that connect spending to business value.
A useful dashboard structure organizes metrics into three layers:
Real-time layer: Current spend rate (USD/hour), active request count, GPU utilization, cache hit rates. Refreshes every minute. Surfaces anomalies and operational issues immediately.
Daily summary layer: Total spend, cost breakdown by feature and team, per-user percentiles (median, P95, P99), cost per token by model tier, comparison to prior day and prior week. Surfaces trends and allocation changes.
Monthly planning layer: Monthly spend totals, forecast vs. actuals, per-feature cost trends, optimization opportunity analysis, reserved instance utilization and coverage recommendations. Supports budget planning and infrastructure decisions.
# Simulate a multi-day cost time series for dashboard illustration
days = 30
daily_costs = []
rng3 = np.random.default_rng(seed=42)
for d in range(days):
# Organic growth trend of ~2% per day
growth_factor = 1.02**d
# Weekend dip (days 5-6, 12-13, etc.)
day_of_week = d % 7
weekday_factor = 0.65 if day_of_week >= 5 else 1.0
# Random variation
noise = rng3.normal(1.0, 0.05)
base_daily = 180.0 # Base cost in USD per day
daily_costs.append(base_daily * growth_factor * weekday_factor * noise)
# Compute rolling 7-day average
rolling_7d = []
for i in range(days):
start = max(0, i - 6)
rolling_7d.append(np.mean(daily_costs[start : i + 1]))
# Compute projected monthly total using last 7-day average rate
last_week_avg = np.mean(daily_costs[-7:])
monthly_projection = last_week_avg * 30
total_mtd = sum(daily_costs)30-Day Cost Summary: Total (30 days): $6,612.60 Average daily: $220.42 Peak daily: $326.54 (day 30) Minimum daily: $120.77 (day 6) Last 7-day avg: $272.26/day Monthly projection (at current rate): $8,167.81

Cost Regression Detection
Cost regressions occur when code changes or configuration updates inadvertently increase cost per request. Without automated detection, they often go unnoticed until they accumulate into significant spending increases.
A cost regression can take several forms: a new prompt template that adds 200 tokens to every request, a code path that requests a larger model than necessary, a bug that disables prefix caching, or a new feature that generates much longer responses than expected.
Detecting regressions requires tracking per-feature cost metrics over time and alerting when they deviate significantly from baseline. The key metrics to track are average tokens per request (both input and output) and cost per request, broken down by feature. A step change in these metrics after a deployment is a strong signal of a cost regression.

Limitations and Practical Considerations
LLM cost management involves trade-offs that pure technical analysis cannot fully resolve. Understanding these limitations shapes how to apply the techniques in this chapter.
The quality-cost trade-off is real and task-dependent. Right-sizing models and applying aggressive quantization reduces costs, but some tasks require the capabilities of larger, less compressed models. Code generation with complex logical dependencies, context-sensitive reasoning, and multi-step problem solving often sees meaningful quality degradation from INT4 quantization or smaller models. Establishing quality baselines and monitoring for regressions is essential whenever cost optimizations are applied. A cost reduction that degrades user outcomes is not a real saving.
Caching is powerful but requires careful invalidation design. Semantic caching introduces the risk of serving stale or incorrect responses to queries that are semantically similar to cached queries despite differing in intent from cached queries. The similarity threshold that determines cache hits is difficult to set correctly for all query types simultaneously. FAQ-style queries tolerate higher similarity thresholds; open-ended creative queries may require very low thresholds or no semantic caching at all. Monitoring cache hit rates alongside user satisfaction metrics is important for calibrating the threshold appropriately.
Infrastructure cost models become outdated quickly. GPU pricing changes as new hardware generations are released and as cloud providers compete. A cost model built on A100 pricing in 2023 is substantially different from one built on H100 pricing in 2025. Cost models need regular recalibration against current market rates, and architectural decisions (model size, quantization level) should be revisited when new hardware or pricing options become available.
Cost allocation creates organizational incentives that must be designed carefully. Chargeback systems that make individual teams bear the full cost of their AI usage can discourage beneficial applications if teams do not have the budget to absorb the costs. Shared infrastructure with showback reporting is often more effective at driving efficiency awareness without creating barriers to adoption. The organizational design of cost allocation matters as much as the technical implementation.
Anomaly detection thresholds require calibration and maintenance. A fixed multiplier like "alert if cost exceeds 2.5x EWMA" will produce false positives during legitimate traffic growth and may miss gradual regressions that slowly increase the EWMA. Effective monitoring systems combine EWMA-based anomaly detection with percent-change alerts over different time windows, correlation with deployment events, and manual review processes for investigating alerts before acting on them.
Total cost of ownership extends beyond inference. The analysis in this chapter focuses on inference costs, which are the dominant ongoing expense. But total cost of ownership includes model development and fine-tuning costs (significant for custom models), evaluation and safety review infrastructure, human review of model outputs in high-stakes applications, and the engineering time required to implement and maintain cost management systems themselves. Cost optimization initiatives that reduce inference costs while increasing engineering overhead may not be net-positive.
Summary
Cost management for LLM deployments requires sustained attention across four interconnected areas.
Cost modeling begins with the token-based cost model: cost per token is determined by infrastructure cost divided by throughput. GPU utilization is the primary lever, doubling utilization halves the effective cost per token. The prefill-decode distinction matters because these phases have different cost characteristics.
Cost optimization draws on model right-sizing (the most impactful decision), quantization (reduces memory footprint and improves memory-bandwidth-bound throughput), caching (prefix caching for shared prompts, semantic caching for repeated queries), speculative decoding (increases effective throughput for predictable generation tasks), and hardware strategy (reserved instances for baseline load, spot/preemptible for batch workloads).
Cost allocation requires request-level instrumentation with tenant, feature, and user metadata. The choice between showback and chargeback is organizational as much as technical; showback builds awareness without creating friction, while chargeback creates direct incentives for efficiency. Per-user limits prevent runaway spending from individual users.
Cost monitoring tracks costs at multiple granularities: real-time for operational awareness, daily for trend detection, and monthly for planning. Anomaly detection using EWMA baselines catches cost spikes early. Per-feature token tracking enables regression detection after deployments. Budget enforcement with graceful degradation maintains service availability under budget pressure by routing to cheaper model tiers rather than blocking requests entirely.
The next chapters continue examining production systems concerns, including quality monitoring and observability patterns that complement the cost monitoring approaches covered here.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about LLM Cost Management.
LLM Cost Management Quiz
Reference
Citation details
Cite or share this article.
Continue with the full handbook
This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.
Explore Language AI HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

Comments
No comments yet. Be the first to share your thoughts!