The Problem
A team launches an AI-powered document analysis feature. During the pilot, 50 users make an average of 20 queries per month. The API cost per query is $0.04 (short inputs, concise outputs). The team projects: "At 10,000 users, that's $8,000/month in API costs — totally manageable." Six months later, the feature has 10,000 users, but the actual API bill is $47,000/month. What happened? Three things the launch-cohort model missed: (1) average input length tripled as users moved from short memos to full contracts, (2) average output length doubled as users learned to ask for more detailed analysis, and (3) power users emerged who make 500+ queries per month, dragging the average from 20 to 55 queries per user. The per-query cost went from $0.04 to $0.11, and the per-user cost went from $0.80/month to $6.05/month. The feature is now significantly more expensive than projected, and the team is over budget.
This is the unit economics trap: launch-cohort data is not representative of scale. Early adopters use features differently than mainstream users. API costs are variable (they scale with usage), not fixed (like server costs). And the most expensive users — power users who make 500+ queries — are often the ones who find the feature most valuable, meaning adoption success directly drives cost growth. A PM who doesn't model unit economics at scale is flying blind.
Cost modeling at scale
The fix is building a cost model that uses real production parameters, not launch-cohort estimates:
Per-query cost breakdown:
- Input token cost: (average input tokens) × (price per 1K input tokens)
- Output token cost: (average output tokens) × (price per 1K output tokens)
- Infrastructure cost: (API gateway, caching, monitoring) ÷ (queries per month)
- Total per-query cost: input + output + infrastructure
Project at three usage levels:
- Launch cohort (current): small user base, short inputs, low frequency
- 10x scale: mainstream users, longer inputs, higher frequency, power users emerge
- 100x scale: full user base, full input complexity, peak usage patterns
The 10x and 100x projections reveal whether unit economics hold or break. If per-query cost triples at 10x (because input length triples) and per-user cost increases 7x (because query frequency increases), the feature that looked affordable at launch may be unsustainable at scale.
API costs are variable, not fixed
Traditional software features have fixed infrastructure costs (servers) that don't scale linearly with users. AI features have variable API costs that scale directly with usage — every additional query costs money. This means adoption success (more users, more queries) directly increases costs. A feature that "scales beautifully" in traditional software may scale terribly with AI if per-query economics are negative.
Margin impact and pricing structures
Once you know the cost, the question is whether the pricing covers it. Three common pricing structures for AI features, and when each breaks down:
Per-seat pricing: Every user pays the same flat fee (e.g., $30/user/month). Breaks down when usage varies dramatically across users: one user makes 10 queries/month (cost: $1.10), another makes 10,000 (cost: $1,100). Both pay $30. The light user is overcharged; the heavy user is subsidized. At scale, the heavy users dominate costs and the feature loses money on them.
Usage-based pricing: Users pay per query (e.g., $0.15/query). Aligns cost with revenue perfectly — heavy users pay more. Breaks down for user psychology: users hesitate to use the feature because every query costs money, reducing adoption. Also breaks down for budget predictability — neither the user nor the company knows what the bill will be.
Hybrid pricing: Per-seat fee covers baseline usage (e.g., $30/seat includes 500 queries), plus per-query fee above the threshold ($0.10/query over 500). Combines predictable revenue (per-seat) with cost alignment (usage-based above threshold). Light users pay a fair price; heavy users pay proportionally to their actual cost. This is the most robust model for AI features with high usage variance.
A SaaS product charges $50/seat/month for an AI document analysis feature. The average user makes 200 queries/month at $0.12/query = $24/user in API costs. Margin per user: $26. But the top 10% of users make 2,000 queries/month = $240/user in API costs. What's the unit economics problem, and what pricing change would fix it?
The problem is usage variance: per-seat pricing assumes all users consume equally, but the top 10% cost 10x the average. At scale, heavy users erode blended margin. Hybrid pricing fixes this: the per-seat fee covers baseline usage (with margin), and the per-query fee above a threshold captures the cost of heavy users. Light users aren't overcharged; heavy users pay proportionally. This aligns revenue with cost — the core principle of sustainable AI feature pricing.
Calculating ROI
ROI = (Total Value - Total Cost) / Total Cost
Total cost includes:
- API costs (variable, scales with usage) — the largest line item for most AI features
- Infrastructure (API gateway, caching, monitoring, vector DB if RAG)
- ML team time (maintenance, retraining, eval updates, incident response)
- PM time (monitoring, cross-functional coordination, stakeholder communication)
- Opportunity cost (what else could this team build?)
Total value includes:
- Time saved: (hours saved per user per month) × (number of users) × (loaded hourly rate)
- Revenue increase: (feature-driven conversions or upsells) × (revenue per conversion)
- Cost reduction: (manual process hours eliminated) × (hourly rate of displaced work)
- Retention improvement: (reduced churn rate) × (customer lifetime value)
The key discipline is quantifying both sides with real data, not estimates. "It feels valuable" is not an ROI calculation. "Users save an average of 3.2 hours/week (measured via time-tracking study), at a loaded rate of $85/hour, across 2,000 users = $544,000/month in time saved, against $47,000 in API costs and $30,000 in team time = $467,000/month net value = 515% ROI" is an ROI calculation.
A PM calculates ROI for an AI feature at 515%, using "time saved" estimated from a single informal Slack poll asking users "roughly how much time do you think this saves you?" A skeptical VP asks how confident the team is in that number. What's the strongest response?
A self-reported estimate from an informal poll is a soft input dressed up as a hard number — respondents guessing "roughly how much time" rarely produce numbers as reliable as an actual time-tracking study (e.g., instrumented before/after task timing, or a controlled comparison group). Presenting 515% ROI as if it were as solid as the API-cost side of the equation (which usually is precisely measurable from billing data) overstates confidence in the weaker input. The fix isn't abandoning the ROI calculation — it's tightening the measurement on the value side to match the rigor already applied to the cost side, exactly the "real data, not estimates" standard this lesson sets.
Build
Build it
The code artifact lesson_08_04_unit_economics.py implements a unit economics model builder:
CostComponent — a dataclass for one cost line item: name, per_unit_cost, unit_type (per_query, per_user, per_month), and scaling_behavior (fixed, variable)ValueComponent — a dataclass for one value line item: name, per_unit_value, unit_type, and measurement_sourcePricingModel — an enum (PER_SEAT, USAGE_BASED, HYBRID) with per-seat fee, included queries, and overage feeUnitEconomicsModel — a collection of cost components, value components, pricing model, and usage parameters (users, queries_per_user, input_tokens, output_tokens)project_at_scale() — projects total cost, total value, margin, and ROI at a given user countcheck_economics_health() — flags negative unit economics, unsustainable margins, and pricing model mismatches- A worked example: the document analysis feature from this lesson's opening, modeled at launch, 10x, and 100x scale with hybrid pricing
"""
lesson_08_04_unit_economics.py
A unit economics model builder for AI features.
Models per-query cost at scale, evaluates pricing structures,
calculates ROI, and flags negative unit economics before they
become a budget crisis.
Usage:
python lesson_08_04_unit_economics.py
No external dependencies, no network calls, no API keys required.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
class UnitType(Enum):
PER_QUERY = "per_query"
PER_USER = "per_user"
PER_MONTH = "per_month"
class ScalingBehavior(Enum):
FIXED = "fixed"
VARIABLE = "variable"
class PricingType(Enum):
PER_SEAT = "per_seat"
USAGE_BASED = "usage_based"
HYBRID = "hybrid"
@dataclass
class CostComponent:
name: str
per_unit_cost: float
unit_type: UnitType
scaling: ScalingBehavior
notes: str = ""
@dataclass
class ValueComponent:
name: str
per_unit_value: float
unit_type: UnitType
measurement_source: str = ""
@dataclass
class PricingModel:
pricing_type: PricingType
per_seat_fee: float = 0.0
included_queries: int = 0
overage_fee_per_query: float = 0.0
per_query_fee: float = 0.0
@dataclass
class UsageParams:
users: int
queries_per_user: float
avg_input_tokens: float
avg_output_tokens: float
price_per_1k_input: float
price_per_1k_output: float
@dataclass
class UnitEconomicsModel:
feature_name: str
costs: list[CostComponent] = field(default_factory=list)
values: list[ValueComponent] = field(default_factory=list)
pricing: Optional[PricingModel] = None
usage: Optional[UsageParams] = None
def api_cost_per_query(self) -> float:
if not self.usage:
return 0.0
u = self.usage
return (u.avg_input_tokens / 1000 * u.price_per_1k_input +
u.avg_output_tokens / 1000 * u.price_per_1k_output)
def project_at_scale(self, user_count: int) -> dict:
u = self.usage
if not u:
return {}
total_queries = user_count * u.queries_per_user
api_per_query = self.api_cost_per_query()
api_cost = total_queries * api_per_query
other_costs = 0.0
for c in self.costs:
if c.unit_type == UnitType.PER_QUERY:
other_costs += total_queries * c.per_unit_cost
elif c.unit_type == UnitType.PER_USER:
other_costs += user_count * c.per_unit_cost
elif c.unit_type == UnitType.PER_MONTH:
other_costs += c.per_unit_cost
total_cost = api_cost + other_costs
total_value = 0.0
for v in self.values:
if v.unit_type == UnitType.PER_QUERY:
total_value += total_queries * v.per_unit_value
elif v.unit_type == UnitType.PER_USER:
total_value += user_count * v.per_unit_value
elif v.unit_type == UnitType.PER_MONTH:
total_value += v.per_unit_value
if self.pricing:
p = self.pricing
if p.pricing_type == PricingType.PER_SEAT:
revenue = user_count * p.per_seat_fee
elif p.pricing_type == PricingType.USAGE_BASED:
revenue = total_queries * p.per_query_fee
else: # HYBRID
base_revenue = user_count * p.per_seat_fee
overage_queries = max(0, u.queries_per_user - p.included_queries)
overage_revenue = user_count * overage_queries * p.overage_fee_per_query
revenue = base_revenue + overage_revenue
else:
revenue = 0.0
margin = revenue - total_cost
roi = ((total_value - total_cost) / total_cost * 100) if total_cost > 0 else 0.0
return {
"users": user_count,
"total_queries": int(total_queries),
"api_cost_per_query": round(api_per_query, 4),
"total_api_cost": round(api_cost, 2),
"total_cost": round(total_cost, 2),
"total_value": round(total_value, 2),
"revenue": round(revenue, 2),
"margin": round(margin, 2),
"roi_pct": round(roi, 1),
}
def check_economics_health(self) -> list[str]:
warnings = []
if not self.usage or not self.pricing:
warnings.append("Missing usage params or pricing model.")
return warnings
launch = self.project_at_scale(50)
scale_10x = self.project_at_scale(self.usage.users * 10)
if launch.get("margin", 0) > 0 and scale_10x.get("margin", 0) < 0:
warnings.append(
f"Positive margin at launch ({launch['margin']}) but negative "
f"at 10x scale ({scale_10x['margin']}) — unit economics break "
f"with adoption. Consider hybrid pricing or cost reduction."
)
api_per_query = self.api_cost_per_query()
if self.pricing.pricing_type == PricingType.PER_SEAT:
per_user_api = api_per_query * self.usage.queries_per_user
if per_user_api > self.pricing.per_seat_fee:
warnings.append(
f"Per-user API cost (${per_user_api:.2f}) exceeds per-seat "
f"fee (${self.pricing.per_seat_fee:.2f}) — negative margin "
f"per user. Switch to hybrid or usage-based pricing."
)
if scale_10x.get("roi_pct", 0) < 0:
warnings.append(
f"Negative ROI at 10x scale ({scale_10x['roi_pct']}%) — "
f"the feature destroys more value than it creates at scale."
)
return warnings
def render_projection(model: UnitEconomicsModel) -> str:
lines = [f"# Unit Economics: {model.feature_name}\n"]
for label, users in [("Launch Cohort", 50), ("10x Scale", 500), ("100x Scale", 5000)]:
proj = model.project_at_scale(users)
if not proj:
continue
lines.append(f"## {label} ({proj['users']} users)\n")
lines.append(f"- Total queries: {proj['total_queries']:,}")
lines.append(f"- API cost per query: ${proj['api_cost_per_query']}")
lines.append(f"- Total API cost: ${proj['total_api_cost']:,.2f}")
lines.append(f"- Total cost: ${proj['total_cost']:,.2f}")
lines.append(f"- Total value: ${proj['total_value']:,.2f}")
lines.append(f"- Revenue: ${proj['revenue']:,.2f}")
lines.append(f"- Margin: ${proj['margin']:,.2f}")
lines.append(f"- ROI: {proj['roi_pct']}%\n")
warnings = model.check_economics_health()
if warnings:
lines.append("## Health Warnings\n")
for w in warnings:
lines.append(f"- ⚠️ {w}")
lines.append("")
return "\n".join(lines)
def build_example_model() -> UnitEconomicsModel:
model = UnitEconomicsModel(feature_name="Document Analysis AI")
model.usage = UsageParams(
users=50,
queries_per_user=20,
avg_input_tokens=800,
avg_output_tokens=400,
price_per_1k_input=0.15,
price_per_1k_output=0.60,
)
model.costs = [
CostComponent("API gateway", 0.001, UnitType.PER_QUERY,
ScalingBehavior.VARIABLE, "per-query routing cost"),
CostComponent("Monitoring infrastructure", 500.0, UnitType.PER_MONTH,
ScalingBehavior.FIXED, "Langfuse + alerting"),
CostComponent("ML team maintenance", 8000.0, UnitType.PER_MONTH,
ScalingBehavior.FIXED, "0.5 FTE for retraining + eval"),
]
model.values = [
ValueComponent("Time saved per query", 2.50, UnitType.PER_QUERY,
"Time-tracking study: avg 12 min saved per query at $12.50/hr"),
ValueComponent("Retention improvement", 15.0, UnitType.PER_USER,
"Users with feature are 8% less likely to churn; "
"CLV impact estimated at $15/user/month"),
]
model.pricing = PricingModel(
pricing_type=PricingType.HYBRID,
per_seat_fee=30.0,
included_queries=100,
overage_fee_per_query=0.15,
)
return model
if __name__ == "__main__":
model = build_example_model()
print(render_projection(model))