The situation
A platform team runs a shared Amazon Bedrock estate for the whole company. Three product teams call the same Claude and Titan models through one set of AWS credentials: a support-summariser, a marketing copy generator, and a multi-tenant chat feature that serves a few hundred paying customers. Around the model calls sit the usual scaffolding, Lambda functions for orchestration, an S3 bucket of source documents, and a Bedrock Knowledge Base backing the chat feature’s retrieval.
The monthly bill has grown past the point where anyone waves it through. Finance can see the total Bedrock spend, and they can see it is up forty per cent quarter on quarter, but they cannot see which of the three products drove the rise, and they certainly cannot see which chat customers are heavy enough to be unprofitable. On-demand Bedrock usage lands in the bill as one undifferentiated line for input and output tokens per model; there is nothing in that line that says “marketing” or “tenant 412”. The Provisioned ThroughputReserved Bedrock capacity bought by the hour for a fixed term, paid for whether traffic fills it or not. commitment the chat team bought last quarter is another flat line of hours, equally silent about who consumed it.
The ask is concrete. Attribute the spend per product for internal chargeback, attribute the chat spend per tenant so the pricing team can find the loss-makers, and get an alert before any team blows through its monthly allowance rather than a fortnight after. Nobody wants to route each product through a separate AWS account just to read the bill.
What actually matters
The first thing to be clear about is where Bedrock cost comes from, because you can only attribute what you can measure. On-demand inference is priced per thousand tokens, and input and output tokens are priced separately, with output usually the dearer of the two. A verbose summary is not the same cost as a terse one even for identical input. On top of that, any Provisioned Throughput you have bought is charged by the Model unitThe billing block Provisioned Throughput is sold in – one unit delivers a fixed tokens-per-minute rate for a specific model. hour whether or not you send it traffic, so a committed model has a fixed cost that attribution has to spread across whoever the commitment was for. Batch inferenceSubmitting a bulk job of model calls to run asynchronously at a lower per-token price, trading immediacy for cost. sits at a discount to on-demand for work that can wait. So the raw material of any cost story is tokens in, tokens out, and committed hours, and the bill aggregates all of it per model unless you give AWS a reason to split it.
That reason is the second thing that matters: the grain you attribute at. Account-level attribution is the crudest and comes free, one bill per account. Application-level attribution answers “which product”, and it is the grain chargeback usually needs. Tenant-level attribution answers “which customer”, which is what a multi-tenant SaaS needs to price fairly and spot the unprofitable accounts. Request-level attribution answers “exactly which call cost what”, which is the grain for granular chargeback, anomaly hunting, and reconciling a disputed number. Each finer grain costs more to capture and store, so the discipline is picking the coarsest grain that actually answers the question rather than logging every token because you can.
The third is where the signal comes from, because Bedrock offers two quite different attribution mechanisms and they answer different questions. Cost allocation tags flow tag keys from your resources into the billing pipeline, so anything with a taggable resource, Lambda, S3, a Knowledge Base, can carry a team or cost-center tag and show up split that way in Cost Explorer and the Cost and Usage Report. But a raw on-demand InvokeModel call is not a tagged resource, so tags alone historically could not split shared Bedrock inference. Application inference profiles close that gap: an inference profile is a Bedrock resource that wraps a foundation model, you tag the profile, and you route a product’s or tenant’s calls through their profile, so the usage and cost attach to that profile’s tags and land split in Cost Explorer and the CUR. That is the mechanism that turns one Bedrock line into per-application or per-tenant lines without separate accounts.
The fourth is granularity of the raw record. Cost Explorer and the CUR give you cost broken down by tag, service, and time, which is the billing-grade view finance reconciles against. But they do not tell you the token count of an individual request. For per-request chargeback, anomaly detection, or attributing a cost inside a single profile to a specific end user, you need model invocation logging, which writes each call’s input and output token counts (and optionally the payloads) to CloudWatch Logs or S3. That is the fine-grained ledger you compute a per-request or per-user cost from, at the price of storing the logs and doing the arithmetic yourself.
The last thing that matters is closing the loop, because attribution that nobody acts on is just a nicer-looking bill. Once spend is split by tag, AWS Budgets can watch each product’s or tenant’s slice and fire an alert (or an automated action) at a threshold, and it can forecast against the trend so the warning arrives before the month closes rather than after. Attribution tells you who spent; budgets and alerts are what make that knowledge change behaviour. And once you can see who spends, the levers to actually reduce it, a cheaper model for the easy calls, prompt and response caching, batch inference for the non-urgent work, only become targetable because you finally know where to point them.
What we’ll filter on
- Attribution grain, do we need account, per-application, per-tenant, or per-request?
- Signal source, does the mechanism split shared on-demand Bedrock inference, or only the surrounding taggable resources?
- Billing-grade versus computed, does it reconcile against the AWS bill, or is it a number we derive ourselves from logs?
- Setup and running cost, tag hygiene, a profile per tenant, log storage and processing.
- Closes the loop, can it drive an alert or an automated action before the bill lands?
The attribution landscape
Separate AWS accounts. The bluntest split: give each product its own account and let Organizations consolidate the billing. Attribution per product falls out for free because each account is its own bill, and blast radius and quotas are isolated too. But it does nothing for per-tenant attribution inside a multi-tenant product, it multiplies operational overhead, and retrofitting it onto a shared estate is a migration, not a config change. Right for hard isolation between products, overkill purely to read a bill.
Cost allocation tags. Activate tag keys in the Billing console and they become dimensions in Cost Explorer and the CUR. Tag the Lambda functions, the S3 buckets, and the Knowledge Base with team and cost-center, and the cost of that scaffolding splits cleanly per product. The gap is Bedrock inference itself: a plain on-demand InvokeModel is not a resource you can hang a tag on, so tags alone leave the token spend, usually the biggest number, unsplit. Essential for the surrounding resources, insufficient on its own for shared inference.
Application inference profiles. A Bedrock resource that wraps a foundation model (or a cross-region set of them) and carries its own ARN and tags. Route a product’s or a tenant’s invocations through their profile and the token usage and cost attach to that profile, so activating the profile’s tags as cost allocation tags gives you per-application or per-tenant Bedrock spend in Cost Explorer and the CUR. This is the mechanism built for exactly this problem: splitting shared on-demand inference without separate accounts. The cost is managing a profile per attribution unit and routing calls to the right one; per-tenant at a few hundred tenants means a profile strategy, not one profile each by hand.
Model invocation logging. Turn it on per region and Bedrock writes every invocation’s metadata, including input and output token counts, to CloudWatch Logs or an S3 bucket, with the request and response payloads optional. This is the only source of a genuine per-request token count, so it is what you compute fine-grained chargeback or a per-end-user cost from. It is a computed number, not a billing-grade one; you multiply logged tokens by the published price yourself, and you own the log storage and the query cost. Right for per-request and per-user granularity, more than you need if per-product is the whole question.
Cost Explorer and the Cost and Usage Report. The reporting surface over everything the tags and profiles feed. Cost Explorer is the interactive, filter-and-group view (by the team tag, by service, by month) for eyeballing trends and answering “which product moved”. The CUR is the exhaustive line-item export, hourly or daily, that lands in S3 for Athena or QuickSight when you need to join Bedrock cost to your own tenant table or drive a custom chargeback report. Both are billing-grade and reconcile against the bill; neither carries per-request token detail on its own.
AWS Budgets. A cost or usage budget scoped by the same tags, with alert thresholds and forecasting. This is the loop-closer: a budget per product tag that emails and pages at eighty per cent of the monthly allowance and forecasts an overrun before it happens, optionally wired to a budget action that throttles or requires approval. It does not attribute anything itself; it watches the slices the tags and profiles have already carved out.
Side by side
| Mechanism | Finest grain | Splits shared on-demand inference | Billing-grade | Per-request tokens | Closes the loop |
|---|---|---|---|---|---|
| Separate accounts | Per product | ✓ (one bill each) | ✓ | ✗ | ✗ |
| Cost allocation tags | Per resource | ✗ | ✓ | ✗ | ✗ |
| Application inference profiles | Per app / per tenant | ✓ | ✓ | ✗ | ✗ |
| Model invocation logging | Per request | ✓ (computed) | ✗ | ✓ | ✗ |
| Cost Explorer / CUR | Per tag, per hour | reports it | ✓ | ✗ | ✗ |
| AWS Budgets | Per tag | reports it | ✓ | ✗ | ✓ |
Reading the table against the three asks: per-product chargeback wants application inference profiles plus cost allocation tags on the scaffolding, surfaced in Cost Explorer; per-tenant profitability wants a per-tenant profile strategy, or model invocation logging where a profile each is too many; per-request or disputed numbers want invocation logging; and every one of them wants a Budget on the resulting tag so the alert beats the bill. No single mechanism does the whole job, and separate accounts, the bluntest one, does not touch the per-tenant question at all.
The picks in depth
The per-product chargeback is the application inference profile case, and it is the one that finally splits the token spend. Create an inference profile per product wrapping the models each uses, tag each profile with team and cost-center, and change each product’s Bedrock client to invoke via its profile ARN instead of the bare model ID. Activate those tag keys as cost allocation tags in the Billing console, and within a day or so Cost Explorer starts showing Bedrock cost grouped by team. In the same pass, tag the Lambda, S3, and Knowledge Base resources with the matching keys so the scaffolding cost lands in the same buckets. The result is a chargeback view that reconciles against the AWS bill, product by product, with no new accounts. The one discipline that keeps it honest is tag governance: a call routed through the wrong profile, or a resource left untagged, shows up as unattributed spend, so enforce the tags with a Service Control Policy or a tag policy rather than trusting everyone to remember.
The per-tenant profitability question is a matter of grain and volume. A few hundred tenants is too many to eyeball but small enough that a profile-per-tenant strategy is viable if you automate the provisioning, and it gives you tenant cost straight in Cost Explorer the same way products do. Where the tenant count or churn makes a profile each impractical, model invocation logging is the fallback: stamp each request with the tenant ID (in your own application metadata alongside the call), log every invocation’s token counts, and compute per-tenant cost by joining the logs to the published per-token prices in Athena. That is a derived number rather than a billing-grade one, so treat it as the management view for pricing decisions, not the figure finance reconciles against, and remember that logging the payloads as well as the counts brings the tenants’ prompt content into your logs, which is a data-handling decision to make deliberately.
Model invocation logging is also the answer whenever the question is per-request. Anomaly hunting (“which call spiked the bill on the third”), reconciling a tenant’s disputed invoice, or attributing cost inside a single shared profile down to an end user all need the individual token counts that only the logs carry, because Cost Explorer and the CUR stop at the tag and the hour. The cost is real, log volume at scale is its own bill, so scope it to the regions and models that matter and expire the logs on a lifecycle policy rather than keeping every payload forever.
Budgets are what make any of it operational. Once the spend is split by team or tenant tag, a cost budget per slice with an alert at, say, eighty per cent and a forecast-based alert on top gives each team a warning while they can still act, and a budget action can throttle or gate further spend automatically if a threshold is breached. This is the piece the finance team actually asked for when they said “before the bill, not after”. And once attribution makes the heavy spenders visible, the reduction levers become targetable: route the easy summariser calls to a cheaper model, cache repeated prompts and responses, and push the non-urgent marketing generation to batch inference at its discount, each aimed at the product or tenant the attribution just exposed rather than sprayed across the whole estate.
A worked example: splitting the marketing spike
The bill jumps, and the platform team wants to know who and why before the standup. With the estate already carved into per-product inference profiles tagged team=marketing, team=support, team=chat, Cost Explorer is the first stop: filter to the Bedrock service, group by the team tag, and the marketing slice is plainly the one that doubled. That is the “who” answered at billing grade in under a minute, something the single undifferentiated line could never have told them.
The “why” needs a finer grain than the tag carries. Marketing’s own profile is shared across several campaigns, so the team turns to model invocation logging for that profile’s region and queries the logs in Athena, summing output tokens by the campaign ID their application stamps on each request. One campaign is generating enormous responses, long output at the dearer output-token rate, which is exactly the shape of a cost spike the token pricing predicts. The fix is a prompt change to cap the response length and a switch to batch inference for that campaign’s overnight run, and the lever is aimed precisely because the logs said which campaign, not just which product.
The loop closes with a Budget. The team sets a cost budget scoped to team=marketing with an alert at eighty per cent of the monthly allowance and a forecast alert on top, so the next campaign that starts running hot pages them mid-month instead of surprising finance at the end. Three mechanisms, one per grain, each doing the job the one above it could not: the tag and profile for “which product”, the invocation logs for “which campaign”, and the budget so the next spike announces itself.
What’s worth remembering
- Bedrock on-demand cost is tokens in and tokens out, priced separately with output usually dearer, plus any Provisioned Throughput charged by the model-unit hour whether or not you use it; you can only attribute what you can measure.
- Raw on-demand Bedrock spend arrives as one undifferentiated line per model, and nothing in that line names a product or a tenant until you split it deliberately.
- Cost allocation tags split the surrounding taggable resources (Lambda, S3, Knowledge Bases) but not a bare
InvokeModelcall, so tags alone leave the token spend unsplit. - Application inference profiles are the mechanism that splits shared inference: tag the profile, route a product’s or tenant’s calls through it, and the token cost lands per profile in Cost Explorer and the CUR without separate accounts.
- Model invocation logging is the only source of per-request token counts, so it is what you compute fine-grained or per-tenant chargeback from; it is a derived number, not a billing-grade one, and it costs storage.
- Pick the coarsest grain that answers the question: account is free but crude, per-application suits chargeback, per-tenant suits SaaS pricing, per-request suits anomaly hunting and disputes.
- Cost Explorer and the CUR are the billing-grade reporting surface over the tags and profiles; they stop at the tag and the hour and carry no per-request token detail.
- AWS Budgets close the loop: a budget per tag with threshold and forecast alerts warns each team before the month ends rather than after, and can drive an automated throttle.
- Attribution is only as good as tag hygiene; enforce the keys with tag or service control policies so misrouted calls and untagged resources do not pile up as unattributed spend.
- Once you can see who spends, the reduction levers, a cheaper model for easy calls, prompt and response caching, and batch inference for non-urgent work, become targetable at the product or tenant the attribution exposed.