lumo

Module 01 · Lesson 11 of 13Free

Estimating the cost and latency of a feature

Level
Beginner
Topic
AI engineering fundamentals
Reading time
15 min

When you finish this page you can

  1. Count the calls behind one completed task, including refreshes and retries, as labelled assumptions.
  2. Turn typical and long per-call costs into a daily and monthly budget, with thinking tokens included.
  3. Build a latency budget from queue wait, time to first token, thinking and output speed.
  4. Explain why a feature that runs in the background should promise freshness rather than speed.

Builds on 2 lessons

Start with lesson 315 min
  1. Calling a model API: request, response and cost15 min
  2. Model API errors, rate limits and retries17 min

Video lesson · coming soon

The launch question has two numbers

Deskora is a help desk app. A shop’s support team uses it to answer customer emails from one shared inbox. Each customer problem becomes a ticket: one customer’s whole conversation with the support team, message by message.

Tomás, a Deskora engineer, has built a short summary at the top of long tickets. Support agents like Priya read it instead of the whole thread.

Launch is in two weeks. Deskora’s product lead asks Tomás: what will summaries cost each month, and how fast will they be?

From his first test in Calling a model API, Tomás knows one summary costs about $0.0006 on Claude Haiku 5.5: about $38 a day for 40,000 summaries. He almost replies “about $1,140 a month.”

That number prices one call, not a feature that runs all day on tickets that keep growing. This lesson answers one question: how do you turn the cost and time of one call into a monthly budget and a latency promise you can keep?

Of Deskora’s 120,000 new tickets a day, 40,000 get a summary. That share and every other number below are estimates or assumptions for this lesson’s scenario.

One summary is many calls

The to estimate is one completed task, not one API call. Here the task is “Priya opens a long ticket and finds a summary that matches it.” Several calls stand behind it.

  • The first summary. Deskora makes it in the background when a ticket reaches 8 messages, the threshold the design in the next lesson uses.
  • Refreshes. A summary of messages 1–8 says nothing about message 30, so Deskora makes a new one as the ticket grows.
  • Retries. Calls that fail with a 429 or a 5xx are sent again; the Python SDK retries them twice by default 5. See Model API errors, rate limits and retries.

Each refresh is a new one-turn call with the whole thread, not a follow-up question. So earlier thinking is not resent, but the thread is, and it is longer every time.

Check yourselfAnswer in your head first, then reveal
A long ticket gets its first summary at message 8 and grows to 60 messages. If Deskora makes a new summary after every new message, how many calls does that one ticket make, and do they all cost the same?Show answerHide answer
53 calls: one at message 8 and one for each of the next 52. Each sends the whole thread, so the call at message 60 sends about seven times the input of the first. Not one call per ticket: that is what the per-call estimate assumed.

Diagram not drawn yet: summary-calls-long-ticket.svg

A horizontal axis of message numbers from 0 to 60 for one long Deskora ticket. Vertical bars mark a summary call at messages 8, 13, 18 and so on up to 58: 11 bars. Each bar’s height is that call’s input tokens, rising from about 2,150 at message 8 to about 14,650 at message 58. A label reads: one call at the end, 15,150 input tokens; 11 refreshes, 92,400 input tokens in total.

One long ticket, refreshed every 5 messages: 11 calls, each bigger than the last. Together they send about 6 times the input of one final summary.

How often to refresh is Tomás’s choice, and it moves the bill more than anything else.

The refresh rule sets the bill

When should Deskora make a new summary for a ticket that keeps growing?

After every new message.

Always current. But a 60-message ticket makes 53 calls, and the day costs about $904 on Claude Haiku 5.5: 24 times the per-call estimate.

When an agent opens a ticket that has changed.

No call is wasted on unopened tickets. But Priya waits for the model on every changed ticket she opens, and the call count depends on how often agents open tickets, which Deskora hasn’t measured.

Why this choice

The summary saves Priya about five minutes of reading old messages. Four new messages take her about a minute. So a summary a few messages behind keeps most of its value, at about a fifth of the every-message cost.

When the choice changes. Chat tickets get messages seconds apart, so a quiet period, such as two minutes without a new message, fits better than a count. If agents open tickets less often than every 5 messages, refreshing on open makes fewer calls, at the price of a wait. The design in the next lesson settles the rule with real data.

Typical and long tickets, priced

Tomás prices two cases, using the shared token numbers from lesson 03. As of October 2026, Claude Haiku 5.5 costs $0.10 per million input tokens and $0.50 per million output tokens, for prompts up to 100,000 tokens 1. Thinking tokens are billed as output tokens 4.

The formula

cost per task = Σ over its calls (input tokens × input price + (thinking + summary tokens) × output price) × (1 + retry rate); cost per day = Σ over cases (tickets × cost per task)

Prices are per million tokens. The calls are the first summary and each refresh.

Typical ticket: 3 calls

