How to see Amazon Bedrock cost per team (2026)

Akal Cloud 9 min read

Quick answer

Two mechanisms, answering different questions. IAM principal cost allocation adds a line_item_iam_principal column to CUR 2.0 and surfaces tags on the calling user or role under an iamPrincipal/ prefix, with no code change, but AWS states it only populates from 8 April 2026. Application inference profiles are tagged ARNs your code invokes instead of a model ID, so they identify the application rather than the caller. CloudWatch cannot do either: ModelId is the only dimension on its Bedrock metrics.

Until recently, "which team spent this on Bedrock?" had no answer in the bill. Every invocation from every application in an account collapsed into one line per model, and the usual workaround was to stitch CloudTrail events to CloudWatch metrics to billing data by hand. AWS has since shipped two features that answer it directly, and they answer different questions.

Why doesn't Bedrock cost break down by team by default?

Because there is nothing to break it down by. Bedrock inference is not a resource you tag. There is no instance, no cluster, no bucket carrying a team tag that the billing pipeline can attach to the charge. The charge is attached to a model invocation, and an invocation historically carried no identity beyond the account it was billed to.

That is the same structural problem that EKS split cost allocation data solves for pods: a shared resource generating cost on behalf of many owners, with no native column naming the owner. AWS solved it for Bedrock in two different ways.

What is IAM principal cost allocation?

AWS supports cost allocation for Amazon Bedrock based on IAM principal identity and tags. Per that documentation, when you enable IAM principal data in CUR 2.0, "AWS automatically records the caller identity (IAM principal ARN) for each Bedrock API call in the line_item_iam_principal column."

The CUR 2.0 line item column reference defines it as "the IAM ARN of the principal that performed the Amazon Bedrock model inference. This column is populated when you enable IAM principal data in your CUR 2.0 data export. Currently supported for Amazon Bedrock only."

The ARN takes one of three documented shapes:

arn:aws:iam::123456789012:user/userID_A
arn:aws:iam::123456789012:role/application-role
arn:aws:sts::123456789012:assumed-role/application-role/session-name

On its own that is a caller, not a team. The second half is tagging the IAM users and roles themselves. AWS states that when you "apply tags to IAM principals (users or roles), those tags are automatically captured with associated costs and token usage," and explicitly that this "eliminates manual reconciliation processes that required combining CloudWatch and CloudTrail logs with billing data."

The feature is new enough that it is worth checking your export version. The CUR 2.0 data dictionary notes that the INCLUDE_IAM_PRINCIPAL_DATA table configuration "only adds data in the new columns starting April 8, 2026." Enabling it does not backfill a period before that.

How do you turn it on?

Three steps, in this order, per the AWS setup instructions:

  1. In the IAM console, apply tags to the users and roles that call Bedrock. AWS suggests keys that map to your org chart: department, cost-center, team, project, environment.
  2. In Billing and Cost Management, under Cost Organization, open Cost Allocation Tags, filter for IAM principal type tags, select yours and choose Activate.
  3. In Data Exports, create a Standard data export (CUR 2.0) and under Additional export content select Include caller identity (IAM principal) allocation data.

There is a chicken-and-egg step people trip over. AWS documents that "for Amazon Bedrock, tags only appear for activation after the IAM principal with the tags has made at least one API call. This applies whether the principal is an IAM user, role, or assumed role session." So a freshly tagged role that has not yet invoked a model will not be in the activation list at all, and nothing is wrong.

Then the usual billing latency applies twice over: "after you apply tags to your IAM principals, it can take up to 24 hours for the tag keys to appear on your cost allocation tags page for activation. It can then take up to 24 hours for tag keys to activate."

What does it cost you in report size?

This is the part that does not appear in any vendor's summary of the feature. AWS states plainly: "Enabling IAM principal data will increase the number of CUR rows by a factor of the number of calling identities accessing each model, resulting in larger file sizes compared to typical CUR exports."

Read that multiplicatively. Ten roles calling four models is up to forty rows where you had four. If you already run INCLUDE_RESOURCES and INCLUDE_SPLIT_COST_ALLOCATION_DATA on the same export, those multipliers compose. AWS's own best-practice list for this feature ends with "plan for CUR file size growth: account for increased Amazon S3 storage costs when enabling IAM principal data," and warns against high-cardinality tag values: "do not use unique session IDs, timestamps, or random GUIDs as tag values."

Where do the tags actually land in CUR 2.0?

In the tags column, which the CUR 2.0 tags column reference defines as "a map column containing key-value pairs of all tags and their values for a given line item," of type map<string, string>. Two conditions gate what appears in it: "tag keys only appear in this column if they've been enabled as cost allocation tags in the Billing console. After being enabled, a particular key only appears in the map column if it has a value that applies to the specific line item."

Keys are prefixed by where the tag came from, so the same word from different sources does not collide:

PrefixTag source
resourceTags/Tags applied directly to AWS resources
userAttribute/User attributes imported from IAM Identity Center
accountTag/Tags applied at the AWS account level
costCategory/Tags derived from AWS Cost Categories
iamPrincipal/Tags applied to IAM principals

AWS's own worked example has department arriving from four sources at once with four different values, which is worth internalising before you write a query that assumes there is one:

{
  "resourceTags/department": "teamA",
  "userAttribute/Department": "teamB",
  "accountTag/department":    "teamC",
  "costCategory/department":  "teamD"
}

