Module 3: Measuring Cost and Tokens per Run

The `claude-sonnet-5` Pricing

Description

With estimate_tokens already built, the second piece needed to calculate money is missing: the price. This lesson fixes, once and for the rest of this guide, claude-sonnet-5's pricing constant — a number that never gets re-researched or recalculated in any later module. Every lesson that needs to calculate a cost, from lesson 05 onward, is going to cite this constant without justifying it again.

A language model's real pricing isn't a single number — it's, at minimum, two: one for input tokens, one for output tokens, and this lesson confirms, with real executed arithmetic, the asymmetry between them lesson 02 already previewed.

Connection to the module

This lesson adds the two constants that complete observability/cost_calculator.py's second piece: INPUT_PRICE_CENTS_PER_MILLION_TOKENS and OUTPUT_PRICE_CENTS_PER_MILLION_TOKENS. Lesson 05 combines them with estimate_tokens (lesson 03) into estimate_cost_cents, the module's central function.


The constant: list price, cited, fixed for the whole guide

claude-sonnet-5 pricing (list price, verified against the official Claude documentation): $3.00 per million input tokens, $15.00 per million output tokens.

A launch promotional price exists — $2.00/$10.00 per million tokens — valid through August 31, 2026. This guide fixes its constant at the list price ($3.00/$15.00), not the promotional one, for a simple reason: the list price is the number that stays true after the promotion ends, and this guide is going to remain published after that date. Every calculation in this guide uses the list price; the promotional one is mentioned, here, once, as an honest note — never as the operating number.

In cents, so this guide's arithmetic always stays in int — the same style you already used for every Reservo price:

INPUT_PRICE_CENTS_PER_MILLION_TOKENS = 300    # $3.00 / 1M tokens -- claude-sonnet-5, precio de lista
OUTPUT_PRICE_CENTS_PER_MILLION_TOKENS = 1500  # $15.00 / 1M tokens -- claude-sonnet-5, precio de lista

300 and 1500 are cents per one million tokens — not per individual token. This scale matters: dividing 300 / 1_000_000 directly would give a fraction of a cent per token, impossible to represent as int without losing all precision. Lesson 05 shows how to multiply first and divide after, in exactly that order, so the integer arithmetic never collapses to 0 prematurely.


Confirming the asymmetry: the output token costs five times more

Lesson 02 previewed it as a concept; here it gets confirmed with real numbers. Compare the cost of the same amount of tokens, depending on whether they're input or output:

def estimate_cost_cents(input_tokens, output_tokens):
    return (
        input_tokens * INPUT_PRICE_CENTS_PER_MILLION_TOKENS
        + output_tokens * OUTPUT_PRICE_CENTS_PER_MILLION_TOKENS
    ) // 1_000_000


for n in (1_000, 10_000, 100_000, 1_000_000):
    costo_si_es_entrada = estimate_cost_cents(n, 0)
    costo_si_es_salida = estimate_cost_cents(0, n)
    print(f"{n:>9} tokens -> como entrada: {costo_si_es_entrada:>5} centavos   como salida: {costo_si_es_salida:>5} centavos")

What to expect:

     1000 tokens -> como entrada:     0 centavos   como salida:     1 centavos
    10000 tokens -> como entrada:     3 centavos   como salida:    15 centavos
   100000 tokens -> como entrada:    30 centavos   como salida:   150 centavos
  1000000 tokens -> como entrada:   300 centavos   como salida:  1500 centavos

In every row, the cost as output is exactly five times the cost as input — 15 versus 3, 150 versus 30, 1500 versus 300 — the same ratio as between $15.00 and $3.00. The first row — 1,000 tokens — shows something more: as input, 1,000 tokens cost 0 cents (integer division rounds down again, exactly as in lesson 03), but as output they already cost 1 cent — the asymmetry is real enough that, for small token amounts, it can be the difference between "costs nothing, according to this arithmetic" and "already costs something measurable."


Why this asymmetry matters for an agent's design

This isn't a billing curiosity — it has a direct practical consequence for anyone designing an agent like Reservo. An agent that reads lots of context — a long history, extensive tool results — but replies briefly pays, proportionally, much less than an agent that generates long, verbose responses, or that repeatedly requests tools with complex arguments. claude-sonnet-5's output cost weighs five times more per token than input — so, at an equal token count, output is what dominates the bill.

This observation isn't an instruction to optimize anything — that, precisely, is cost-optimization-caching-guide's topic, not this guide's. It's simply the underlying reason lesson 05 is going to show output_tokens as a column worth watching closely in every CostReport: not because it's more "interesting" than input_tokens, but because, token for token, it costs more.


Why this asymmetry exists: a computational reason, not an arbitrary one

