AWS Builder Center
"Which Agent Is Costing Me the Most?" — Per-Agent Cost Attribution for Amazon Bedrock AgentCore

"Which Agent Is Costing Me the Most?" — Per-Agent Cost Attribution for Amazon Bedrock AgentCore

Calculate per-agent cost on Amazon Bedrock AgentCore by correlating each runtime's IAM role with Bedrock model invocation logs — no code changes or Application Inference Profiles needed. Walks through the three-step logic (list runtimes, query logs by role, pull CloudWatch metrics), the gotchas, and how to turn it into a live CloudWatch dashboard with hourly custom metrics, alarms, and anomaly detection.

A customer asked me a question I couldn't answer on the spot. They had several AI agents running on  AgentCore  — customer service, document analysis, internal research, and more. The CTO wanted to know: "How much does each agent cost us per month?"
The infrastructure side was easy. Each  AgentCore Runtime  is its own resource — CPU and memory costs are already separated in the bill. But the big-ticket item — LLM inference — was one undifferentiated line item. All their agents call  Amazon Bedrock , and the token charges just pile up together. There was no way to tell that the research agent was burning through 500K input tokens a day while the travel planner barely used 10K.
And here's why these are fundamentally separate cost buckets:  AgentCore  is platform-agnostic. It doesn't just work with Bedrock — your agents can call Anthropic directly, OpenAI, Google Gemini, a self-hosted model on SageMaker, Ollama running locally, or any LLM endpoint behind an API. AgentCore orchestrates the agent runtime, not the model. It gives you compute isolation, session management, memory, tool gateways, and observability — regardless of where your LLM lives.
This is a deliberate design choice, and it's the right one. But it means inference cost and infrastructure cost are fundamentally decoupled. AgentCore bills you for compute (vCPU-hours, GB-hours). Your model provider — whether that's Bedrock, OpenAI, or your own endpoint — bills you separately for tokens. There's no built-in link between the two, because there can't be one. AgentCore doesn't know or care what model you're calling, and your model provider doesn't know which AgentCore runtime made the call.
This decoupling is actually the norm in production AI systems. Think of it like a web application: your compute (EC2, ECS) is billed separately from your database (RDS, DynamoDB). Nobody expects the compute bill to include database costs. But when someone asks "what does this application cost?", you need to add them up. Same thing here — except with agents, the inference side is often 85-95% of the total, making it the one you really can't afford to leave unattributed.
That's the gap this post closes. AgentCore knows how much compute each agent used. Bedrock knows how many tokens were consumed. But neither knows the full picture per agent. Someone has to connect the dots.
This post shows a no-code-change way to attribute per-agent cost by correlating AgentCore runtime IAM roles with Bedrock invocation logs. If your agents call a non-Bedrock model provider (OpenAI, Anthropic direct, self-hosted on SageMaker), the general pattern still applies — you'd just substitute the provider's equivalent logging. This post focuses on the Bedrock path because that's where most customers start.

Why Per-Agent Cost Visibility Matters

Before jumping into the how, it's worth stepping back and asking — why does anyone care about cost per agent instead of just the total bill?
1. Value for money. If your customer service agent costs 500 dollars a month and handles 10,000 conversations, that's 5 cents per conversation — probably a great deal compared to the alternative. But you can't make that judgment without knowing the per-agent number. Cost without context is just a number. Cost per agent tells you whether you're getting value.
2. Budgeting and forecasting. When a business unit wants to launch a new agent, the first question from finance is "what will it cost?" If you can show that your existing document-analyzer agent costs USD 155 per month at 3,100 invocations, you can extrapolate for the new one. Without per-agent data, you're guessing.
3. Chargeback across teams. In large organizations, different teams or business units own different agents. The retail team shouldn't subsidize the finance team's agent costs. Per-agent attribution lets you charge each team for what they actually use.
4. Optimization targeting. When the total bill spikes, where do you look? With per-agent visibility, you can immediately see which agent is responsible and whether it's a volume increase, a prompt regression, or a model change. Without it, you're debugging blind.
5. Governance and cost controls. As agent adoption grows from 2-3 pilots to 20-30 production agents, costs can grow non-linearly. Per-agent tracking lets you set budgets, create alerts when an agent exceeds its threshold, and catch runaway costs before they become a surprise on the monthly bill.
6. Model selection decisions. Maybe your simple FAQ agent is running on Claude Sonnet when Haiku would do the job at one-fifth the cost. You can't make that call without knowing what each agent is spending on inference.
The bottom line: as organizations move from experimenting with one agent to running a fleet, per-agent cost visibility shifts from "nice to have" to "table stakes."

