TraptureIQ Guides Cost

Your ADK agent works. Nobody can say what one conversation costs.

A single question through a multi-agent pipeline becomes four, seven, a dozen priced model calls. The bill arrives as one number a month later. Between those two facts is where agent budgets go to die, and closing the gap turns out to be a measurement problem, not a finance one.

Updated August 2026 · 7 min read · Written against ADK 2.6.2

The uncomfortable version of the problem: most teams shipping agents can tell you their monthly model spend to the rupee, and cannot tell you which agent, which model, or which customer produced it. The bill is real. The attribution is guesswork.

That gap is not carelessness. It is structural. A traditional service costs money per request in a way that scales with traffic you already measure. A multi-agent system costs money per decision, and one user question can produce a dozen of them, each at a different price, some of which nobody planned.

One question is not one call

Here is what a routing agent with three specialists actually does to your budget when somebody types a sentence into it.

ONE USER QUESTION“book my trip”research_agent4 priced calls62%summariser2 priced calls21%formatter1 priced call5%SHARE OF THE RUN’S SPENDThe invoice shows one number. The graph shows which branch earned it.
A single request through a three-agent pipeline. The invoice records one total; the spend is concentrated in one branch, and nothing in the total says which.

The interesting number is not the total. It is 62%. One branch is carrying most of the cost, and until you can see that, every optimisation is a guess: you cache the wrong thing, you downgrade the wrong model, you rewrite the prompt that was never expensive.

The four dimensions that answer a real question

Cost data is only useful in the shape somebody asks for it. In practice that is four dimensions, and it is worth being deliberate about which ones you can actually produce:

  • Per sub-agent: which part of the graph spends. This is the one that changes architecture decisions.
  • Per model: what a model swap would really save, as opposed to what the price-per-million-tokens table suggests it would save. See token economics for why those two numbers differ so often.
  • Per session: what one conversation costs, which is the only unit that maps onto a customer, a support ticket or a price point.
  • Per end user: who is expensive. Necessary the moment you sell seats or have one power user distorting an average.

Per-tool cost is the one people ask for next, and it deserves a caveat rather than a feature bullet: what you can derive is the cost of the model call that consumed a tool's output. That is an upper bound on the tool's contribution, not a figure you can add to per-agent cost without double-counting the same money.

Why this is harder in ADK than it looks

ADK is unusually good here: it emits OpenTelemetry spans carrying token usage without being asked, which means the raw material for cost attribution is already on the wire. The difficulty is entirely in reading it correctly, and there are four traps that a reasonable first implementation falls into. We hit all four.

1. Two spans, one model call

ADK emits two spans that share a single LLM event id for one call. Join cost records onto spans by that id and every call is counted twice: a per-agent cost view that is uniformly, plausibly, exactly double. Nothing looks broken; the numbers are just wrong. Deduplicate on the event id before attributing.

2. The root span is empty

The invocation span at the top of an ADK trace carries zero attributes. Every trace-level figure (total cost, model list, environment, user) has to be rolled up from its descendants. Read the root expecting a summary and you get nulls.

3. The user id is not on the spans

From ADK 2.1.0, user.id appears only on log records, never on spans, and only when message content capture is on (OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true). With ADK's defaults, cost-per-user is empty forever, not wrong, just permanently blank. It needs the logs endpoint, that setting, and a backfill in both directions, because traces and logs flush independently. The catch: the same setting also ships prompt and response text.

4. Environment cannot be detected, only declared

There is no reliable way to tell a local run from a deployed one after the fact. ADK's resource carries only service.name, and that is null when OTEL_SERVICE_NAME is unset, not the string unknown_service you might code against. Source IP does not say "local" either. Declare it with OTEL_RESOURCE_ATTRIBUTES=deployment.environment=… or accept that your load tests and your production spend land in the same bucket.

See the number before you deploy, not on the invoice

The most valuable property of measuring cost through configuration rather than an SDK is one nobody asks for in a demo: there is no instrumented build. An agent running on a laptop reports exactly like a deployed one, because the difference between them is an environment variable rather than a code path.

That moves the moment of discovery. Most teams learn what an agent costs about a month after shipping it, when the prompt is expensive to change and the architecture is load-bearing. Seeing it during development means the reasoning model that thinks for 3,000 tokens a turn is a design decision you make deliberately, rather than one you discover in a finance review.

# nothing in agent.py changes
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://api.traptureiq.com/v1/traces"
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="https://api.traptureiq.com/v1/logs"
export OTEL_EXPORTER_OTLP_HEADERS="x-api-key=tiq_..."
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=local"

pip install opentelemetry-exporter-otlp-proto-http   # not bundled with google-adk
adk web

Verified end to end against real Vertex AI Gemini calls on both Cloud Run and Agent Engine, deployed agent to deployed collector, priced correctly, with no edit to the agent in either case. One caveat worth knowing before you test: adk run does not initialise OpenTelemetry at all, so it exports nothing and looks like a broken integration.

Two rules that keep the numbers trustworthy

Price at ingest and freeze it. If cost is recomputed at query time, correcting a rate rewrites history, and last month's report changes after you have quoted it to somebody. Freezing at ingest means a pricing fix applies going forward and old reports still reconcile.

Treat a missing price as a bug, not a zero. Token counts arrive for any model ADK can call; cost only exists for models in your pricing catalogue. Anything missing prices at zero, which produces a dashboard that works perfectly and is quietly wrong. We found this exact failure with non-Gemini models: a customer running Anthropic or OpenAI models through ADK saw a functioning cost page full of zeros.

Where to start

If you are measuring nothing today, the order that produces value fastest is: get per-call cost flowing at all, then attribute it per sub-agent, then join it to the agent graph so the expensive branch has a name. At that point cost stops being a monthly surprise and starts being a design input, which is the only version of this that changes what you build.

Questions people actually ask

How do I track the cost of a Google ADK agent?

ADK emits OpenTelemetry spans that already carry token usage per model call. Point the standard OTLP exporter at a collector that prices those spans and you get cost per call without changing agent code. The three environment variables are OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS.

Why does my per-agent cost look roughly double the real spend?

ADK emits two spans that share a single LLM event id for one model call. Joining cost records to spans on that id without deduplicating counts the same call twice. Deduplicate on the event id before you attribute anything.

Can I see agent cost while developing locally, before I deploy?

Yes. The same environment variables work with adk web and adk api_server on a laptop, so an uninstrumented local run reports exactly like a deployed one. Note that adk run does not initialise OpenTelemetry at all and exports nothing.

Why is my non-Gemini model showing zero cost?

Token counts arrive for any model ADK calls, but cost is only computed for models present in the pricing catalogue. A model with no pricing row prices at zero and produces a working dashboard full of zeros rather than an error. Check the catalogue covers every model your agents actually call.

Does cost change retroactively if I correct a price later?

It should not. Cost computed and frozen at ingest means last month’s report still reconciles after you fix a rate. Recomputing at query time makes historical reports move under you, which is indistinguishable from a bug during an audit.

Measure it on your own agent

Two environment variables, no change to agent.py. Free for 14 days.