So the Bedrock spend for a department is iamPrincipal/department, and it is a genuinely different number from resourceTags/department. Naming that distinction in your queries is not pedantry: it is the difference between "cost this team's code caused" and "cost sitting on resources this team owns."

The map keys can be queried as columns with the dot operator, so in Athena a per-department Bedrock breakdown is roughly:

SELECT tags['iamPrincipal/department'] AS department,
       sum(line_item_unblended_cost)   AS cost
FROM   cur2
WHERE  line_item_iam_principal IS NOT NULL
  AND  bill_billing_period_start_date = TIMESTAMP '2026-08-01 00:00:00'
GROUP  BY 1
ORDER  BY 2 DESC

Filtering on line_item_iam_principal IS NOT NULL rather than on a service name is deliberate: AWS defines that column as "currently supported for Amazon Bedrock only", so its presence already restricts the rows to Bedrock inference without depending on the exact spelling of a product code.

What do application inference profiles do instead?

They attach the identity to the call path rather than the caller. Per the Create an application inference profile documentation, "you can create an application inference profile with one or more Regions to track usage and costs when invoking a model." You give it a modelSource (a foundation model, or a cross-Region system-defined profile), optionally attach tags, and get back an inferenceProfileArn that your application invokes in place of the model ID. The CreateInferenceProfile API reference gives the request shape: modelSource is a union whose member is copyFrom, and tags is an array of key/value objects.

aws bedrock create-inference-profile \
  --inference-profile-name checkout-assistant \
  --model-source copyFrom=arn:aws:bedrock:us-east-1::foundation-model/MODEL_ID \
  --tags key=team,value=payments key=cost-center,value=CC-4471

The two mechanisms answer different questions, and they compose:

IAM principal allocationApplication inference profile
Identity isWho calledWhich application called
Attached toAn IAM user or roleA profile ARN your code invokes
Code changeNoneYes: invoke the profile ARN, not the model ID
Appears asline_item_iam_principal and iamPrincipal/ tagsTags on an AWS resource, which is what the resourceTags/ prefix covers
Fails whenMany teams share one roleTeams call the model ID directly and bypass it

If several teams share a single application role, IAM principal tags cannot separate them and profiles can. If teams have their own roles but you cannot change application code, the reverse. Note also that application inference profiles are not available everywhere: the supported Regions list names fifteen Regions, and AWS notes "some models, such as embedding models, do not support inference profiles."

Can CloudWatch metrics tell you cost per team?

No, and it is worth being exact about why, because this is where most home-built Bedrock dashboards stop. The bedrock-runtime CloudWatch metrics reference lists the metrics published under the AWS/Bedrock namespace:

MetricAWS description
InvocationsNumber of successful requests to Converse, ConverseStream, InvokeModel and InvokeModelWithResponseStream
InputTokenCountNumber of tokens in the input
OutputTokenCountNumber of tokens in the output
CacheReadInputTokensInput tokens read from the prompt cache. "These tokens are charged at a reduced rate and don't count toward your TPM quota."
CacheWriteInputTokensInput tokens written to the prompt cache. "These tokens count toward your TPM quota."
InvocationLatencyTime from request sent to last token received
TimeToFirstTokenTime to first token, streaming operations only
InvocationClientErrorsInvocations that result in client-side errors
InvocationServerErrorsInvocations that result in AWS server-side errors
InvocationThrottlesInvocations that the system throttled

And then the constraint. AWS documents exactly two dimension shapes for these metrics: "ModelId – all metrics" and "ModelId + ImageSize + BucketedStepSizeOutputImageCount." There is no principal dimension, no profile dimension, no tag dimension. CloudWatch can tell you what a model did; it cannot tell you who asked. Token counts per team have to come from the billing path, not the metrics path.

Two more things worth knowing before you build alerting on these. Throttles are not errors and not invocations: "throttled requests and other invocation errors don't count as either Invocations or Errors," and the count you see depends on your SDK retry settings. And EstimatedTPMQuotaUsage, tempting as it looks, carries an explicit AWS warning that it "is an approximation and does not reflect the reservation-based token consumption that drives throttling decisions… do not use this metric as the sole indicator for quota use or capacity planning."

What does none of this tell you?

Four things, and pretending otherwise is how a chargeback model loses its audience:

  • Cost per feature or per customer. Both mechanisms resolve to an identity you configured in advance. If one role serves ten customers, the bill sees one role.
  • Anything before you switched it on. The IAM principal column has data only from 8 April 2026 onward, and only after you enable the table configuration. Cost allocation tag backfill, covered in why cost allocation tags show up empty, works on tag activation status, not on a column that was never exported.
  • Whether the spend was worthwhile. A department total is an allocation, not a unit economic. The denominator, requests served or customers supported, is yours and is not in the CUR.
  • Rates. Deliberately, no per-token prices appear above. Allocation tells you whose tokens they were, not what a token costs, and the two are metered separately: AWS notes only that prompt-cached reads "are charged at a reduced rate". Per-model rates change, so read them on the Amazon Bedrock pricing page and validate against your own CUR rather than against a number in a blog post.

One last note on export format. Both of these are CUR 2.0 features. If you standardised on FOCUS for cross-cloud reporting, neither the line_item_iam_principal column nor the CUR 2.0 table configurations exist there, for the same reason FOCUS cannot produce per-pod cost. Export both.

See this on your own bill

Akal Cloud connects in about two minutes and shows the same numbers against your real AWS accounts.

Get started on AWS Marketplace

Related reading