The Aha Moment: IAM Roles Are Already the Key

When you deploy an agent to AgentCore with  agentcore deploy , the runtime automatically gets its own IAM execution role. You don't configure this — it just happens. When that agent calls Bedrock, the API request carries that role's identity.
Now here's the piece that connects everything: if you enable  Bedrock model invocation logging , every single API call gets logged to CloudWatch with the caller's identity. I pulled up a log entry and there it was:
{
"identity": {
"arn": "arn:aws:sts::123456789012:assumed-role/RetailAgentRole/BedrockAgentCore-7da1ac37"
},
"modelId": "global.anthropic.claude-sonnet-4-6",
"output": {
"outputBodyJson": {
"usage": {
"inputTokens": 24257,
"outputTokens": 1729
}
}
}
}

 identity.arn  has the role name.  output.outputBodyJson.usage  has the exact token counts. Connect the two, and you have per-agent inference cost. No code changes to the agents. No  Application Inference Profiles . No tagging gymnastics. Just one prerequisite: enable invocation logging.

Why Not Application Inference Profiles?

Fair question. AIPs are the AWS-native way to tag Bedrock usage for billing — you create one AIP per agent or business unit, tag it, and costs show up segmented in Cost Explorer.
The reason I went with invocation logs is practical:
  • Zero setup on the agents. No need to update each agent's  model_id  to an AIP ARN. No deploys, no config changes, no redeploys.
  • Real-time visibility. AIPs give you billing data with a ~24 hour delay (that's how Cost Explorer works). Invocation logs are queryable within seconds.
  • Works retroactively. Once you enable invocation logging, you can calculate cost for any time window. AIPs only track usage from the moment they're attached.
AIPs are still the right choice for finance-grade chargeback reporting (more on that later). But for engineering visibility, dashboards, and real-time alerting, invocation logs + IAM role correlation is simpler and faster to set up.

How It All Fits Together

Here's the architecture. Each AgentCore Runtime assumes its own IAM role, calls Bedrock through that role, and Bedrock logs the identity + token usage. The cost calculator queries those logs by role name and pulls runtime compute metrics from CloudWatch to give you the full per-agent picture.
agent-core-runtime-per-agent-cost-attribution

The solid arrows show the runtime data flow (agent → IAM role → Bedrock → invocation logs). The dashed arrows show the cost calculator querying CloudWatch Logs Insights, CloudWatch Metrics, and Cost Explorer to assemble the per-agent cost breakdown.

Building the Calculator

The logic boils down to three steps:
Step 1 — Map runtimes to IAM roles. The AgentCore control plane API gives you this directly:
client = boto3.client("bedrock-agentcore-control")
for rt in client.list_agent_runtimes()["agentRuntimes"]:
detail = client.get_agent_runtime(agentRuntimeId=rt["agentRuntimeId"])
print(f"{detail['agentRuntimeName']}: {detail['roleArn']}")

Step 2 — Query invocation logs by role. A  CloudWatch Logs Insights  query does the heavy lifting:
fields modelId,
output.outputBodyJson.usage.inputTokens as input_t,
output.outputBodyJson.usage.outputTokens as output_t
| filter identity.arn like "RetailAgentRole"
| stats sum(input_t) as total_input,
sum(output_t) as total_output,
count(*) as invocations
by modelId

Step 3 — Pull runtime compute metrics. CloudWatch gives you per-runtime CPU and memory:
cw.get_metric_data(MetricDataQueries=[{
"Id": "m1",
"MetricStat": {
"Metric": {
"Namespace": "AWS/Bedrock-AgentCore",
"MetricName": "CPUUsed-vCPUHours",
"Dimensions": [
{"Name": "Resource", "Value": runtime_arn},
{"Name": "Service", "Value": "AgentCore.Runtime"},
],
},
"Period": period, "Stat": "Sum",
},
}])

Then multiply tokens by model pricing and compute hours by AgentCore pricing. That's it.

Gotchas I Hit

Token counts are nested. For streaming calls ( ConverseStream , which is what most agents use), the top-level  inputTokenCount  and  outputTokenCount  fields are  null . The actual numbers live at  output.outputBodyJson.usage.inputTokens . Took me a bit of log spelunking to figure that out.
CloudWatch needs two dimensions.  AgentCore CloudWatch metrics  require both  Resource  (the runtime ARN) AND  Service  (set to  AgentCore.Runtime ). If you only pass Resource, you get empty results back with no error. Ask me how I know.
Four token types, not two. If your agents use  prompt caching , you also need  cacheReadInputTokens  and  cacheWriteInputTokens  — they have very different pricing (cache reads are 90% cheaper than regular input tokens).

What It Looks Like

Here's what the output looks like with four agents deployed across different business units. The numbers below are representative of patterns I've seen with customers running small fleets of agents — yours will vary based on volume, prompt sizes, and model choice.
Summary — Active Agents
Agent
Invocations
Inference $
Infra $
Total $
% of Total
customer-service-bot
8,200
$245.40
$18.92
$264.32
52.1%
document-analyzer
3,100
$142.80
$12.45
$155.25
30.6%
internal-research
1,500
$68.30
$8.70
$77.00
15.2%
travel-planner
400
$8.20
$2.10
$10.30
2.0%
TOTAL
13,200
$464.70
$42.17
$506.87
100%
Cost split: Inference 92% | Infrastructure 8%
A few things jump out:
  • customer-service-bot is 52% of total cost despite being just one of four agents. It's handling the highest volume with large conversation contexts. That's where prompt optimization would have the biggest impact.
  • Inference dominates at 92%. Infrastructure is almost a rounding error. If you're trying to cut agent costs, optimizing prompts, using caching, or picking a smaller model for simple tasks will move the needle far more than worrying about compute.
  • travel-planner barely registers — 400 invocations, $10 total. Maybe it doesn't need Sonnet and could run on Haiku at a fraction of the cost.
These are the kinds of insights you just can't get from an account-level bill.

The Full Cost Picture for an Agent

The script today covers the two biggest cost buckets — inference and runtime compute — which together account for 99%+ of cost in my account. But an AgentCore agent can use other primitives too. Here's the complete picture and how to attribute each:
Component
Per-Agent?
How to Attribute
Inference (LLM tokens)
✅
Invocation logs filtered by IAM role (this script)
Runtime (vCPU + memory)
✅
CloudWatch metrics per runtime ARN (this script)
Memory (events, storage, retrieval)
✅
Each memory resource has its own CloudWatch metrics. Link to agent via the runtime's BEDROCK_AGENTCORE_MEMORY_ID env var
Gateway (API invocations, tool indexing)
⚠️
Per-gateway CloudWatch metrics exist. If a gateway is dedicated to one agent, straightforward. If shared, split proportionally by invocation count
CodeInterpreter (CPU + memory)
⚠️
Shared Code Interpreter resource (aws.codeinterpreter.v1), split proportionally
Memory is the most natural extension — agents typically have their own memory resource, and the CloudWatch metrics are already per-resource. Gateway gets trickier when shared across agents, but the invocation metrics do exist per-gateway.

How to Set This Up

1. Enable Bedrock model invocation logging — this is the single most important step. Open the  Amazon Bedrock console  → Settings → toggle on Model invocation logging → select CloudWatch Logs as the destination. Every  Converse  and  InvokeModel  call in the account will now be logged with identity and token usage.
2. Verify your runtimes have separate IAM roles — this is the default when you deploy with  agentcore deploy . Each runtime gets its own role. If two agents share a role, their costs will be merged.
That's it for prerequisites. The three code snippets above are the core building blocks — list runtimes, query logs by role, pull compute metrics. Wire them together with a pricing lookup table and you have a complete per-agent cost calculator. The Logs Insights query is the most important piece — that's where the IAM role correlation happens.

When This Approach Doesn't Work

Every approach has limits. This one falls short when:
  • All agents share one IAM role. Common in early prototypes where someone just creates a single  BedrockAgentRole  and points everything at it. In that case you get account-level data, not per-agent. Fix: deploy each agent as its own runtime (which is the default with  agentcore deploy ).
  • You use non-Bedrock models. Invocation logs are Bedrock-specific. If your agents call OpenAI, Anthropic direct, or a self-hosted SageMaker endpoint, you'd need the equivalent logging from that provider — OpenAI's usage export, for instance.
  • You need legally-exact numbers. This is an estimate based on published prices. For contracts, invoices, or chargeback that affects real money movement, use CUR 2.0 (covered next).

What About Billing-Grade Accuracy?

This script gives you an estimate based on published per-token pricing. It's typically 95-99% accurate. The small gap comes from pricing tiers (Standard vs Priority vs Flex), reserved capacity, and rounding.
For finance-grade chargeback reporting, you'd use  CUR 2.0  with  Athena  and tag your resources ( Application Inference Profiles  for inference, resource tags for AgentCore primitives). That gives you exact billing data, but with a ~24 hour delay.
The two approaches complement each other:

This Script
CUR 2.0 + Cost Explorer
Accuracy
Estimate (~95-99%)
Exact (billing system of record)
Latency
Real-time
~24 hour delay
Setup
Enable invocation logging
Enable CUR export, activate cost allocation tags, set up Athena
Best for
Engineering visibility, optimization, dashboards
Finance, chargebacks, monthly reports

What I Learned

Building this reinforced something I keep seeing with agentic workloads: the highest-leverage cost optimization isn't tuning your microVM size or session timeouts. It's:
  • Shorter prompts — every token in your system prompt gets charged on every call
  • Prompt caching — cache read tokens are  90% cheaper  than input tokens
  • Right-sizing models — use  Haiku  for simple routing/classification, Sonnet for complex reasoning
  • Fewer agent loops — an agent that takes 8 cycles to answer costs 8× more than one that takes 1
The per-agent visibility from this script is what makes those optimization decisions data-driven instead of guesswork.

Taking It Further: Dashboards and Alerts

Running the script manually is fine for ad-hoc analysis, but execs and platform teams want a dashboard they can glance at. The natural next step is to push per-agent cost as  CloudWatch custom metrics  and build a dashboard on top.

Step 1: Emit Custom Metrics

After calculating per-agent cost (using the three building blocks above), push the results to CloudWatch. Use a custom namespace like  AgentCore/CostAttribution  with  AgentName  as the primary dimension:
cloudwatch.put_metric_data(
Namespace="AgentCore/CostAttribution",
MetricData=[
{
"MetricName": "InferenceCost",
"Dimensions": [
{"Name": "AgentName", "Value": "customer-service-bot"},
{"Name": "ModelId", "Value": "claude-sonnet-4"},
],
"Value": 12.45,
"Unit": "None", # USD
},
{
"MetricName": "InfrastructureCost",
"Dimensions": [
{"Name": "AgentName", "Value": "customer-service-bot"},
],
"Value": 1.82,
"Unit": "None",
},
{
"MetricName": "TotalCost",
"Dimensions": [
{"Name": "AgentName", "Value": "customer-service-bot"},
],
"Value": 14.27,
"Unit": "None",
},
{
"MetricName": "TokensConsumed",
"Dimensions": [
{"Name": "AgentName", "Value": "customer-service-bot"},
{"Name": "TokenType", "Value": "input"},
],
"Value": 2500000,
"Unit": "Count",
},
],
)

Emit one set of metrics per agent, per hour. The  AgentName  dimension is what lets you filter and group in dashboards.

Step 2: Schedule It

Wrap the logic in a Lambda function and trigger it hourly with  EventBridge Scheduler :
# Create a schedule that runs every hour
aws scheduler create-schedule \
--name agentcore-cost-attribution \
--schedule-expression "rate(1 hour)" \
--target '{"Arn": "arn:aws:lambda:us-east-1:123456789012:function:agent-cost-calculator", "RoleArn": "arn:aws:iam::123456789012:role/scheduler-role"}' \
--flexible-time-window '{"Mode": "OFF"}'

The Lambda runs the same three steps — list runtimes, query invocation logs for the last hour, pull compute metrics — then calls  put_metric_data . Total execution time is typically under 30 seconds.

Step 3: Build the Dashboard

Create a  CloudWatch dashboard  with four widgets that give you the full picture at a glance:
  1. Total Cost per Agent (stacked area) — shows hourly cost by agent, stacked so you see both individual and total. Business-hours spikes become immediately visible.
  1. Inference vs Infrastructure (line chart) — compare the two cost types side by side. Useful for spotting when infrastructure cost grows disproportionately (long-running sessions, idle timeouts too high).
  1. Tokens Consumed per Agent (stacked area) — raw token volume over time. Correlate with cost to see if a cost spike is from more invocations or larger prompts.
  1. Cost Distribution (pie chart) — which agent is what percentage of total spend. The exec-friendly view.
You can create the dashboard programmatically with  put_dashboard  or manually in the CloudWatch console. Either way, the key is using the  AgentCore/CostAttribution  namespace and  AgentName  dimension to filter each widget.
Here's what the dashboard looks like with four agents running:
dashboard-screenshot


What You Get Out of the Box

Once the metrics are flowing into CloudWatch, you get these capabilities without writing any additional code:
  •  Alarms  — set a threshold on any agent's hourly cost. Get an SNS notification when the customer-service-bot exceeds its budget. Catch runaway prompts before they become a billing surprise.
  •  Anomaly detection  — CloudWatch learns the normal cost pattern for each agent and alerts you when something deviates. A prompt change that doubles token usage? You'll know within the hour.
  • Sharing — dashboards can be shared with anyone in the organization. Give leadership a read-only link they can bookmark.
A note on CloudWatch costs: custom metrics, alarms, and dashboards aren't free — see the  CloudWatch pricing page  for current rates. For a typical fleet of 10 agents, CloudWatch charges are usually small relative to the agent costs you're tracking, but worth knowing.
An hourly batch is the sweet spot for most teams. It's frequent enough to catch issues within the hour, cheap to run (one Lambda invocation + a few Logs Insights queries), and gives you smooth trend lines on the dashboard. Near-real-time via  CloudWatch Logs subscription filters  is possible too, but usually overkill unless you're running high-volume production agents where a bad deployment could burn through thousands of dollars in minutes.

Wrap-up

Per-agent cost attribution on AgentCore doesn't need code changes, Application Inference Profiles, or custom tagging schemes. Just enable Bedrock model invocation logging, make sure each agent has its own IAM role (the default), and correlate the two in CloudWatch Logs Insights. Three code snippets — list runtimes, filter logs by role, pull compute metrics — give you the full picture.
Once you have the data, pushing it to CloudWatch custom metrics unlocks dashboards, alarms, and anomaly detection for free. That's what takes this from a one-off script to an operational tool your team can rely on.
The approach is Bedrock-specific, but the pattern generalizes. If you're calling non-Bedrock models from AgentCore, the same IAM-role-as-attribution-key idea works — you just need the equivalent logging from your model provider. The important thing is to start getting per-agent numbers on the board. You can't optimize what you can't measure.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article