input = 3 × 150 + (8 + 13 + 18) × 200 = 8,250 tokens; output = 3 × (300 + 80) = 1,140 tokens; cost = 8,250 × $0.10 ÷ 1,000,000 + 1,140 × $0.50 ÷ 1,000,000 = $0.000825 + $0.00057 = $0.001395

About 20 messages of about 200 tokens each, plus the 150-token system prompt on every call. Assumed per call: 300 thinking tokens and an 80-token summary.

Long ticket: 11 calls

input = 11 × 150 + (8 + 13 + … + 58) × 250 = 1,650 + 90,750 = 92,400 tokens; output = 11 × (800 + 100) = 9,900 tokens; cost = $0.00924 + $0.00495 = $0.01419

About 60 messages of about 250 tokens each. Assumed per call: 800 thinking tokens and a 100-token summary; using these on early calls overestimates slightly.

Retries

typical: $0.001395 × 1.02 = $0.0014229; long: $0.01419 × 1.02 = $0.0144738

Assumed: 2% of calls are sent again, each counted as fully billed to be safe.

One day

30,000 × $0.0014229 + 10,000 × $0.0144738 = $42.69 + $144.74 = $187.43, about $187

40,000 summarized tickets a day, one in four of them long (shared scenario estimate).

One month

$187.43 × 30 days = about $5,600

Five times the $1,140 Tomás nearly sent.

The thinking amounts are the weakest assumption. If thinking runs at twice the assumed amount, the month is about $7,700. After launch, usage.output_tokens_details.thinking_tokens reports the real amount on every call 4.

Diagram not drawn yet: summary-cost-by-ticket-case.svg

Two horizontal 100% bars. Top bar, share of summarized tickets: typical 75%, long 25%. Bottom bar, share of daily cost on Claude Haiku 5.5: typical $46, about 24%; long $150, about 76%.

Long tickets are a quarter of the volume and three quarters of the cost.

A long ticket is 3 times longer than a typical one but costs about 10 times as much. It gets more calls, and each call sends more, so cost grows with roughly the square of length. One average ticket would sit where little of the money is.

Tomás keeps the estimate as code, so he can rerun it with logged numbers after launch:

python
from dataclasses import dataclass

# claude-haiku-5-5, US dollars per million tokens, as of October 2026.
# Valid for prompts up to 100,000 tokens; above that Haiku 5.5 charges 5x.
INPUT_PRICE = 0.10
OUTPUT_PRICE = 0.50  # thinking tokens are billed at this price too

SYSTEM_TOKENS = 150
FIRST_AT = 8         # first summary at message 8
REFRESH_EVERY = 5    # new summary after every 5 new messages
RETRY_RATE = 0.02    # share of calls sent again, each billed in full
SUMMARIZED_PER_DAY = 40_000


@dataclass
class TicketCase:
    name: str
    share: float             # fraction of summarized tickets
    messages: int            # messages when the ticket goes quiet
    tokens_per_message: int
    thinking_tokens: int     # assumed, per call
    summary_tokens: int      # assumed, per call


def cost_per_ticket(case: TicketCase) -> float:
    total = 0.0
    for n in range(FIRST_AT, case.messages + 1, REFRESH_EVERY):
        input_tokens = SYSTEM_TOKENS + n * case.tokens_per_message
        output_tokens = case.thinking_tokens + case.summary_tokens
        total += (input_tokens * INPUT_PRICE + output_tokens * OUTPUT_PRICE) / 1_000_000
    return total * (1 + RETRY_RATE)


cases = [
    TicketCase("typical", 0.75, 20, 200, 300, 80),
    TicketCase("long", 0.25, 60, 250, 800, 100),
]
daily = 0.0
for case in cases:
    per_ticket = cost_per_ticket(case)
    day = per_ticket * case.share * SUMMARIZED_PER_DAY
    daily += day
    print(f"{case.name}: ${per_ticket:.5f} per ticket, ${day:,.2f} a day")
print(f"total: ${daily:,.2f} a day, about ${daily * 30:,.0f} a month")

The budget is ready. The second question is time.

Thinking is most of the wait

A summary’s has four parts. The job waits in Deskora’s queue for a free . The model reads the prompt; time to first token is the time from sending the request to the first output token 3. Then it writes its thinking, and only then the summary.

Writing time is output tokens divided by output speed, in tokens per second. Latency depends on prompt length, output length and thinking effort 2. The provider ranks models by speed but gives no seconds, so every time below is an assumption to measure.

The formula

time to summary = queue wait + time to first token + (thinking tokens + summary tokens) ÷ output speed

Assumed: 2 s of queue wait, 100 output tokens a second, and a time to first token of 0.5 s for the typical prompt and 1 s for the long one.

Typical ticket

2 s + 0.5 s + (300 + 80) ÷ 100 tokens/s = 2 + 0.5 + 3.8 = 6.3 s

Long ticket

2 s + 1 s + (800 + 100) ÷ 100 tokens/s = 2 + 1 + 9 = 12 s

Long ticket with one retry

