[KongchangAI]
· 3 min read· 1,700 words

Give Your n8n Workflow a Cost Ledger: Per-Call LLM Pricing in Practice

Give Your n8n Workflow a Cost Ledger: Per-Call LLM Pricing in Practice

Log one cost row per LLM call, alert on daily spend vs its own median, and turn monthly bill shock into a daily signal.

n8n natively logs token usage only per execution, lacking per-workflow aggregation, so small prompt or retry changes can quietly double costs until the month-end invoice. The fix: a Cost node after every model call writes model, tokens, dollars, and execution id into a cost_log table, with prices embedded inline. A daily query compares each workflow's spend to its 7-day median, alerting at spike_ratio >= 2.0. One real case caught a 3.1x spike next morning.

When the Bill Is Your Only Clue

Running eleven workflows that call large models, yet receiving only a single total API bill at month's end with nothing logged in between. The author's experience is all too familiar: after one "optimization" to a classification prompt, model costs tripled overnight — but nobody noticed until the invoice arrived thirty days later.

n8n cost ledger approach source

This isn't an isolated case. n8n's execution logs do record token usage for some AI nodes, but it's buried inside individual run data — you have to click through them one by one. No aggregation, no per-node attribution, no trend lines. The invoice tells you the total, but nobody tells you the distribution. And the distribution is exactly where surprises live — problems rarely come from the total rising, but from one workflow quietly getting more expensive.

The author's inbox triage agent had crept from $0.40 a day to $1.30. The cause: the so-called "improved" classification prompt grew 2.4x longer, plus a second model call was added for "edge cases." The workflow was running fine — just fine and expensive.

Ledger Design: A Cost Node After Every LLM Call

The core design is deliberately boring: every node that calls a model — OpenAI nodes, Anthropic nodes, or HTTP requests to inference endpoints — is immediately followed by a Cost node. It's a Code node with a uniform structure that writes one row per call:

// Cost node: append one row per LLM call
const call = $input.first().json;
const PRICE = {
  'gpt-4o-mini':        { in: 0.15,  out: 0.60  },   // USD per 1M tokens
  'gpt-4o':             { in: 2.50,  out: 10.00 },
  'claude-3-5-sonnet':  { in: 3.00,  out: 15.00 },
  'claude-3-5-haiku':   { in: 0.80,  out: 4.00  },
};
const p = PRICE[call.model];
const row = {
  run_id:     $execution.id,          // n8n execution id
  event_id:   call.event_id,          // my own correlation key
  workflow:   $workflow.name,
  node:       call.node_name,
  model:      call.model,
  in_tokens:  call.usage.prompt_tokens,
  out_tokens: call.usage.completion_tokens,
  usd:        (call.usage.prompt_tokens * p.in +
                call.usage.completion_tokens * p.out) / 1e6,
  ts:         new Date().toISOString()
};
// insert into cost_log (run_id, event_id, workflow, node, model, in_tokens, out_tokens, usd, ts)

Three key details:

The price table lives in the node, not in config. Prices change rarely, so updating one shared Code snippet (made into a shared sub-workflow imported everywhere) is enough. The author didn't want a database query inserted between the model call and the log write — the ledger must never slow down or break the workflow it observes.

Records actual model calls, not estimates. Token counts come from the API response's usage field, not by counting words in the prompt. If a provider doesn't return usage (some proxy endpoints don't), the node writes in_tokens: null and flags it as usd_estimated — estimated rows never enter alert queries, only dashboards.

Every AI workflow gets one, no exceptions. Those workflows that "barely call models" are exactly the ones where a single prompt change quietly doubles token counts.

n8n is an open-source workflow automation platform that orchestrates tasks through visually connected nodes, supporting control flow like conditional branches, loops, and retries. In AI scenarios, it can call nodes for OpenAI, Anthropic, and others, or reach any inference endpoint through an HTTP Request node. Each run of a workflow is called an execution, and n8n assigns it an execution id while logging each node's input and output. This article's Cost node leverages built-in variables like $execution.id and $workflow.name to bind call context together with cost rows. Since n8n's native execution logs only store token usage per individual run and can't aggregate across runs, the data must be written out to a database table cost_log to enable per-workflow trend and anomaly analysis.

Daily Query: Compare Against Your Own Median

A scheduled workflow runs the following query at 07:00 daily and sends results to Slack:

-- daily spend per workflow, vs its own 7-day median
WITH daily AS (
  SELECT workflow, DATE(ts) AS d, SUM(usd) AS spend
  FROM cost_log WHERE ts >= NOW() - INTERVAL 14 DAY
  GROUP BY workflow, DATE(ts)
),
medians AS (
  SELECT workflow, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY spend) AS med
  FROM daily WHERE d < CURRENT_DATE GROUP BY workflow
)
SELECT d.workflow, d.spend, m.med,
       ROUND(d.spend / NULLIF(m.med, 0), 2) AS spike_ratio
FROM daily d JOIN medians m ON d.workflow = m.workflow
WHERE d.d = CURRENT_DATE - 1;

