Explain LLM spend per customer
Ask your agent to find which customers, models, and agent runs are driving cost and failures.
On this page
Anectico connects LLM calls to the customer and the application journey that caused them. This lets you investigate cost as part of production behavior, not only as an organization-wide total. Your agent reads the spend and finds the outliers. You open the person page or run page it links to.
Ask your agent
Which models and which customers drove our LLM spend in the last 7 days? For the top customer, was it heavy use, a retry loop, or a broken workflow? Give me the link to their page.
| Job | MCP tool or action | CLI command |
|---|---|---|
| Summarize spend: total cost, tokens, and calls, per model and per agent | llm_cost_summary (a read action) |
anectico llm summary |
| Read spend over time as a bucketed series | llm_cost_series (a read action) |
anectico llm series |
| Rank the customers that account for the most spend | llm_cost_top_persons (a read action) |
anectico llm top-persons |
| Say who is burning the bill and why, in one call | llm_spend_breakdown (a read action) |
anectico llm spend-breakdown |
| Find the runs with many steps, retries, or failures | list_agent_runs |
anectico agent-runs list |
| Read one customer’s timeline beside their LLM calls | get_person_timeline |
anectico persons timeline |
| Read the organization’s model price overrides | list_llm_pricing (a read action) |
anectico llm pricing list |
The spend reads need llm:read. Reading the price overrides needs settings:read. MCP calls also
need mcp:read.
Find who is burning the bill, and why
One read answers both halves for one project and one time window. It ranks the people by what their model calls cost, and it breaks the same spend down by agent, workflow, model, kind of call or entry point, for the whole window and for each of the top people.
Who is burning our model bill this week, and why? Name the top five people. For each one, say which agent or step the cost comes from, and which part of the bill you cannot price.
Your agent calls llm_spend_breakdown (CLI: anectico llm spend-breakdown). It needs llm:read.
Two more scopes name more of the answer, and a key without them still gets the counted spend:
persons:readnames the people. Without it the ranking has no person ids, and the answer says which scope shows them.agents:readtogether withpersons:readnames each person’s costliest run and links to it. A run points to its person, so without both the run is left out and the answer says which scope is missing.
Breaking the spend down by entrypoint needs traces:read as well, because it shows the names of
spans. Without it that one call is refused. The other four need only llm:read.
Choose what to break it down by
Pass group_by (CLI: --group-by). The default is agent.
group_by |
What it groups by | Where Anectico reads it |
|---|---|---|
agent |
The name of the agent | gen_ai.agent.name, the first span of the run that has one |
workflow |
The name of the workflow | gen_ai.workflow.name, the first span of the run that has one |
model |
The model | The model of each call |
operation |
The kind of call, such as chat or embeddings |
gen_ai.operation.name of each call |
entrypoint |
The first span of the run, usually the request or job that started it | The span name your app chose |
If your app does not name its agents, the answer shows one group, “not named”. Try model,
operation or entrypoint. There is no feature value, because no SDK or convention marks a
feature on a model call. To see spend per feature, name the agent or the workflow after the
feature, or start each feature’s work from a span named after it and group by entrypoint.
Read the answer
- The window. The answer gives the exact start and end, in UTC. If your plan keeps less data than you asked for, it says the start was raised.
- Totals are counted over the whole window. They are not the sum of the people on the page.
people_totalcounts everyone who made a model call. The page holds at most 20 people (default 10) and, for the window and for each person, at most 10 groups (default 5). What does not fit is combined intoother_groupswith its own amount, so the lines you see plus the remainder equal the whole. - Cost is an estimate in US dollars. Amounts are in nano-USD: divide by 1,000,000,000.
Anectico computed each cost when the call arrived, from its token counts and a price. The price is
either the cost your app stated (
client) or the price table with your overrides (table). A later price change does not change a stored call. - A call with no known price is its own line. The
unpricedline gives its calls, its tokens and the models behind it, and its cost is the wordunknown. It is never added to a total as zero. A total that leaves such calls out says how many, andcost_completeis false: the real cost is at least the figure shown. Give the model a price withupsert_llm_pricing; that prices new calls only. - Calls with no person are their own line.
unattributedcounts the runs that carry no customer id, and the runs whose customer id Anectico does not know.unattributed_unresolvedcounts the second kind apart. They are counted and never ranked. - A person is a customer Anectico knows. The customer id on a call is chosen by your app, so Anectico does not take it as a person. It uses the customer that its own identity records give that id. Two ids of one customer, or ids that were merged, count as one person. An id that no customer owns is not ranked, even if it looks like another customer’s id.
- Each person has a costliest run. It is the run with the highest known cost in the window, with a link. A person whose calls are all unpriced has none.
- A run belongs to the first customer id seen on it inside the window. A run that the window cuts in two can lose its id and count as unattributed.
Names of agents, workflows, spans and models were chosen by your app. An agent name is the name
your app declared. It is not a verified Anectico agent or key, and it has no link. A name is shown as
a label: long names are cut at 200 characters, and label_altered says when the label is not the
whole name. Two names that start the same are still two rows with their own amounts. Each row has a
key_id that identifies the whole name, and rows are told apart by key_id, never by the label.
The one name that goes back to a tool is an unpriced model’s identifier, in model, and it is given
only when it is whole. Your agent reads names as data. It does not follow instructions found in them.
What it does not tell you
It does not rank accounts. A customer can belong to several accounts and membership changes over
time, so an account total would double count. To find a person’s account, open their page. It does
not say why a person spent so much: whether it was heavy use, a retry loop or a broken workflow
is for your agent to read from the costliest run (get_agent_run) and the person’s timeline. A
window with too many model calls for one exact answer is refused with “narrow the window”; ask for a
shorter one.
What your agent gets back
The summary compares spend, token volume, calls, model, and agent. The ranking gives each customer’s
cost, tokens, and call count, and each row has a link to that customer’s person page. A row with
resolved false is an id your app declared that no customer owns. It is its own row, it is never
added to a customer, and it has no link. Look for:
- one customer with unusually high request or retry volume;
- a model change that increased cost or latency;
- failed calls that were retried repeatedly;
- agent runs with many tool or model steps; and
- calls without customer identity.
calls counts model calls, including calls inside a run. runs counts traces with at least one
model call. The per-agent calls, cost and tokens add up to the summary totals. An empty agent name
with other: false means unattributed; other: true combines groups beyond the display limit.
The model breakdown also keeps a combined remainder when MCP shortens its detail. Both breakdowns
and totals cover the same measured population. A run’s model is its most-used model by call
count; ties choose the alphabetically first identifier.
Token volume uses OpenTelemetry’s inclusive total: input plus output. Reasoning tokens are already a detail subset of output, so the organization, model, person, time-series, and by-agent views never add them a second time.
Open the proof
Open the customer’s person page at /view/person/:id. Its timeline shows LLM calls beside the
surrounding events, errors, traces, and replay. This reveals whether the cost came from legitimate
heavy use, a retry loop, a broken workflow, or abuse. To inspect one run, open the run page at
/view/agent-run/:runId and review its span waterfall. Compare model calls and tool calls, find
repeated steps, and check any linked replay or error. Both pages are read-only, name their project,
and have one action, Copy link. See Proof pages and Investigate agent
runs.
Account paid-period comparisons
Use llm_account_spend or anectico llm account-spend for an account-scoped read and optional
comparison with its declared paid period. Declared account value
owns allocation, exact arithmetic, comparison states, permissions and evidence limits. Keep this
financial comparison separate from the ordinary model-spend breakdowns above.
Missing or incorrect cost
Cost needs a recognized model, or an explicit cost value, plus accurate token counts. Customer attribution needs an active identity before the LLM span begins. See Record LLM calls for the instrumentation requirements.
Organization pricing overrides are applied when a span is processed. Organization rates take precedence over global and built-in rates. Persisted costs are stable and are not recalculated after an override changes. A span that was already queued uses whichever rate is active when it is processed. Override propagation is asynchronous across ingestion replicas, so verify changes with a newly processed call after the refresh interval.
To set or remove an override, open Billing and usage (/billing) in the Console, or ask your
agent (upsert_llm_pricing, delete_llm_pricing; CLI: anectico llm pricing set, anectico llm pricing delete). Setting a price needs settings:write, and removing one needs settings:delete.
Exact account comparison states
Interpret the returned state using the account-spend comparison contract. Retain all blocking reasons and the full priced, unpriced and unallocated figures.