12 s + 1 s backoff + (1 s + 9 s) = 23 s

The retry repeats the whole call after a short wait. Assumed backoff: 1 s.

On the long ticket, thinking is 8 of the 12 seconds. A shorter prompt barely moves this budget; less thinking moves it most.

If Priya waited for the summary, streaming would help little: thinking comes before any text, so she would see an empty box for about 9 seconds.

Promise freshness, not speed

Deskora doesn’t make her wait. The summary is made in the background, before anyone opens the ticket, so 12 seconds versus 6 never reaches her.

Diagram not drawn yet: summary-wait-vs-freshness.svg

Two panels. Left, summary made on open: Priya opens a 60-message ticket, then a bar shows about 12 seconds of waiting, mostly thinking, before the summary appears. Right, summary made in the background: refreshes ran at messages 50 and 55 while nobody was looking; Priya opens the ticket at message 58 and sees the summary at once. A label shows it covers messages 1 to 55, and messages 56 to 58 sit just below it, unread by the model.

On open, Priya waits for the model. In the background she never waits, but the summary can be a few messages behind.

The risk instead is staleness: a summary of a ticket that has moved on. So Tomás promises freshness:

  • Agents never wait for a summary.
  • It covers every message except at most the newest 4, plus any that arrived in the last 30 seconds.

The 30 seconds is the 23-second worst case plus headroom, and an assumption until measured.

Before launch, Tomás runs the summarizer over 200 old tickets, logging usage and the time of every call. That check costs about $1, and its numbers replace the assumptions.

Levers that come later

The estimate uses list prices. Later lessons lower them:

Whether Haiku’s summaries are good enough is a question for Choosing a model.

Try it yourself

Time one small call and read its usage. It costs at most about $0.0005 on Claude Haiku 5.5. If you don’t have an API key, skip this.

bash
curl -s -o reply.json -w "total: %{time_total} s\n" https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -d '{"model": "claude-haiku-5-5", "max_tokens": 1000, "system": "Summarize the support ticket in exactly three lines.", "messages": [{"role": "user", "content": "Ines: My order 7731 arrived without the charger.\nOmar (agent): Sorry. I will send one today.\nInes: It is now a week later and nothing has come. Can you check?"}]}'
python3 -c 'import json; print(json.load(open("reply.json"))["usage"])'

The output looks like this; the numbers here are illustrative and yours will differ:

text
total: 2.4 s
{'input_tokens': 85, 'output_tokens': 170, 'output_tokens_details': {'thinking_tokens': 110}, ...}

What to look for: output_tokens ÷ total time is a rough, low estimate of output speed, since the total includes time to first token. Run it five times; the spread between runs is why the budget has a worst case.

Where this shows up

The next lesson designs the feature that has to keep this budget and this promise.

Common mistakes

  • Pricing the call, not the task. Count first summaries, refreshes and retries.
  • Using one average ticket. Long tickets get more calls and bigger calls.
  • Leaving thinking out. It is billed as output and is most of the wait.
  • Promising speed for background work. Promise freshness, and measure it.

Check your understanding

Check yourselfAnswer in your head first, then reveal
Tomás changes the refresh rule from every 5 messages to every 10. Roughly what happens to the cost of a long ticket, and what does Priya notice?Show answerHide answer
The long ticket drops from 11 calls to 6 (messages 8, 18 … 58), so its cost roughly halves. Priya sees summaries up to 9 messages behind, about two minutes of reading. Not “nothing changes for her”: the saving is paid for in freshness.
Check yourselfAnswer in your head first, then reveal
After launch, logs show long-ticket summaries use 1,600 thinking tokens per call, not 800. Which part of the estimate moves, and does the latency promise still hold?Show answerHide answer
Output per long call grows from 900 to 1,700 tokens, so the month rises. Under these assumptions a long summary now takes about 20 s instead of 12, and a retry pushes it near 40 s, breaking the 30-second promise. Not the input cost: the thread didn’t change.
Check yourselfAnswer in your head first, then reveal
Deskora moves summaries from the background to “make one when the agent opens the ticket”. Which latency number now matters most?Show answerHide answer
The time until Priya sees text: time to first token plus all the thinking, about 9 s on a long ticket under these assumptions. Not the freshness lag: the summary is now current, but she waits for it every time.

Summary

  1. Price the completed task, not the call. With refreshes and retries, Deskora’s summaries cost about $5,600 a month on Claude Haiku 5.5 (as of October 2026), five times the per-call estimate.
  2. The refresh rule sets the bill: about $904 a day for every message, about $187 for every 5.
  3. Price a typical and a long case: long tickets are a quarter of the volume and three quarters of the cost.
  4. Latency = queue wait + time to first token + output tokens ÷ output speed. Thinking is most of it; measure every part.
  5. A background feature promises freshness, not speed: agents never wait, and the summary covers all but the newest few messages.

Report a mistake

Tell us what’s wrong in this lesson and we’ll fix it.