The alert rule is a single line: spike_ratio >= 2.0 triggers a Slack alert with the workflow name and yesterday's spend.

Note the comparison isn't against total spend, but against its own median ratio. A workflow that's usually $0.30 and suddenly hits $0.90, and one that's usually $30 and suddenly hits $90, are the same class of problem — both mean something changed and nobody reviewed it.

The morning digest is a table, not a chart: workflow, yesterday's spend, 7-day median, ratio. The author reads it in thirty seconds over coffee. Charts are for quarterly reviews; tables are for Tuesday mornings.

PERCENTILE_CONT(0.5) is a continuous percentile function in the SQL standard, where 0.5 is the median. Compared to the mean, the median is insensitive to extreme values: if a workflow's spend spikes one day due to abnormal retries, the mean gets pulled up, distorting subsequent alert thresholds; the median stably represents "typical day" spend. The WITHIN GROUP (ORDER BY spend) clause specifies the sort order, working with GROUP BY workflow to compute per-workflow. Additionally, WHERE d < CURRENT_DATE excludes today's data to avoid incomplete same-day spend dragging down the baseline; the 14-day window balances sample size and timeliness — accommodating weekend swings while keeping up with recent usage pattern changes.

One Alert That Paid for the Whole System

Two weeks after wiring up the ledger, an alert fired on the repurposing workflow: spike_ratio of 3.1, that day's spend $2.10, median $0.68.

The cause was the retry policy changed the day before — broadening the retry scope to a larger class of failures. Broader retries meant the workflow would re-run its two most expensive model calls on failures that would previously have terminated directly. The retry change was correct for reliability, but it quietly introduced a 3x cost change.

Without the ledger, this would have waited until next month's invoice; with the ledger, the author caught it over coffee and tightened the retry conditions that same morning.

That's the entire value proposition: the ledger doesn't reduce costs — it converts "the invoice went up" (monthly, unactionable) into "this workflow changed behavior yesterday" (daily, actionable). Cost control comes from visibility, and visibility is all the ledger does.

In automated workflows, retry policies typically specify "when a certain class of error occurs, automatically re-execute the failed node." Broadening retry scope from a narrow set to a larger class of failures significantly increases retry counts. If the failure happens after an LLM call, retries often require re-executing that call — and LLM calls are billed per token, so every extra retry means real dollar cost. Especially when the failure occurs in the latter half of the workflow, retries may need to re-run the first two most expensive calls, compounding costs. Such "broadening retries for reliability" changes typically don't surface cost problems in functional tests, since tests rarely cover high-frequency failure scenarios. Only a per-workflow cost ledger comparing against historical medians can turn this kind of hidden cost change into an actionable alert.

What the Ledger Can't Do

It's not precise accounting. Token counts from usage are exact, but the price table is a snapshot that drifts as providers change pricing. The ledger answers "which workflow, roughly how much, which way is the trend going" — it's a dashboard, not a tax return.

It's not a prompt optimizer. It will never tell you why a workflow got more expensive — when that retry event happens, a human still has to read the diff. The ledger narrows the search to one workflow, one day. Diagnosis is still human work.

It's not free either: every model call now writes a database row. At the author's scale, a few hundred rows a day, it's negligible. If you're running millions of calls a day, sample (log one in a hundred, then multiply by the factor). The pattern survives sampling, as long as the sample rate is included in the alert rule's denominator.

Turning Cost into an Actionable Signal

The takeaway from this approach isn't that you must use n8n — it's a more general idea: between model calls and the bill, there must be an observability layer. Managing costs purely by vendor invoice is essentially a monthly blind box; writing one structured cost row at every call point turns the unactionable bill into daily actionable anomalies.

For individual developers and small teams building their own agent orchestration or automation pipelines, it's worth reserving a similar Cost node slot in the architecture. It doesn't need to be complex — a few hundred lines of code will do — but it can turn "why is this month so expensive" into "why did this workflow get expensive yesterday."

One-line Summary

Write a cost row after every LLM call, alert on the ratio of daily spend to its own median, and turn the monthly bill blind box into a daily actionable signal.

Paragraph Summary

The article addresses the problem of invisible costs when multiple workflows call large models, proposing a lightweight cost ledger. n8n natively logs token usage only per individual execution, lacking per-workflow aggregation and attribution, so small changes to prompts or retry policies can quietly double costs — only discovered at month-end via invoice. The approach attaches a uniformly structured Cost node after every model call node, writing model, token counts, dollar cost, and execution id into a cost_log table, with the price table embedded in the node to avoid slowing the workflow. A daily scheduled query computes each workflow's day spend versus its 7-day median, triggering a Slack alert when spike_ratio >= 2.0. In a real case, broadened retry policy pushed one workflow's cost ratio to 3.1, and the ledger let the author tighten conditions the next morning. The ledger only provides visibility — it doesn't explain causes or do precise accounting; at very large scale, sampling with an adjusted alert denominator works.

#n8n workflow cost tracking#LLM cost monitoring#token usage accounting#AI workflow observability#cost anomaly alerting
Share:

Related articles