Module 7: Technical comparison of providers
Cost benchmark
Latency tells you whether your product feels good. Cost tells you whether your product is viable. An endpoint with a P50 of 1s but that costs $10,000/month is a different problem from one with a P50 of 4s but $200/month.
In this capsule you're going to build a calculator that estimates the real monthly cost of each provider for different traffic profiles. You're not going to use "the nominal price per 1M tokens" — you're going to calculate what you actually pay considering warm pool, storage, free tier, everything.
By the end you'll be able to:
- Calculate the approximate monthly cost for any provider given your volume and average request length
- Compare providers at 3 traffic scales (low/medium/high) and find the "cross-over point" where the winner changes
- Detect hidden costs that most people miss (warm pool, egress, model storage)
- Make a defensible spreadsheet to show a cofounder or finance
Why it matters
Three real scenarios where this changes decisions:
Scenario 1. An MVP startup estimates "we're going to use OpenAI to start". They do the math: $0.15 per 1M input tokens, seems cheap. They don't consider that their average request includes 3000 tokens of RAG context. The real cost is 20× what they estimated.
Scenario 2. A team decides on self-hosting with Ollama "because it's free". They don't include the cost of the VM with a GPU running 24/7, which ends up being $1,800/month. More expensive than OpenAI at their current volume.
Scenario 3. An agency uses Modal and it looks cheap. They don't notice that their min_containers=2 costs them $1,400/month of warm pool even though there's almost no nighttime traffic.
All of these are avoidable with 15 minutes of a spreadsheet. This capsule teaches you to do it right.
Mental model: cost types
| Type | Who charges it | Characteristic |
|---|---|---|
| Per token | OpenAI, Anthropic, OpenRouter, Together | Linear with volume. Scales well downward. |
| Per GPU second | Modal, Replicate, RunPod | Linear with inference time. Sensitive to optimizations. |
| Fixed (rent) | Self-hosted (VM with GPU 24/7) | Independent of volume. Wins at high volume. |
| Free | Ollama local on your hardware | You pay with your hardware/electricity, not billed. |
Cross-over points: the line where one type stops being cheaper than another. Identifying them is the point of this capsule.
The base formula
For token-based providers:
monthly_cost = monthly_requests × avg_tokens_per_request × price_per_token
For GPU-based providers:
monthly_cost = (monthly_requests × seconds_per_request × price_per_second)
+ (warm_hours × 3600 × price_per_second)
+ model_storage_cost
For self-hosted (VM 24/7):
monthly_cost = monthly_hours × VM_price_per_hour
+ amortized_setup_cost
+ maintenance_cost (your time $$$)
Calculator: implementation
Create cost_calculator.py:
# cost_calculator.py
from dataclasses import dataclass
from typing import Literal
# ============================================================
# Reference prices (verify the current ones in each provider's docs)
# These are approximate as of early 2026; check before using.
# ============================================================
PRICES = {
"openai_gpt-4o-mini": {
"input_per_1m_tokens": 0.15,
"output_per_1m_tokens": 0.60,
},
"openai_gpt-4o": {
"input_per_1m_tokens": 2.50,
"output_per_1m_tokens": 10.00,
},
"anthropic_claude-3.5-sonnet": {
"input_per_1m_tokens": 3.00,
"output_per_1m_tokens": 15.00,
},
"openrouter_mistral-7b-instruct": {
"input_per_1m_tokens": 0.07,
"output_per_1m_tokens": 0.07,
},
"openrouter_mixtral-8x7b-instruct": {
"input_per_1m_tokens": 0.24,
"output_per_1m_tokens": 0.24,
},
"modal_a10g": {
"per_second": 0.000306, # GPU only
},
"modal_t4": {
"per_second": 0.000164,
},
"modal_a100_40gb": {
"per_second": 0.001097,
},
"selfhosted_a10g_aws": {
"per_hour": 1.006, # g5.xlarge spot approx
},
"selfhosted_a100_aws": {
"per_hour": 4.10, # p4d.24xlarge spot approx
},
}
# ============================================================
# Usage profile
# ============================================================
@dataclass
class UsageProfile:
requests_month: int
avg_input_tokens: int = 500
avg_output_tokens: int = 200
avg_gpu_seconds: float = 2.0 # for GPU providers
warm_pool_hours_month: float = 0 # 0 if min_containers=0; 720 if 24/7
def cost_openai(self, model: str) -> float:
p = PRICES[f"openai_{model}"]
return (
self.requests_month * self.avg_input_tokens * p["input_per_1m_tokens"] / 1_000_000
+ self.requests_month * self.avg_output_tokens * p["output_per_1m_tokens"] / 1_000_000
)
def cost_anthropic(self, model: str) -> float:
p = PRICES[f"anthropic_{model}"]
return (
self.requests_month * self.avg_input_tokens * p["input_per_1m_tokens"] / 1_000_000
+ self.requests_month * self.avg_output_tokens * p["output_per_1m_tokens"] / 1_000_000
)
def cost_openrouter(self, model: str) -> float:
p = PRICES[f"openrouter_{model}"]
total_tokens = self.avg_input_tokens + self.avg_output_tokens
return self.requests_month * total_tokens * p["input_per_1m_tokens"] / 1_000_000
def cost_modal(self, gpu: str = "a10g") -> float:
price_sec = PRICES[f"modal_{gpu}"]["per_second"]
active_gpu = self.requests_month * self.avg_gpu_seconds * price_sec
warm_pool = self.warm_pool_hours_month * 3600 * price_sec
storage_model = 2.0 # ~14GB × $0.10/GB/month ≈ $1.40, rounded
return active_gpu + warm_pool + storage_model
def cost_selfhosted(self, gpu: str = "a10g_aws") -> float:
per_hour = PRICES[f"selfhosted_{gpu}"]["per_hour"]
return 720 * per_hour # 24×30 = 720 hrs/month
def cost_ollama_local(self) -> float:
return 0.0 # your hardware/electricity, not billed directly
# ============================================================
# Comparative report
# ============================================================
def report(profile: UsageProfile, label: str):
print(f"\n=== {label} ===")
print(f" Volume: {profile.requests_month:,} requests/month")
print(f" Tokens: {profile.avg_input_tokens} input + {profile.avg_output_tokens} output")
print(f" Warm pool: {profile.warm_pool_hours_month} hrs/month\n")
print(f" {'Option':<45} {'Cost/month':>12}")
print(f" {'-' * 60}")
candidates = [
("OpenAI GPT-4o-mini", profile.cost_openai("gpt-4o-mini")),
("OpenAI GPT-4o", profile.cost_openai("gpt-4o")),
("Anthropic Claude 3.5 Sonnet", profile.cost_anthropic("claude-3.5-sonnet")),
("OpenRouter Mistral 7B", profile.cost_openrouter("mistral-7b-instruct")),
("OpenRouter Mixtral 8x7B", profile.cost_openrouter("mixtral-8x7b-instruct")),
("Modal Mistral 7B (A10G)", profile.cost_modal("a10g")),
("Modal Mistral 7B (T4)", profile.cost_modal("t4")),
("Self-hosted Ollama (AWS A10G)", profile.cost_selfhosted("a10g_aws")),
("Ollama local (your hardware)", profile.cost_ollama_local()),
]
for name, cost in sorted(candidates, key=lambda x: x[1]):
marker = " ←" if cost == min(c for _, c in candidates) else ""
print(f" {name:<45} ${cost:>10.2f}{marker}")
# ============================================================
# Three representative profiles
# ============================================================
if __name__ == "__main__":
LOW = UsageProfile(
requests_month=10_000,
avg_input_tokens=300,
avg_output_tokens=150,
avg_gpu_seconds=1.5,
warm_pool_hours_month=0,
)
MEDIUM = UsageProfile(
requests_month=200_000,
avg_input_tokens=500,
avg_output_tokens=200,
avg_gpu_seconds=2.0,
warm_pool_hours_month=200, # warm pool during business hours
)
HIGH = UsageProfile(
requests_month=2_000_000,
avg_input_tokens=800,
avg_output_tokens=300,
avg_gpu_seconds=2.5,
warm_pool_hours_month=720, # warm pool 24/7
)
report(LOW, "LOW: MVP / hobby (10k req/month)")
report(MEDIUM, "MEDIUM: Product in growth (200k req/month)")
report(HIGH, "HIGH: Established product (2M req/month)")
Run:
python cost_calculator.py
Typical reading of results
Approximate outputs (with 2026 numbers, adjust to current prices):
=== LOW: MVP / hobby (10k req/month) ===
Ollama local (your hardware) $ 0.00 ←
OpenAI GPT-4o-mini $ 1.65
OpenRouter Mistral 7B $ 0.32
Modal Mistral 7B (T4) $ 4.46
OpenAI GPT-4o $ 27.50
...
Self-hosted Ollama (AWS A10G) $ 724.32
=== MEDIUM: Product in growth (200k req/month) ===
OpenRouter Mistral 7B $ 9.80 ←
Ollama local $ 0.00 (excluded — doesn't scale this way)
OpenAI GPT-4o-mini $ 39.00
Modal Mistral 7B (T4) + warm pool $ 187.20
...
OpenAI GPT-4o $ 900.00
Self-hosted Ollama (AWS A10G) $ 724.32
=== HIGH: Established product (2M req/month) ===
Self-hosted Ollama (AWS A10G) $ 724.32 ←
Modal Mistral 7B (T4) full warm $ 1,200.00 (approx)
OpenRouter Mistral 7B $ 154.00
OpenAI GPT-4o-mini $ 660.00
OpenAI GPT-4o $ 12,000.00
Patterns that emerge:
- Low: local "wins" if your hardware is enough. OpenRouter Mistral is scandalously cheap if you don't need GPT-4 quality.
- Medium: OpenRouter still wins for cases where Mistral is enough. OpenAI/Anthropic win if your quality requires it.
- High: self-hosted starts to make sense. Paying for a GPU 24/7 vs paying per usage changes the math.
Cross-over points: where the winner changes
Interesting question: at what volume does Self-hosted beat OpenAI?
# Solve: 720 × $1 = req × 700 × ($0.15 + $0.60) / 1M (gpt-4o-mini)
# $720 = req × 525 / 1M
# req = 1.37M requests/month
With 700 total tokens/request, OpenAI GPT-4o-mini is cheaper than Self-hosted A10G up to ~1.4M req/month. Past that point, self-hosted wins.
But — and this is important — Self-hosted assumes 100% utilization (24/7 traffic saturating the GPU). If your traffic is bursty (200K during business hours, 0 at dawn), the GPU is idle 50% of the time and you need to multiply your break-even by ~2×.
Lesson: cross-over points depend on your traffic pattern, not just the total volume.
Hidden costs that almost nobody includes
1. Warm pool. Modal with min_containers=1 A10G = ~$720/month extra. Easy to forget; painful at the end of the month.
2. Egress. Modal and cloud providers charge for outbound transfer. For text chat it's negligible; for images/audio it can hurt.
3. Model storage. 50GB volumes with several models = ~$5/month. Cumulative.
4. Human maintenance (self-hosted). If your team spends 5hrs/week maintaining the GPU, patching drivers, debugging OOM — that's engineer time. At $80/hr × 20hrs/month = $1,600/month "invisible".
5. Logging and observability. Datadog, Sentry, etc. add ~$50-300/month. You need it but it doesn't show up in the LLM provider calc.
6. Tier creep in OpenAI. "We're going to start with gpt-4o-mini" → "we need better quality, we move to gpt-4o" → a bill 15× higher. Plan for that drift.
Common traps
Trap 1 — "OpenAI says $0.15 per 1M tokens, so cheap." That's for input. Output costs 4×. And your real request adds input + output. Read the full prices.
Trap 2 — "Ollama is free." Your hardware isn't free ($2000 laptop, ~$15/month electricity running full-time). For personal use, "free enough". For a commercial product, consider amortization.
Trap 3 — "Self-hosted is always cheaper at scale." Only if the GPU is saturated. If your nighttime traffic is 5% of daytime, a GPU running 24/7 wastes 50%+ of the time. Modal/serverless can still win.
Trap 4 — "I compare Mistral 7B vs GPT-4 by price." It's not a fair comparison. If Mistral 7B doesn't solve your task with acceptable quality, its "low price" is irrelevant. Compare models that meet the quality SLA, not just any model.
Trap 5 — "I calculated with prices from a year ago." Prices change. OpenAI lowered gpt-4o-mini significantly in 2025. Anthropic changed pricing. Modal adjusts. Check current prices when you're going to make a decision.
Exercise
Your product is a B2B technical support assistant:
- 80K requests/month expected
- Each request includes 1500 tokens of RAG context + 200 tokens of question = ~1700 input
- Average response ~250 tokens
- Traffic concentrated from 9am-6pm business hours, almost nothing at night
- Your biggest enterprise client demands that the data not leave the US (cloud-managed providers get a ⚠️)
Calculate:
- Approximate monthly cost with OpenAI GPT-4o-mini (is it valid for your enterprise client?)
- Approximate monthly cost with Modal Mistral 7B + warm pool during business hours
- Approximate monthly cost with Self-hosted Ollama in the EU
- Which do you recommend and why?
See solution
case_profile = UsageProfile(
requests_month=80_000,
avg_input_tokens=1700,
avg_output_tokens=250,
avg_gpu_seconds=2.5,
warm_pool_hours_month=200, # ~9hrs × 22 days
)
# OpenAI gpt-4o-mini
# = 80000 × 1700 × 0.15 / 1M + 80000 × 250 × 0.60 / 1M
# = $20.40 + $12.00 = $32.40
# ⚠️ NOT valid if the enterprise client restricts data residency in the US
# Modal Mistral 7B A10G
# Active GPU: 80000 × 2.5 × $0.000306 = $61.20
# Warm pool: 200 × 3600 × $0.000306 = $220.32
# Storage: ~$2
# Total: ~$283.52
# (verify Modal's SOC2/data residency certification with your client)
# Self-hosted Ollama EU
# A10G on AWS EU eq.: ~$1.05/hr × 720 = $756/month
# + human maintenance cost (variable)
# Recommendation:
# - If Modal meets your client's data residency (verify), Modal is ~3× cheaper
# - If not, Self-hosted EU is the only valid option and you accept the cost
# - OpenAI isn't viable due to the contractual restriction
Defensible decision: document the numbers, verify certifications with the client's legal team, and recommend with explicit contractual and economic reasons.
Summary
You learned:
- ✅ Cost types (token, GPU-second, fixed, free) and when each one wins
- ✅ Calculate monthly cost including hidden costs (warm pool, storage, egress, human)
- ✅ Identify cross-over points where the economic winner changes
- ✅ Design representative profiles (low/medium/high) that reflect typical growth
- ✅ Defend a recommendation with numbers, not opinions
Checkpoint: if you can answer "how much does this cost a month?" without fumbling, with a number justified by a formula, you're ready.
Next capsule
04 — Quality benchmark. The hardest of the three dimensions. We're going to pin it down with: a representative evaluation set, a clear rubric, LLM-as-judge, and defensible metrics.
Resources
- OpenAI Pricing — source of truth, changes.
- Modal Pricing — current GPU per second.
- OpenRouter Models — aggregated prices per model.
- Anthropic Pricing — Claude variants.
- Artificial Analysis — Pricing Comparisons — public comparative table.
- AWS EC2 GPU Pricing — for self-hosted calculation.