It's worth understanding where the difference comes from, not just memorizing that it exists. Lesson 02 already explained a language model generates its response autoregressively: one token at a time, and every new token requires a full pass of the model over all the context accumulated up to that point. Processing input text — the question, the history, the tool_results — is, in contrast, work the model can do in parallel, all at once, over all the text it already has ahead of it. Generating a hundred output tokens involves, roughly, a hundred sequential passes of the model; reading a hundred input tokens involves, roughly, a single pass over all hundred at once. That difference in real computational work — sequential versus parallel — is the underlying reason almost every language-model provider, not just Claude, charges more for the output token than for the input token. The exact ratio (5x for claude-sonnet-5) is specific to this model and this date; the direction of the asymmetry — output costing more than input — is structural.

The mistake of using an "average" price: quantified

Someone wanting to simplify the formula might be tempted to use a single "average" price — for example, (300 + 1500) / 2 = 900 cents per million, applied to the sum of input and output tokens — instead of keeping the two constants separate. This lesson closes by confirming, with real numbers, why that simplification produces an incorrect result, and how wrong it can get depending on the run's profile.

def blended_cost_cents(input_tokens, output_tokens):
    """La forma INCORRECTA: un solo precio promedio, aplicado al total de
    tokens sin distinguir dirección. Existe aquí solo para medir el error
    que produce -- nunca se usa en el resto de esta guía."""
    blended_price = (INPUT_PRICE_CENTS_PER_MILLION_TOKENS + OUTPUT_PRICE_CENTS_PER_MILLION_TOKENS) // 2
    return ((input_tokens + output_tokens) * blended_price) // 1_000_000


n = 100_000

# Perfil 1: parecido al run de Ana (lección 05) -- entrada domina levemente.
correcto_ana = estimate_cost_cents(64 * n, 56 * n)
incorrecto_ana = blended_cost_cents(64 * n, 56 * n)

# Perfil 2: un agente verboso -- responde mucho más de lo que lee.
correcto_verboso = estimate_cost_cents(30 * n, 150 * n)
incorrecto_verboso = blended_cost_cents(30 * n, 150 * n)

for label, correcto, incorrecto in [
    ("perfil tipo Ana (30 in / 56 out aprox.)", correcto_ana, incorrecto_ana),
    ("perfil verboso (30 in / 150 out)", correcto_verboso, incorrecto_verboso),
]:
    print(f"{label}")
    print(f"  correcto (precios separados) : {correcto:>6} centavos = ${correcto / 100:.2f}")
    print(f"  incorrecto (precio promedio) : {incorrecto:>6} centavos = ${incorrecto / 100:.2f}")
    print(f"  error                        : {incorrecto - correcto:>6} centavos = ${(incorrecto - correcto) / 100:.2f}")
    print()

What to expect:

perfil tipo Ana (30 in / 56 out aprox.)
  correcto (precios separados) :  10320 centavos = $103.20
  incorrecto (precio promedio) :  10800 centavos = $108.00
  error                        :    480 centavos = $4.80

perfil verboso (30 in / 150 out)
  correcto (precios separados) :  23400 centavos = $234.00
  incorrecto (precio promedio) :  16200 centavos = $162.00
  error                        :  -7200 centavos = $-72.00

Two profiles, two errors of opposite sign. In the first case — similar to Ana's run, with input and output relatively balanced — the average price overestimates the real cost by nearly five dollars per 100,000 runs. In the second case — an agent that generates far more than it reads, a realistic pattern for an agent drafting long responses — the average price underestimates the real cost by seventy-two dollars: the average price doesn't know that run spent, proportionally, much more on the token that costs five times as much. No single price — average or otherwise — can replace the two separate constants without introducing an error that flips direction depending on the run's profile. This is the definitive reason estimate_cost_cents, throughout this guide, always receives input_tokens and output_tokens as two separate arguments, never as a single total.


Common mistakes

  1. Using the promotional price ($2.00/$10.00) as the operating constant. This guide mentions it once, as an honest note — the list price ($3.00/$15.00) is what's used in every calculation, from this lesson through Module 8's close, without exception.

  2. Dividing 300 (or 1500) directly by a small number of tokens, expecting a meaningful result. 300 tokens_de_entrada / 1_000_000 gives a tiny fraction of a cent — the real formula (lesson 05) multiplies first by the token count and divides at the end, so integer arithmetic doesn't lose all the information prematurely.

  3. Forgetting the price is expressed per million, not per token. INPUT_PRICE_CENTS_PER_MILLION_TOKENS = 300 doesn't mean "300 cents per token" — it means 300 cents per every million tokens. Confusing the scale is the easiest mistake to make when reading these constants for the first time.

  4. Assuming the 5x asymmetry is a general rule for every language model. It's claude-sonnet-5's specific ratio, cited from the official Claude documentation on the date this constant was fixed. Other models, and claude-sonnet-5 itself in the future, may have a different ratio — that's why this guide cites the source and the date, instead of presenting the number as a universal truth.

  5. Recalculating or "double-checking" the pricing in a later lesson. This guide's DISEÑO is explicit: this constant gets fixed once, here, and the lessons and modules that follow reuse it by citing it, without looking it up or questioning it again.


Exercises

Exercise 1: Calculate the cost of 500,000 output tokens (Easy)

Without running anything first: using this lesson's formula, how many cents do 500,000 output tokens cost? Confirm with code.

