Module 7: Cost Control Ai In Production And Mcp
2. Cost control: where the money leaks
Description
By the end of this lesson you'll be able to explain what a token is without falling back on the word "token," you'll know why the text the model writes is charged more than the text it receives, and you'll be able to calculate the cost of a concrete execution and project it to a month with the real volume of your instance. Above all, you'll come out with cost_log working: a record that writes one row per AI execution, built with a Code node that only reads data that already comes in the item.
This matters because it's the foundation of everything else. The six lessons that follow will ask you for decisions —do I raise or lower Max Iterations?, this model or that one?, local or API?, do I expose this over MCP?— and none of those decisions can be made with opinions. They're made with numbers. Terra Market can't answer why its bill tripled because it never measured, and that's the real problem: not that the spend went up, but that it went up without anyone being able to see it. A system that spends a lot but is measured is a system you administer; one that spends little but isn't measured is a surprise waiting its turn.
Connection to the module: lesson 1 laid out the problem and gave you the case. This lesson builds the instrument. Everything that comes after uses it: lesson 3 measures the effect of Max Iterations with these numbers, lesson 4 compares models with these numbers, lesson 5 calculates the break-even point with these numbers, and the project in lesson 8 puts a cap and an alert on this same record. An important boundary: here you build the cost record, but the observability layer where it lives —structured logging, metrics store, alerts— is module 4 of this guide. If you already set up that layer, cost_log is one more metric that plugs in there. If not, this lesson gives you the minimal version that works on its own.
What exactly is being charged
Let's start at the bottom step, because almost everyone skips it and then doesn't understand their bill.
When your workflow talks to a language model, it doesn't send it "a message." It sends it a stack of text, and the model returns another stack of text. What's charged is the amount of text that travels in each direction, measured in a unit called a token.
A token is a little piece of text. It's not a letter and not exactly a word: it's a fragment of the size the model uses internally to chop up language. A short, common word is usually one token; a long or infrequent word gets split into two or three; spaces and punctuation count too. As a mental rule of thumb for English, an average word runs between one and two tokens, and a hundred-word paragraph is around 150 tokens. Don't take it as an exact formula —each provider chops differently and the numbers change between models—; take it as the scale.
Think of it like the bill from a shop that charges by the part. You don't pay the shop "to fix the car": you pay for each screw, each nut, and each minute. If you send the car in with a 300-page manual attached "just in case," the shop reads it, and it charges you for reading it. It doesn't matter that it wasn't needed.
Now the important part, and it's the one that changes how you design: the price isn't the same in both directions.
| Direction | What it's called | What it includes | Relative price |
|---|---|---|---|
| What you send | Input tokens | The system prompt, the user's message, the conversation history, the tool descriptions, the tool results | The cheaper of the two |
| What the model returns | Output tokens | The response text, and also the internal reasoning if the model produces it | Several times more expensive than input |
Why output costs more. It's worth understanding, because it explains several design decisions you'll make. Processing the input is reading: the model can look at all the text at once, in parallel. Producing the output is writing, and writing is sequential: each little piece of the response is generated after the previous one, and to generate it the model has to reconsider everything up to that point. A thousand-token input is processed in a single pass; a thousand-token output is produced in a thousand steps. The price reflects that work.
Think of it this way: reading a twenty-page contract takes you half an hour. Writing a twenty-page contract takes you two days. It's the same amount of paper and it's not the same work.
From that comes a design principle that holds for everything you do in production:
Reducing output pays off more than reducing input. If you have to choose where to cut, start with what the model writes, not with what it reads.
That has an immediate consequence for Terra Market. ticket-classify returns a category —three or four words—, so its cost is dominated by input. reply-draft returns a full reply draft —two or three hundred words—, so its cost is dominated by output. They're two different optimization problems and you don't attack them the same way. In ticket-classify you'll look at the prompt and the history; in reply-draft you'll look at how much text you ask it to write.
The formula, and where to get the prices
The calculation is simple arithmetic. The only thing that changes between providers is the two prices:
cost_of_one_call =
(input_tokens / 1,000,000) × input_price_per_million
+ (output_tokens / 1,000,000) × output_price_per_million
Prices are published per million tokens, and that's why the division. It's a large unit on purpose: an individual call costs a tiny fraction, and only when you multiply by thousands of executions does the number become readable.
You won't find in this guide a table with those two prices. It's deliberate. The prices of AI APIs change —they almost always go down, but they change—, there are batch discounts, there's prompt caching that makes repeated parts cheaper, and there are differences between the direct provider and the clouds that resell it. A price table printed in a course is a trap: someone will use it to budget a year from now and will budget wrong. What does serve you forever is knowing where to look for them:
| Provider | Where the current price is |
|---|---|
| Anthropic (Claude) | platform.claude.com/docs/en/pricing |
| OpenAI | openai.com/api/pricing |
| Google (Gemini) | ai.google.dev/pricing |
| Local models with Ollama | Zero per token — but the server costs money, and that's lesson 5 |
When you build your cost record, the price goes in as a configurable constant, not embedded in the code. When the provider changes it —or when you change models— you want to touch one single place. In n8n that means an environment variable, an Edit Fields node at the start of the workflow, or a row in a data table. Any of the three works; what doesn't work is having it written in four different nodes.
Worked example: how much a Terra Market ticket costs
Let's do the complete calculation with the right structure and with symbolic prices, so you see the mechanics. Substitute P_IN and P_OUT with the current prices of whatever provider you use the day you do this.
Terra Market measures a typical execution of ticket-classify and finds this:
One execution of ticket-classify (typical case, 1 single call to the model)
Input
system prompt (classification instructions) ≈ 320 tokens
descriptions of the 2 connected tools ≈ 180 tokens
the customer's ticket ≈ 240 tokens
───────────
total input ≈ 740 tokens
Output
the chosen category + one line of justification ≈ 35 tokens
The calculation for that execution:
cost = (740 / 1,000,000) × P_IN + (35 / 1,000,000) × P_OUT
= 0.00074 × P_IN + 0.000035 × P_OUT
What to expect when you do this calculation with real prices. You'll get a number with five or six zeros after the decimal point, and your first reaction will be "this can't be the problem." It's the correct reaction and it's the trap. The cost of an AI execution is always imperceptible. The cost shows up in the multiplication, and that's why the second step isn't optional:
Monthly projection of ticket-classify
300 executions/day × 30 days = 9,000 executions/month
monthly input = 9,000 × 740 = 6,660,000 tokens ≈ 6.66 million
monthly output = 9,000 × 35 = 315,000 tokens ≈ 0.32 million
monthly cost = 6.66 × P_IN + 0.32 × P_OUT
Now the number is readable: 6.66 million input tokens a month, for a workflow that does one small thing. Notice the proportion, because it's the lesson of this example: of those 740 input tokens, 500 are fixed —the system prompt and the tool descriptions travel identically in every one of the 9,000 calls— and only 240 are the actual ticket. Two-thirds of what you pay is text that repeats.
And here's the lever almost nobody uses: if you trim the system prompt from 320 to 160 tokens without losing classification quality, you save 160 tokens × 9,000 executions = 1.44 million tokens a month, every month, without touching anything else. It's the kind of optimization that doesn't show in an individual execution and that shows up a lot on the bill.
Now repeat the same calculation for reply-draft and see how the profile changes:
One execution of reply-draft (typical case)
Input ≈ 1,400 tokens (longer system prompt, with the return policy)
Output ≈ 380 tokens (the reply draft)
120 executions/day × 30 days = 3,600 executions/month
monthly input = 3,600 × 1,400 = 5.04 million tokens
monthly output = 3,600 × 380 = 1.37 million tokens
reply-draft runs less than half as often as ticket-classify and consumes a comparable amount of input tokens — plus an output four times larger, which is also charged at the expensive price. If someone asks you "which of the two do we optimize first?", the answer is no longer an opinion.
Watch out for a temptation. These numbers are Terra Market's, which is a made-up company. Don't copy them to your instance. Measure them in yours, which is exactly what you build in the next section.
The five leaks
With the arithmetic clear, let's see where the money escapes in practice. These five explain the vast majority of runaway bills, and it's worth knowing them by name so you can look for them.
Leak 1 — The agentic loop with no reasonable ceiling. It's the most expensive and the most silent. An agent doesn't make one call to the model: it makes as many as it needs. Each pass sends the whole accumulated context again plus the new part, so pass number ten costs quite a bit more than the first. If the ceiling is high, one bad execution can cost ten or twenty times what a normal one costs, and come back green. It's Terra Market's case and it's all of lesson 3.
Leak 2 — The prompt that grows by accumulation. Nobody decides "I'm going to double the system prompt." What happens is that every time the agent gets an odd case wrong, someone adds an exception sentence to cover it. Twelve exceptions later, the prompt has 900 words and travels in full on every call, forever. It's a constant addend multiplied by all your volume. How to detect it: compare today's prompt with the one from three months ago. If you've never trimmed it, it has grown.
Leak 3 — The context that drags along. In conversations with memory, each turn sends the previous turns again. Turn ten pays for the previous nine. In a support chat that lasts twenty messages, the last messages cost several times what the first ones cost. How to detect it: if your agent has memory connected, look at the token consumption of the first turn against the last turn of a long conversation. The difference tells you whether you need a bounded memory window.
Leak 4 — The retry that pays again. In module 2 you learned to put Retry On Fail on the fragile nodes, and it's a good practice. On an AI node it has a nuance it doesn't have on an HTTP Request: each retry pays for the full call again. Three attempts on a call that fails twice cost three calls. If the provider has a bad afternoon and your failure rate goes from 1% to 15%, that line multiplies without anyone changing anything. How to detect it: cross your AI-node error rate with your retry configuration. If you have three attempts and a 10% failure rate, you're paying 20% extra.
Leak 5 — The executions nobody asked for. Test workflows that were left published. Scheduled triggers every five minutes when the business needs them once an hour. Duplicated workflows running in parallel because someone cloned one to test and didn't unpublish it. It's the least intellectually interesting leak and the most frequent. How to detect it: list your published workflows that contain an AI node and, for each one, answer "who consumes its result?". If the answer is "nobody," you found it.
Notice a pattern: all five are invisible to traditional monitoring. None produces an error, none lowers the success rate, none triggers an availability alert. The only surface where you see all of them is the one you're about to build.
Building cost_log
Now the concrete part. Let's instrument an AI workflow so it records its own consumption.
Where the numbers come from
When an AI node finishes, n8n shows information about that call's consumption in the execution panel — typically an object with the input tokens, the output tokens, and the total. The exact name of those fields and the exact place where they appear depend on the model sub-node and on your version of n8n, so the first step isn't skipped and isn't guessed:
- Open the workflow you want to instrument and run it once with real data.
- In the execution panel, open the agent node (or the model sub-node) and look at its output and its logs.
- Locate with your own eyes where the tokens are. Note the exact path: whether it's in
json.tokenUsage, whether it's injson.usage, whether the fields are calledpromptTokensandcompletionTokensorinput_tokensandoutput_tokens.
That reconnaissance step takes two minutes and saves you half an hour of writing code against a field that doesn't exist. Each version and each provider has its own shape, and what you see on your screen overrides whatever any guide says.
The Code node that counts and calculates
With the path identified, the calculation is a Code node. And here it's worth being explicit about why the Code node is the right tool for this in n8n 2.0, because there's a lot of confusion about it.
Since n8n 2.0 the Code node runs in an isolated process and has hard restrictions: it can't make HTTP requests, it can't read or write files, and in Cloud only a few modules are available (crypto and moment). There's no fetch, no axios, no require of arbitrary packages, no this.helpers, no $env. If your instinct is "I'll write a Code node that calls the provider's pricing API," that can't be done and has to be done with an HTTP Request node.
But counting tokens and multiplying by a price requires none of that. The tokens already come in the item. You already have the price as a constant. All that's needed is arithmetic over data that's already in memory — and for that the Code node is exactly the right tool. It's the good use: transforming and calculating over what already arrived.
// Node: Code — "Calculate cost"
// Mode: Run Once for All Items
// Input: the output of the AI node, which carries the token usage
// Output: one item per call, ready to be written to cost_log
// Prices per million tokens. You take them from the provider's pricing
// page the day you set this up, and you review them every quarter.
// They go up here, in a single place, so changing them is trivial.
const PRICE_IN_PER_M = 0; // ← replace with the current input price
const PRICE_OUT_PER_M = 0; // ← replace with the current output price
const MODEL_NAME = 'model-id-you-use';
const rows = [];
for (const item of $input.all()) {
const data = item.json;
// The tokens come from the AI node. Adjust these paths to what
// you saw in YOUR execution panel — don't copy them blindly.
const usage =
data.tokenUsage ?? // frequent shape in model sub-nodes
data.usage ?? // frequent shape in raw responses
{};
// Each provider names the fields differently. We cover the two most
// common shapes and fall back to 0 if neither appears, so a name
// change produces a visible 0 in the record and not an exception.
const inputTokens =
usage.promptTokens ?? usage.input_tokens ?? 0;
const outputTokens =
usage.completionTokens ?? usage.output_tokens ?? 0;
// The arithmetic of the formula, as is.
const costIn = (inputTokens / 1000000) * PRICE_IN_PER_M;
const costOut = (outputTokens / 1000000) * PRICE_OUT_PER_M;
rows.push({
json: {
workflow_name: $workflow.name, // which workflow spent
execution_id: $execution.id, // to be able to go back to the execution
model: MODEL_NAME, // which model handled it
input_tokens: inputTokens,
output_tokens: outputTokens,
total_tokens: inputTokens + outputTokens,
cost_in: costIn,
cost_out: costOut,
// The total cost is what goes into the monthly sum.
cost_total: costIn + costOut,
// Flag the rows where the usage wasn't found: if this column
// fills up with true, your field paths changed and need reviewing.
usage_missing: inputTokens === 0 && outputTokens === 0,
logged_at: new Date().toISOString(),
},
});
}
return rows;
It's worth pausing on three decisions in that code, because they're what makes it survive in production.
First: the prices are up top, together, and named. When the provider changes the price, or when you change models after reading lesson 4, you touch two lines. If those constants were embedded inside the loop, you'd touch the code in several places and one day you'd forget one.
Second: the ?? with fallback to zero, and the usage_missing flag. If tomorrow n8n renames the field or you change providers, the code doesn't blow up: it writes a zero and flags it. A flagged zero is infinitely better than an exception, because the record keeps working and you see the signal in the column. A cost workflow that takes down the business workflow when a field name changes is a cost workflow someone is going to disconnect.
Third: the raw tokens are stored, not just the cost. If in six months the price changes, with the tokens stored you can recompute history; with just the cost, you can't. Always store the physical magnitude, not just its conversion to money. It's the same reason an electricity meter records kilowatt-hours and not dollars.
What to expect when you run it. The output panel shows one item for each model call there was in that execution. If the agent took three passes, you'll see three items — and that's your first visual evidence that the agentic loop is real and costs money. Each item brings input_tokens and output_tokens with numbers, and cost_total with a tiny decimal. If usage_missing comes back true, don't go on: go back to the execution panel and fix the field paths. A record that writes zeros is worse than no record, because it gives false reassurance.
Where the record lives
The items that node produces have to go somewhere. Three options, from least to most:
| Where | When it's a fit | What you lose |
|---|---|---|
| n8n native data table | Starting today, with no extra infrastructure. It's the fastest | Limited queries; it's not a time-series store |
| PostgreSQL (the same instance database, in a separate schema) | When you want to group, sum by month, and cross with other metrics using SQL | Nothing relevant for this case; it's the sensible option in most instances |
| The metrics tool you already have | If module 4 already left you an observability layer set up, cost is one more metric there | Nothing — it's the right option if it already exists |
Whichever it is, one hard rule: the cost record can't break the business workflow. If the log write fails, the customer's ticket still has to be classified. In practice that means putting the cost branch on a separate output, with its own error handling, configured not to stop the main flow. The exact mechanism —Continue On Error, error branch, global error workflow— you learned in module 2; here it's just applied.
From measuring to budgeting
With cost_log writing, you can now answer questions that used to be opinions. It's worth listing the four most used, because they're the ones you'll be asked:
-- 1. How much did we spend this month, by workflow?
SELECT workflow_name,
SUM(cost_total) AS spend,
SUM(total_tokens) AS tokens,
COUNT(*) AS calls
FROM cost_log
WHERE logged_at >= date_trunc('month', now())
GROUP BY workflow_name
ORDER BY spend DESC;
-- 2. What was the most expensive execution, and what's the typical case?
SELECT workflow_name,
MAX(cost_total) AS worst_call,
AVG(cost_total) AS avg_call,
COUNT(*) AS calls
FROM cost_log
WHERE logged_at >= now() - interval '7 days'
GROUP BY workflow_name;
-- 3. How many model calls does a typical execution make?
-- (if this number rises, someone touched Max Iterations or the prompt)
SELECT workflow_name,
execution_id,
COUNT(*) AS calls_per_execution
FROM cost_log
WHERE logged_at >= now() - interval '24 hours'
GROUP BY workflow_name, execution_id
ORDER BY calls_per_execution DESC;
-- 4. Are there rows where the usage wasn't captured?
-- (if this stops being 0, your field paths changed)
SELECT COUNT(*) AS broken_rows
FROM cost_log
WHERE usage_missing = true
AND logged_at >= now() - interval '24 hours';
Query 2 deserves a comment. The average lies and the maximum doesn't. In a system with AI, the average looks reassuring because most executions are cheap; what ruins your month is the few that spike. When you report costs, report the two figures together — and if you can, report the high percentile too. A large gap between the average and the maximum is the signature of an agentic loop with no ceiling, which is exactly what lesson 3 is going to fix.
And query 3 is your detector of configuration changes. If the number of calls per execution rises from one day to the next without the volume changing, someone touched something. That query, running daily, is what would have given Terra Market the answer the same day instead of the following month.
The budget: monthly_budget
Measuring without a cap is contemplating. The final step is to set a number and compare against it.
How it's set. It's not pulled from the air: it's calculated backwards from the operation.
1. Observed cost per execution (use the average AND the maximum, not just one)
2. × expected monthly volume, with a growth margin
3. + a cushion for the expensive executions
4. = monthly_budget
Terra Market, with the numbers above:
ticket-classify: 9,000 executions/month
reply-draft: 3,600 executions/month
growth margin of 20%
cushion of 15% for the tail of expensive executions
What to do with the number. A budget you only look at at the end of the month is worth nothing — the bill already does that. What works is comparing yourself against the running total of the current month, every day, and warning before you get there:
month_running_spend vs. monthly_budget × (day_of_month / days_in_month)
If the running total is above that proportional line, you're going to overshoot.
And you know it on the 8th, not on the 30th.
That calculation is a daily scheduled workflow: it queries cost_log, sums the current month, compares against the proportional line, and if it's above sends a notification. It's literally the same alert structure you set up in module 4 for failures, with a different metric inside. In the lesson 8 project you'll build it in full, with two thresholds —one warning and one emergency— and with an explicit decision about what happens when the second is crossed.
Common mistakes
Storing the cost in money and throwing away the tokens (practical). What happens: the record stores only cost_total. Six months later the provider lowers prices, or the team changes models, and the whole historical series becomes incomparable — you can't tell whether consumption went down or only the price went down. Why it happens: money is what matters in the end, so it seems like the only thing worth storing. How to detect it: ask yourself whether you could recompute last month's cost with today's prices. If you can't, you're missing information. How to fix it: always store input_tokens and output_tokens in addition to the cost, and also store the model that handled the call. With those three fields the history can be recomputed entirely; without them, it can't.
Instrumenting only the workflow you suspect (conceptual). What happens: someone suspects reply-draft, puts measurement on that one only, and discovers it consumes less than expected. Since they didn't measure the others, they still don't know where the spend is and conclude —wrongly— that the problem isn't the AI. Why it happens: instrumenting takes work and you start where the hunch points. How to detect it: count how many of your AI workflows write to cost_log. If it's not all of them, your record has gaps and any conclusion you draw from it is partial. How to fix it: instrument all the workflows that call a model, even the ones that look harmless, and do it before drawing conclusions. The "harmless" workflows that run every fifteen minutes are precisely where leak 5 hides.
Putting the log write in the critical path (practical). What happens: the table where cost_log lives fills up, or the database doesn't respond, and support tickets stop being classified because the workflow halts at the node that writes the cost record. Why it happens: the log node is connected in series, after the business node, without thinking about what happens if it fails. How to detect it: mentally draw your workflow and ask "if this node fails, does the customer notice?". If the answer is yes for the cost node, you have the problem. How to fix it: separate branch, Continue On Error active on the write node, and —if your volume justifies it— batch up and write instead of writing row by row. Observability can never be the cause of a business outage.
Confusing n8n's execution cost with the token cost (conceptual). What happens: someone looks at the execution consumption of their n8n Cloud plan, sees it's running comfortably, and concludes the cost is under control. The AI provider's bill arrives separately and is ten times larger. Why it happens: both are "the cost of the automation" in the head of someone who hasn't broken it down, but they're two bills from two different companies. How to detect it: put the two numbers side by side in the same table. It's almost always surprising which one dominates. How to fix it: when you report the cost of an AI automation, always report it broken down into platform (n8n executions or the server it's hosted on) and model (tokens). They're different levers: the first is optimized with workflow architecture, the second with prompts, model, and guardrails.
Writing a Code node that goes out to look up the price (practical). What happens: someone tries to have the Code node query the provider's pricing page with fetch or axios, so the calculation is always up to date. The node fails. Why it happens: since n8n 2.0 the Code node runs isolated and has no access to the network or the file system; in Cloud only crypto and moment are available. There's no fetch, no axios, no arbitrary require, no this.helpers, no $env. How to detect it: if your code in a Code node mentions any of those things, it won't run. How to fix it: the price is a constant you maintain, not a datum queried on every execution — and if you really want to bring it from somewhere, that's done by an HTTP Request node before the Code, which can go out to the network. The Code node keeps to what it does well here: arithmetic over data that already arrived in the item.
Exercises
Exercise 1 — Calculate and project. Terra Market is evaluating adding a third agent: refund-check, which reads a return request and decides whether it qualifies automatically according to policy. You measured a typical execution and got 1,100 input tokens and 90 output tokens. The workflow would run about 220 times a day.
(a) Write the formula for the monthly cost, leaving the prices as P_IN and P_OUT.
(b) Calculate the monthly input and output tokens.
(c) Of the total input, it's known that 800 tokens are fixed (system prompt + policy) and 300 are the customer's request. How much would you save per month if you trimmed the fixed part to 500 tokens?
(d) Someone proposes: "instead of trimming the prompt, let's use a cheaper model." Explain in one sentence why the two things aren't alternatives.
See solution
(a) The formula, with the volume already inside:
monthly_cost = (220 × 30 × 1100 / 1,000,000) × P_IN
+ (220 × 30 × 90 / 1,000,000) × P_OUT
(b) 220 × 30 = 6,600 executions a month.
input = 6,600 × 1,100 = 7,260,000 tokens ≈ 7.26 million
output = 6,600 × 90 = 594,000 tokens ≈ 0.59 million
(c) Trimming 300 fixed tokens from each call:
savings = 6,600 × 300 = 1,980,000 input tokens a month ≈ 1.98 million
It's a 27% reduction of that workflow's input — without touching
the model, without touching the volume, and without changing the business result.
(d) They're not alternatives: they're multiplicative and they combine. The price and the number of tokens are the two factors of the same product. Halving the price and trimming input by 27% doesn't give you 50% or 27% savings: it gives you the two effects multiplied. And there's an argument about order: trimming the prompt has no quality risk if it's tested, while changing models does. It's best to start with the free lever.
It's worth noting this workflow's profile: 7.26 million input against 0.59 output. It's a classifier, like ticket-classify, and its cost is dominated by what it reads. All the optimization effort goes to the prompt, not to the response.
Why it works: the exercise forces you to do the multiplication almost nobody does before publishing. The cost of one execution is imperceptible; the cost of a month is a business decision. And separating the fixed part from the variable part of the input is what turns "the prompt is long" into a number you can take to a meeting.
Exercise 2 — Classify the leaks. Here are six observations about Terra Market's instance. For each one, say which of the five leaks it corresponds to, whether it's a multiplier or an addend, and what your first action would be.
(a) The ticket-classify agent has memory connected and support conversations reach 15 turns.
(b) There's a workflow called reply-draft-v2-test published, with a trigger every 15 minutes, whose result nobody reads.
(c) The agent node has Retry On Fail with 3 attempts, and the provider's error rate went up to 12% last week.
(d) The system prompt of reply-draft went from 200 to 900 words in four months.
(e) Max Iterations is at 30 in ticket-classify.
(f) weekly-report generates a 4,000-word summary that only two people read, and of those 4,000 they read the first 300.
See solution
| Observation | Leak | Type | First action |
|---|---|---|---|
| (a) 15-turn memory | 3 — context that drags along | Multiplier (grows with the conversation length) | Bound the memory window to the last N turns, or summarize the earlier ones. Measure the cost of turn 1 against turn 15 before and after |
| (b) published test workflow | 5 — executions nobody asked for | Pure addend | Unpublish it. It's the only one of the six that's fixed in ten seconds and with no risk |
| (c) 3 retries with 12% failure | 4 — the retry that pays again | Multiplier over the fraction that fails | Check whether the failure is really retryable. Drop to 2 attempts with increasing wait and measure. A content failure doesn't improve by retrying it — it just gets paid for again |
| (d) prompt that grew | 2 — prompt by accumulation | Constant addend × all the volume | Audit the accumulated exceptions: how many still occur? Trim and measure classification quality before and after |
(e) Max Iterations at 30 | 1 — loop with no reasonable ceiling | Multiplier, and the biggest of all | Measure how many iterations the typical case actually uses, and lower the ceiling to that number plus a margin. It's lesson 3 |
| (f) 4,000-word summary | Variant of 2, but on the output side | Addend, at the expensive price | Ask for a 400-word summary. Output is charged more than input, so this cut pays off more per word than any of the previous ones |
Two method observations. First: the order of attack isn't the order of the table. Always start with (b), because it's free and risk-free, and then with (e), because it's the biggest multiplier. The ones that touch prompts —(d) and (f)— require proving the quality doesn't drop, and that takes more time.
Second: (f) is the only one on the output side, and that's why its cut pays off disproportionately. Trimming 3,600 output words saves more money than trimming 3,600 input words, even though intuitively they seem the same. When you look for where to optimize, always look first at what the model writes.
Why it works: the exercise practices the multiplier/addend distinction applied to concrete cases, and adds a third dimension —input or output— that decides how much each cut pays off. With those three questions you can prioritize any list of findings without needing the exact prices.
Exercise 3 — Design the record to survive. You're going to set up cost_log in Terra Market's instance. Write the list of fields you're going to store per row, and for each one justify in half a sentence why it's there. Then answer these three design questions:
(a) What happens if the provider changes the name of the field where the tokens come?
(b) What happens if the database where cost_log lives doesn't respond for an hour?
(c) What happens if a year from now the provider halves the prices and someone asks "did our consumption go down or only the price?"
See solution
A defensible list of fields:
| Field | Why it's there |
|---|---|
workflow_name | Without this you can't attribute the spend. It's the first question you'll be asked |
execution_id | It lets you jump from the expensive row to the concrete execution and see what happened there. Without this, a spike is a mystery |
model | The price depends on the model. Without this field you can't recompute history or compare models with each other |
input_tokens / output_tokens | The physical magnitude. It's what lets you recompute with other prices and what separates "we consumed more" from "the price went up" |
total_tokens | Redundant but convenient for quick queries. It costs nothing to store |
cost_in / cost_out / cost_total | The money, broken down. The breakdown matters because input and output are optimized differently |
usage_missing | The flag that the instrumentation broke. Without it, a field-name change fills the table with silent zeros |
logged_at | Without a timestamp there's no series, and without a series there's no trend or proportional budget |
(a) If the token field name changes: the code writes zeros and flags usage_missing = true. The business workflow doesn't go down —the ticket is classified anyway— and you find out with the broken-rows query, which should run daily. That's the reason the flag exists. The alternative —for the code to throw an exception— would take down ticket classification over a naming change, which is an absurd price for a metric.
(b) If the database doesn't respond: the write node fails, and since it's on a separate branch with Continue On Error, the main flow continues. You lose an hour of cost data, which is annoying and recoverable. What you don't lose is an hour of ticket classification, which would be an incident with customers involved. Observability degrades gracefully or it doesn't work.
If your volume is high and that loss worries you, the next pattern is to batch up and write every N minutes, or send the events to a queue. But it's best to start simple: an hour of lost cost data doesn't change any decision.
(c) If they halve the prices: with input_tokens, output_tokens, and model stored, the answer is a query. You recompute history with the new prices and compare tokens against tokens — which is the honest comparison, because tokens measure the work and money measures the work times the price. Without those three fields, the question is unanswerable and all you can say is "the bill went down," which answers nothing.
And a note from experience: that third question will be asked of you, sooner or later, in a meeting where someone wants to know whether the team optimized something or got lucky. Being able to answer it with data, instead of with an explanation, is the difference between operating and guessing.
Why it works: the three questions cover the three failure modes of a measurement system — that the capture breaks, that the storage breaks, and that the stored data isn't enough for the future question. A record that survives all three is a record that still exists a year from now, which is when it really matters.
Summary and next step
Now you know what you're being charged for. A token is a little piece of text, and you pay for each one that comes in and for each one that goes out — with an asymmetry that rules: output costs several times more than input, because reading is parallel and writing is sequential. From that comes the principle you'll use throughout the guide: if you have to choose where to cut, start with what the model writes.
You have the formula —tokens divided by a million times the price of the million, summing the two directions— and you know where to look for the current prices instead of trusting a printed table that ages. You did the calculation of a ticket-classify execution and discovered what everyone discovers: that one execution costs an imperceptible fraction and that the cost shows up in the multiplication by volume. And you saw that two-thirds of that workflow's input is fixed text that repeats on every call, which makes trimming the prompt the cheapest lever there is.
You take away the five leaks —agentic loop with no ceiling, prompt that grows by accumulation, context that drags along, retry that pays again, executions nobody asked for— with the characteristic they share: none produces an error, and that's why traditional monitoring doesn't see them.
And you built cost_log: a Code node that reads the token usage from the item, multiplies it by prices that live in a single place, and writes one row per call with the raw tokens in addition to the money. With three decisions that make it survive: prices as constants, fallback to zero with a usage_missing flag instead of an exception, and the physical magnitude stored alongside its conversion. All that within the real restrictions of the Code node in n8n 2.0 — no network, no file system, just arithmetic over what already arrived, which is exactly what it's for.
Before moving on you should be able to: explain why output costs more than input; calculate the monthly cost of a workflow from a measured execution and its volume; name the five leaks and say which ones are multipliers; and say which fields your record stores and why each one is there.
Lesson 3 goes straight to the biggest of the five multipliers: the AI Agent node and its loop. You'll see what exactly an iteration is, why iteration number ten costs much more than the first, what Max Iterations does —whose default value is 10— and why raising it doesn't add spend but multiplies it. You'll also see Return Intermediate Steps, which is the option that lets you see the loop from the inside and that has its own cost. And you'll solve, with the numbers from cost_log, the concrete case that opened the module: what to do with the Max Iterations someone raised to 30 in ticket-classify.
Resources
- Code node — n8n Docs — the reference for the node with which you built
cost_log, including its limits in n8n 2.0: no network or file access, and restricted modules in Cloud. - Built-in methods and variables — n8n Docs —
$input,$workflow,$execution, and the rest of what you do have available inside a Code node. - Data tables — n8n Docs — n8n's native tables, the fastest option to make
cost_logexist today without setting up infrastructure. - Anthropic — pricing · OpenAI — API pricing · Google — Gemini API pricing — the current pricing pages. Check them the day you fill in the Code node's constants, and review them every quarter.
- AI Agent node — n8n Docs — the node whose loop generates the calls you just learned to count. It's the topic of the next lesson.
- Module 4 of this guide — the observability layer where
cost_logshould live long-term: structured logging, metrics store, and alerts.