See solution

500_000 * 1500 // 1_000_000 = 750_000_000 // 1_000_000 = 750 cents.

print(estimate_cost_cents(0, 500_000))

Expected output:

750

Explanation: 750 cents is $7.50 — exactly half of what 1,000,000 output tokens would cost ($15.00), because 500,000 is exactly half a million. Integer arithmetic introduces no rounding error in this case because 500_000 * 1500 = 750_000_000 divides exactly by 1_000_000.

Exercise 2: Find the point where input and output cost the same in dollars (Medium)

How many input tokens does it take to cost the same as 200,000 output tokens? First calculate the cost of 200,000 output tokens, and then find — with code, by testing values — the amount of input tokens that produces the same cost in cents.

See solution
costo_salida = estimate_cost_cents(0, 200_000)
print("costo de 200.000 tokens de salida:", costo_salida, "centavos")

# Como la salida cuesta 5x más por token, hacen falta 5x más tokens de entrada.
tokens_entrada_necesarios = 200_000 * 5
print("tokens de entrada necesarios     :", tokens_entrada_necesarios)
print("costo de esos tokens de entrada  :", estimate_cost_cents(tokens_entrada_necesarios, 0), "centavos")

Expected output:

costo de 200.000 tokens de salida: 300 centavos
tokens de entrada necesarios     : 1000000
costo de esos tokens de entrada  : 300 centavos

Explanation: it takes exactly five times more input tokens (1,000,000 versus 200,000) to match the output's cost — the direct numeric consequence of the 5x asymmetry confirmed in the worked example. This illustrates, with a concrete case, why "amount of tokens" and "cost" aren't interchangeable without knowing whether they're input or output.

Exercise 3: Compare the real cost against the promotional one, and quantify the savings (Hard)

Using the promotional price mentioned in this lesson ($2.00/$10.00 per million tokens, in cents: 200/1000), write an alternative version of estimate_cost_cents that uses those constants. Calculate a run's cost with 10,000 input tokens and 5,000 output tokens under both prices (list and promotional), and calculate the percentage savings the promotional would represent.

See solution
PROMO_INPUT_PRICE_CENTS_PER_MILLION_TOKENS = 200
PROMO_OUTPUT_PRICE_CENTS_PER_MILLION_TOKENS = 1000


def estimate_cost_cents_promo(input_tokens, output_tokens):
    return (
        input_tokens * PROMO_INPUT_PRICE_CENTS_PER_MILLION_TOKENS
        + output_tokens * PROMO_OUTPUT_PRICE_CENTS_PER_MILLION_TOKENS
    ) // 1_000_000


costo_lista = estimate_cost_cents(10_000, 5_000)
costo_promo = estimate_cost_cents_promo(10_000, 5_000)
ahorro_pct = (1 - costo_promo / costo_lista) * 100 if costo_lista else 0

print("costo con precio de lista     :", costo_lista, "centavos")
print("costo con precio promocional  :", costo_promo, "centavos")
print(f"ahorro del promocional        : {ahorro_pct:.1f}%")

Expected output:

costo con precio de lista     : 10 centavos
costo con precio promocional  : 7 centavos
ahorro del promocional        : 30.0%

Explanation: the promotional would save 30% on this specific run — consistent with both promotional prices (input and output) being, roughly, two-thirds of the list prices. This exercise exists only to confirm the magnitude of the difference; this guide's DISEÑO is explicit that the operating constant, the one used in every lesson from here on, remains the list price — the promotional one never replaces estimate_cost_cents in the rest of this guide.


Summary and next step

  • We fixed claude-sonnet-5's pricing constant: $3.00/1M input tokens, $15.00/1M output tokens, list price — cited from the official Claude documentation, with the honest note on the promotional price valid through August 31, 2026.
  • We confirmed, with real execution, the 5x asymmetry between output token cost and input token cost, across four different orders of magnitude (1,000 to 1,000,000 tokens).
  • We explained that asymmetry's practical consequence: an agent that replies verbosely pays more, token for token, than one that reads a lot of context but replies briefly — without this being, yet, an instruction to optimize anything.
  • This constant gets fixed once; the rest of this guide cites it without re-researching or recalculating it.

Next lesson: 05 — Cost per Run, in Cents. With estimate_tokens (lesson 03) and the fixed pricing (this lesson) now ready, we combine them into estimate_cost_cents and cost_for_run, run over a real Reservo run, with a cost breakdown per tool call.


Additional resources

  1. Anthropic — Pricing — The official source for claude-sonnet-5's list price this lesson fixes as a constant.
  2. Anthropic — Models overview — Price comparison across the Claude model family, useful for understanding where claude-sonnet-5 sits.
  3. Python — arithmetic operators — Multiplication and integer division, the foundation of estimate_cost_cents.
  4. Anthropic — Building effective agents — On why an agent's design — how verbose it replies, how much context it needs to read — has direct cost consequences.
  5. Python 3.14 — What's New — The version every line of code in this lesson ran on.