See also: aws-cur-bedrock-token-finops-2026 · bedrock-finops-dashboards-2026 · ai-cost-attribution-multitenant-saas-2026
Pillar: 19 — Agent Frameworks & Observability
Scope: CUR 2.0 (AWS Data Exports) column anatomy for Amazon Bedrock inference, provisioned throughput, guardrails, and agent orchestration workloads
Research date: 2026-06-18
Contract strength: Official AWS documentation (primary); verified Athena DDL patterns (secondary)
| # | Column | Type | Default? | Bedrock-specific significance |
|---|---|---|---|---|
| 1 | line_item_product_code |
string | Yes | Discriminates Bedrock vs Marketplace vs SageMaker |
| 2 | line_item_usage_type |
string | Yes | Full model identity + token direction + tier + routing in one string |
| 3 | line_item_operation |
string | Yes | Inference vs streaming vs guardrails vs agent orchestration |
| 4 | line_item_line_item_type |
string | Yes | Usage / Tax / Credit / RIFee — Bedrock never shows SP types |
| 5 | line_item_usage_amount |
decimal | Yes | Token count, not hours or requests |
| 6 | line_item_unblended_cost |
decimal | Yes | Dollar cost at list price |
| 7 | line_item_iam_principal |
string | Opt-in | Full IAM ARN of Bedrock caller — CUR 2.0 only |
| 8 | resource_tags/* |
map | Opt-in | Cost allocation tags on inference profile ARNs |
| 9 | iamPrincipal/* |
map | Opt-in | Tags on the IAM identity, not the resource |
| 10 | pricing_unit |
string | Yes | “Token” for on-demand; “ModelUnit-month” for PT |
| 11 | pricing_public_on_demand_rate |
decimal | Yes | Per-token public list rate; verify against actual unblended_cost |
| 12 | line_item_resource_id |
string | Opt-in | Model ARN or inference profile ARN |
| 13 | product_region |
string | Yes | Region where request was billed (not necessarily executed for cross-region) |
| 14 | bill_payer_account_id |
string | Yes | Management account for consolidated billing |
| 15 | line_item_usage_start_date |
timestamp | Yes | Aggregation window start; partition key for Athena performance |
Section 1 — Column-by-Column Reference
1.1 line_item_product_code
Type: string
Always present: Yes
The AWS service namespace. For native Bedrock models this is always AmazonBedrock. Two adjacent namespaces require explicit exclusion in Bedrock-focused queries:
| Value | What it represents | Exclude from Bedrock queries? |
|---|---|---|
AmazonBedrock |
Native Bedrock on-demand inference, PT, guardrails, agents, knowledge bases | This is your target |
AWSMarketplace |
Third-party models purchased through AWS Marketplace (e.g., AI21, Cohere, Stability AI accessed via Marketplace subscription, not Bedrock native) | Yes — different pricing structure, no token-level line items |
AmazonSageMaker |
SageMaker inference endpoints, JumpStart, Autopilot | Yes — completely different cost model (per-instance-hour) |
AmazonBedrock + Guardrails |
Guardrails usage is still AmazonBedrock but distinguishable via line_item_operation |
No — filter by operation |
Query pattern:
WHERE line_item_product_code = 'AmazonBedrock'
Gotcha: Third-party models accessed natively through Bedrock (Meta Llama, Mistral, Cohere Command R+, AI21 Jamba) all appear as AmazonBedrock, not AWSMarketplace. Marketplace-subscribed models that you then invoke via Bedrock APIs also appear as AmazonBedrock. The distinction between “Bedrock native” and “Bedrock Marketplace” emerges only in the line_item_usage_type model segment and product_from_location.
1.2 line_item_usage_type
Type: string
Always present: Yes
This is the primary key for Bedrock cost analysis.
Full anatomy of the usage type string
{REGION_CODE}-{MODEL_SEGMENT}-{TOKEN_TYPE}[-{TIER}][-cross-region-global]
Region code mapping (billing region codes, not standard AWS region IDs):
| Billing code | AWS region |
|---|---|
USE1 |
us-east-1 (N. Virginia) |
USE2 |
us-east-2 (Ohio) |
USW2 |
us-west-2 (Oregon) |
EUW1 |
eu-west-1 (Ireland) |
EUC1 |
eu-central-1 (Frankfurt) |
APS1 |
ap-southeast-1 (Singapore) |
APN1 |
ap-northeast-1 (Tokyo) |
CAN1 |
ca-central-1 (Canada) |
SAE1 |
sa-east-1 (São Paulo) |
Model segment — exactly as it appears in the usage type string (case-sensitive, no spaces):
| Usage type model segment | Actual model |
|---|---|
Claude4.6Sonnet |
Anthropic Claude Sonnet 4.6 |
Claude4.6Opus |
Anthropic Claude Opus 4.6 |
Claude3.7Sonnet |
Anthropic Claude 3.7 Sonnet |
Claude3.5Haiku |
Anthropic Claude 3.5 Haiku |
Nova2.0Lite |
Amazon Nova 2 Lite |
Nova2.0Micro |
Amazon Nova 2 Micro |
Nova2.0Pro |
Amazon Nova 2 Pro |
llama3-70b-instruct |
Meta Llama 3 70B Instruct |
gpt-oss-120b |
OpenAI gpt-oss-120b (Bedrock Marketplace) |
openai.gpt-oss-120b |
OpenAI gpt-oss-120b via mantle endpoint |
Token type segment:
| Suffix | Meaning | Relative price |
|---|---|---|
-input-tokens |
Standard prompt tokens | 1× baseline |
-output-tokens |
Generated completion tokens | ~3–5× input |
-cache-read-input-token-count |
Prompt cache hit tokens | ~0.1× input (90% discount) |
-cache-write-input-token-count |
Prompt cache write tokens | ~1.25× input (25% premium) |
Tier suffix (appended after token type, before routing suffix):
| Suffix | Tier | Pricing |
|---|---|---|
| (none) | Standard on-demand | Public list rate |
-flex |
Flex inference (batch, lower priority) | Discounted vs standard |
-priority |
Priority inference (guaranteed capacity) | Premium vs standard |
Routing suffix:
| Suffix | Meaning |
|---|---|
| (none) | In-region inference |
-cross-region-global |
Request routed via cross-region inference profile (billed in the originating region’s billing code) |
-mantle- |
Bedrock-mantle endpoint (used for OpenAI-compatible API surface) |
Complete concrete examples:
USE1-Claude4.6Sonnet-input-tokens → Claude Sonnet 4.6, standard, input, in-region
USE1-Claude4.6Sonnet-output-tokens → Claude Sonnet 4.6, standard, output, in-region
USE1-Claude4.6Sonnet-cache-read-input-token-count → cache read, standard
USE1-Claude4.6Sonnet-cache-write-input-token-count → cache write, standard
USE1-Claude4.6Sonnet-output-tokens-cross-region-global → cross-region routed, billed in USE1
USE1-Nova2.0Lite-input-tokens-flex → Nova 2 Lite, flex tier
USE1-gpt-oss-120b-output-tokens-priority → OpenAI model, priority tier
USE1-openai.gpt-oss-120b-mantle-input-tokens-standard → OpenAI model via mantle (OAI-compat) endpoint
USE1-BedrockAgent-input-tokens → Agent orchestration token usage
USE1-Guardrails-text → Guardrails text unit (not tokens)
USE1-Claude4.6Sonnet-ModelUnit-month → Provisioned throughput monthly charge
Regex to extract model from usage type (Athena/Presto):
REGEXP_EXTRACT(line_item_usage_type, '^[^-]+-(.+?)-(input|output|cache|ModelUnit|mantle)', 1)
1.3 line_item_operation
Type: string
Always present: Yes
Values observed in production as of 2026 (nomenclature stabilized January 2026 — prior to that, many Bedrock line items showed generic Usage):
| Value | What it covers |
|---|---|
InvokeModelInference |
Synchronous InvokeModel API calls |
InvokeModelStreamingInference |
Streaming InvokeModelWithResponseStream API calls |
ConverseInference |
Converse API (unified conversation format) |
ConverseStreamingInference |
Streaming Converse API |
AgentInference |
Bedrock Agents orchestration — includes agent planner tokens |
KnowledgeBaseRetrieve |
Bedrock Knowledge Bases retrieve-only calls |
KnowledgeBaseRetrieveAndGenerate |
Knowledge Bases RAG combined call (includes generation tokens) |
GuardrailsApplyGuardrail |
Standalone Guardrails invocation |
ModelEvaluation |
Bedrock Model Evaluation job token usage |
BatchInference |
Bedrock Batch Inference job processing |
FineTuning |
Fine-tuning training compute (rare — also appears as separate product in some configs) |
Critical: For cost governance, AgentInference tokens are typically 2–4× higher per task than direct InvokeModelInference for the same net work due to orchestration overhead (planner turns, tool-call rounds). This is not a bug — it’s the cost of autonomous orchestration.
Query pattern to compare inference modes:
WHERE line_item_product_code = 'AmazonBedrock'
AND line_item_operation IN (
'InvokeModelInference', 'InvokeModelStreamingInference',
'ConverseInference', 'ConverseStreamingInference'
)
1.4 line_item_line_item_type
Type: string
Always present: Yes
| Value | Meaning | Appears for Bedrock? |
|---|---|---|
Usage |
On-demand resource consumption | Yes — primary type |
Tax |
VAT/GST tax line (separate row) | Yes (in applicable jurisdictions) |
Credit |
AWS promotional or committed-spend credit applied | Yes (if organization has credits) |
RIFee |
Reserved Instance upfront or recurring fee | No — Bedrock has no RI concept |
SavingsPlanCoveredUsage |
Usage covered by Compute Savings Plan | No — Bedrock Savings Plans exist but generate their own SavingsPlanCoveredUsage line type; the covered usage record shows SavingsPlanCoveredUsage |
SavingsPlanNegation |
Negation entry offsetting list-price charge | No (same reason) |
DiscountedUsage |
Usage covered by an EC2 Reserved Instance | No |
Refund |
Credit memo | Rare, possible |
BundledDiscount |
Volume discount applied | Possible at high volume |
EdpDiscount |
Enterprise Discount Program discount | Yes if org has EDP |
Bedrock filter standard practice:
WHERE line_item_line_item_type = 'Usage'
Always filter to Usage unless you specifically need to analyze tax, credits, or discount lines. Failing to filter means SUM cost aggregations will double-count when credits are present.
Bedrock Savings Plans note: AWS launched Bedrock Savings Plans (1- and 3-year commitments) in late 2025. When a Savings Plan is active, the covered token usage shows as SavingsPlanCoveredUsage, not Usage. If your fleet uses Bedrock SPs, you must include both types in consumption queries:
WHERE line_item_line_item_type IN ('Usage', 'SavingsPlanCoveredUsage')
1.5 line_item_usage_amount
Type: decimal(18,8)
Always present: Yes
Unit: tokens (not hours, not requests, not API calls)
This is the most important unit distinction for Bedrock vs every other AWS service. For EC2, this column is instance-hours. For S3, it is GB-months. For Bedrock, it is token count.
Aggregation behavior: CUR aggregates usage amounts by line_item_usage_type over the export granularity window (hourly or daily). A single row in your CUR table represents potentially millions of tokens across thousands of requests that share the same usage type, account, region, and IAM principal.
Token type handling: Each of the four token types (input, output, cache-read, cache-write) appears as a separate row with its own line_item_usage_amount. You must never SUM all rows for a model — this produces a meaningless blend of token types at different unit prices.
Correct total cost calculation:
-- Right: let price × quantity handle it
SUM(line_item_unblended_cost)
-- Wrong: SUM(line_item_usage_amount) * some_rate
-- (rate varies by token type; can't apply a single rate to a blended token count)
Provisioned throughput exception: For PT model units, line_item_usage_amount is the number of ModelUnit-month fractions consumed (e.g., 0.033333 for one day of one model unit). The unit switches to ModelUnit-month as confirmed by pricing_unit.
1.6 line_item_unblended_cost
Type: decimal(18,10)
Always present: Yes
The dollar cost of the line item at the payer account’s list price, before application of Savings Plans, Reserved Instances, or credits. For Bedrock on-demand, this is:
line_item_unblended_cost = line_item_usage_amount × pricing_public_on_demand_rate
When Bedrock Savings Plans are active, line_item_unblended_cost on SavingsPlanCoveredUsage rows still shows the original unblended cost. The effective cost reduction appears in line_item_net_unblended_cost.
Related columns for net-cost analysis:
| Column | Meaning |
|---|---|
line_item_unblended_cost |
List price (always present) |
line_item_net_unblended_cost |
After credits, EDPs, SP discounts |
line_item_blended_cost |
Weighted average across family (useful for consolidated billing; less useful for Bedrock) |
savings_plan_savings_plan_effective_cost |
The effective Savings Plan cost allocated to this usage line |
For most Bedrock FinOps workflows, use line_item_unblended_cost for consistency and predictability. Switch to line_item_net_unblended_cost when building CFO-facing cost reports that must reflect actual cash outflow.
1.7 line_item_iam_principal
Type: string
Availability: CUR 2.0 only — requires INCLUDE_IAM_PRINCIPAL_DATA = TRUE
Data available from: April 8, 2026
The full IAM ARN of the AWS identity that made the Bedrock API call. This is the single most powerful column for attributing Bedrock costs to teams, services, or individual developers in a multi-tenant deployment.
ARN formats
| Identity type | Example ARN | When seen |
|---|---|---|
| IAM user | arn:aws:iam::123456789012:user/alice |
Direct API key usage |
| IAM role (assumed, no session name) | arn:aws:sts::123456789012:assumed-role/BedRockAppRole |
EC2/Lambda with role, no custom session name |
| IAM role (assumed with session name) | arn:aws:sts::123456789012:assumed-role/GatewayRole/team-data-eng |
LiteLLM/gateway using STS AssumeRole with session name |
| Federated SSO user | arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_PowerUser/alice@acme.com |
IAM Identity Center SSO |
| Bedrock API key | arn:aws:iam::123456789012:user/BedrockAPIKey-xR2t4 |
Bedrock-specific API keys |
| Service account | arn:aws:sts::123456789012:assumed-role/SageMakerExecutionRole/SageMakerPipeline |
SageMaker pipeline calling Bedrock |
Activation
- Go to AWS Billing → Data Exports → select your CUR 2.0 export → Edit
- Under Additional content, enable IAM principal data (
INCLUDE_IAM_PRINCIPAL_DATA = TRUE) - Data begins populating from the activation date forward — there is no retroactive backfill
- Allow up to 24 hours for the first data to appear in S3 and Athena
Gateway attribution pattern
A single-role gateway (LiteLLM, Bedrock proxy, custom gateway) will show one IAM ARN for all traffic — useless for per-team chargeback. The correct pattern:
# Per-request STS assumption in LiteLLM custom auth
sts = boto3.client("sts")
response = sts.assume_role(
RoleArn=f"arn:aws:iam::{account_id}:role/BedrockGatewayRole",
RoleSessionName=f"team-{team_slug}", # appears in line_item_iam_principal
Tags=[
{"Key": "team", "Value": team_slug},
{"Key": "cost-center", "Value": cost_center},
{"Key": "project", "Value": project_id},
],
DurationSeconds=3600,
)
# Use response['Credentials'] for this request's Bedrock call
The RoleSessionName appears as the final segment of the ARN:
arn:aws:sts::123456789012:assumed-role/BedrockGatewayRole/team-data-eng
Session tags flow into iamPrincipal/ prefixed columns in the CUR tags map.
1.8 resource_tags/* (Cost Allocation Tags)
Type: nested within tags map column
Availability: Requires cost allocation tag activation in Billing Console
Delay: 24-hour propagation delay after tag activation; not retroactive
In CUR 2.0, cost allocation tags appear inside the tags map column with a resourceTags/ prefix. These are tags applied to AWS resources, not to IAM identities.
For Bedrock, taggable resources include:
- Application inference profiles (have ARNs that can be tagged)
- Custom models (fine-tuned model ARNs)
- Provisioned throughput units
Standard Bedrock on-demand invocation has no resource ARN by default, so resourceTags/ values will be null unless you configure application inference profiles with tags and route requests through them.
Activating a tag for cost allocation:
- AWS Billing → Cost Allocation Tags → User-Defined Tags
- Activate each tag key (takes up to 24 hours; no backfill)
- Tag appears in CUR as
resourceTags/{TagKey}
Full tag prefix taxonomy in CUR 2.0 tags column:
| Prefix | Source |
|---|---|
resourceTags/ |
Tags on the AWS resource (inference profile ARN, custom model) |
iamPrincipal/ |
Tags on the IAM user or role making Bedrock calls |
userAttribute/ |
User attributes from IAM Identity Center directory |
accountTag/ |
Tags at AWS account level (from Organizations) |
costCategory/ |
Values derived from AWS Cost Categories rules |
Athena access pattern:
-- Map accessor
tags['resourceTags/Environment'] AS environment,
tags['iamPrincipal/team'] AS team,
tags['accountTag/BusinessUnit'] AS business_unit
-- Dot operator (equivalent, CUR 2.0 only)
tags.resourceTags.Environment
1.9 iamPrincipal/* (IAM Principal Tag Columns)
Type: nested within tags map column, prefix iamPrincipal/
Availability: CUR 2.0 only; requires INCLUDE_IAM_PRINCIPAL_DATA = TRUE AND the tag key must be activated as a cost allocation tag
IAM principal tags are propagated to CUR from the tags on the IAM user or role that made the API call, and from session tags set at AssumeRole time.
Two sources of iamPrincipal/ data:
- IAM identity tags — tags attached directly to the IAM user or role in IAM
- Session tags — tags set in the
AssumeRolecall; these override identity tags if keys collide
Common tagging schema for Bedrock gateway attribution:
| Tag key | Example value | Usage |
|---|---|---|
team |
data-engineering |
Team-level chargeback |
cost-center |
CC-1042 |
Finance chargeback code |
project |
customer-360 |
Project-level attribution |
environment |
prod |
Prod vs dev spend isolation |
service |
recommendation-engine |
Microservice attribution |
owner |
alice@acme.com |
Individual developer tracking |
In Athena:
tags['iamPrincipal/team'] AS team,
tags['iamPrincipal/cost-center'] AS cost_center,
tags['iamPrincipal/project'] AS project
1.10 pricing_unit
Type: string
Always present: Yes
| Value | When it appears | Meaning |
|---|---|---|
Token |
On-demand inference (standard, flex, priority) | Each token in line_item_usage_amount costs pricing_public_on_demand_rate dollars |
ModelUnit-month |
Provisioned throughput | One model unit purchased for a calendar month; line_item_usage_amount is the fractional month consumed |
TextUnit |
Guardrails text processing | One unit = 1,000 characters processed |
ImageUnit |
Guardrails image processing | One unit = 1 image processed |
Request |
Some Knowledge Base retrieve-only calls | Per-request pricing |
GB-month |
Bedrock Knowledge Base storage | Vector store storage |
Diagnostic use: When troubleshooting unexpected cost spikes, check pricing_unit first. A spike in ModelUnit-month charges means PT units are running idle; a spike in TextUnit charges means Guardrails throughput has grown unexpectedly.
1.11 pricing_public_on_demand_rate
Type: decimal(18,10)
Always present: Yes
The AWS public list rate for one unit of the pricing_unit. For Bedrock on-demand token inference, this is dollars per token.
Example values (illustrative — verify against current Bedrock pricing page):
| Usage type | Approximate rate (USD/token) |
|---|---|
*-Claude4.6Sonnet-input-tokens |
$0.000003 ($3.00/MTok) |
*-Claude4.6Sonnet-output-tokens |
$0.000015 ($15.00/MTok) |
*-Claude4.6Sonnet-cache-read-input-token-count |
$0.0000003 ($0.30/MTok) |
*-Claude4.6Sonnet-cache-write-input-token-count |
$0.00000375 ($3.75/MTok) |
*-Claude4.6Opus-input-tokens |
$0.000015 ($15.00/MTok) |
*-Claude4.6Opus-output-tokens |
$0.000075 ($75.00/MTok) |
Warning: pricing_public_on_demand_rate reflects the public rack rate. If your organization has EDP, volume discounts, or Bedrock Savings Plans, the effective rate differs. Use line_item_unblended_cost / line_item_usage_amount to compute the actual effective rate per token for each line item.
Unit conversion note: CUR reports rates in dollars-per-single-token. To get the familiar “per million tokens” figure:
pricing_public_on_demand_rate * 1000000 AS rate_per_million_tokens
1.12 line_item_resource_id
Type: string
Availability: Requires INCLUDE_RESOURCES = TRUE in Data Export configuration
Delay: No additional delay beyond standard CUR propagation
For Bedrock, line_item_resource_id is populated only when the request was made through an application inference profile or a custom model (fine-tuned or imported). Direct invocation of foundation model IDs does not produce a resource ID.
ARN patterns:
| Resource type | ARN pattern |
|---|---|
| Application inference profile | arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/AIProfName |
| Custom model | arn:aws:bedrock:us-east-1:123456789012:custom-model/CustomModelId |
| Provisioned throughput | arn:aws:bedrock:us-east-1:123456789012:provisioned-model/PtModelId |
| Cross-region inference profile | arn:aws:bedrock:us-east-1::foundation-model/us.anthropic.claude-sonnet-4-5-20251001-v1:0 (system-defined; no account segment) |
| Direct foundation model invocation | (null — no resource ARN) |
Practical implication: To get resource-level tag attribution (via resourceTags/ tag prefix), you must route traffic through application inference profiles with tags applied. This is the only way to attach resource tags to Bedrock on-demand inference. If all your traffic hits foundation models directly, this column will be null and resourceTags/ values will be empty.
1.13 product_region vs line_item_availability_zone
Type: string (both)
Always present: Yes
| Column | What it contains | For Bedrock |
|---|---|---|
product_region |
The AWS region where the service is configured or billed | The region code of the AWS region in which the billing account created the Bedrock usage (may differ from execution region for cross-region inference) |
line_item_availability_zone |
The specific AZ for zonal services | Always empty for Bedrock — Bedrock is a regional service with no AZ-level billing |
Cross-region inference note: When cross-region inference is active, the originating region’s billing code appears in line_item_usage_type (e.g., USE1-...), and product_region reflects the originating region (us-east-1). The actual compute may run in us-west-2 or other regions — CUR does not expose the execution region. Use Bedrock invocation logs for execution-region visibility.
product_from_location vs product_region:
| Column | Example |
|---|---|
product_region |
us-east-1 |
product_from_location |
US East (N. Virginia) |
Both represent the same region. product_from_location is human-readable; use product_region for programmatic filtering.
1.14 bill_payer_account_id vs line_item_usage_account_id
Type: string (both)
Always present: Yes
| Column | Contains | Use case |
|---|---|---|
bill_payer_account_id |
AWS account ID of the management (payer) account | Organizational rollup; identifying which org the usage belongs to |
line_item_usage_account_id |
AWS account ID where the Bedrock API call was made | Per-account cost allocation; multi-account fleet analysis |
bill_payer_account_name |
Human-readable name of management account | CUR 2.0 only — not available in legacy CUR |
line_item_usage_account_name |
Human-readable name of the member account | CUR 2.0 only — not available in legacy CUR |
Multi-account pattern: In an AWS Organization, the CUR is exported from the management account and contains usage from all member accounts. Filter to a specific team’s account:
WHERE line_item_usage_account_id = '123456789012'
For consolidated cross-account rollup:
GROUP BY bill_payer_account_id, line_item_usage_account_id, line_item_usage_account_name
The combination of line_item_usage_account_id + line_item_iam_principal is the two-level attribution hierarchy: account → identity.
1.15 line_item_usage_start_date / line_item_usage_end_date
Type: timestamp
Always present: Yes
These are the Athena partition keys — always filter on them.
CUR 2.0 supports three granularities configured at export creation time:
| Granularity | usage_start → usage_end |
Best for |
|---|---|---|
HOURLY |
1-hour windows | Real-time alerting, anomaly detection |
DAILY |
24-hour windows | Standard cost reporting, dashboards |
MONTHLY |
Calendar month | Invoice reconciliation, executive reports |
Bedrock-specific granularity note: Bedrock does not report token usage in real time. There is typically a 4–8 hour lag between when a Bedrock API call is made and when the corresponding token usage appears in a HOURLY CUR export. For near-real-time attribution (sub-hour), use Bedrock invocation logs, not CUR.
Partition projection (Athena): Always include a date filter to avoid full-table scans:
WHERE line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
AND line_item_usage_start_date < CURRENT_DATE
Section 2 — Columns That Don’t Apply to Bedrock
These columns are present in the CUR 2.0 schema (125 fixed columns) but will always be null or empty for Bedrock line items. Knowing this prevents confusing nulls during data exploration.
| Column group | Columns | Why null for Bedrock |
|---|---|---|
| Reserved Instance | reservation_reservation_arn, reservation_effective_cost, reservation_amortized_upfront_fee_for_billing_period, reservation_unused_quantity, reservation_unused_recurring_fee |
Bedrock has no RI purchasing model |
| Savings Plan | savings_plan_savings_plan_arn, savings_plan_offering_type, savings_plan_payment_option, savings_plan_purchase_term |
Bedrock Savings Plans (launched 2025) use a different column: savings_plan_savings_plan_effective_cost; the RI-style SP columns remain null |
| EC2 Instance | product_instance_type, product_instance_family, product_vcpu, product_memory, product_tenancy, product_operating_system |
Bedrock is serverless; no instance selection |
| Availability Zone | line_item_availability_zone |
Bedrock is regional, not zonal |
| Network | product_transfer_type, product_from_region_code, product_to_region_code |
Data transfer is billed as a separate AmazonEC2 line item (egress), not within the Bedrock line item |
| EBS/Storage | product_volume_type, product_storage_media |
N/A |
| Spot | product_spot_price |
Bedrock has no Spot equivalent (Flex inference is the analogous concept but billed differently) |
Section 3 — CUR 2.0 Opt-In Columns: Activation Requirements
3.1 line_item_iam_principal — IAM Principal Attribution
Activation: AWS Billing Console → Data Exports → Edit export → Additional content → IAM principal data
CLI equivalent:
aws bcm-data-exports update-export \
--export-arn "arn:aws:bcm-data-exports:us-east-1:123456789012:export/your-export-id" \
--export '{
"DataQuery": {
"QueryStatement": "SELECT * FROM COST_AND_USAGE_REPORT",
"TableConfigurations": {
"COST_AND_USAGE_REPORT": {
"INCLUDE_IAM_PRINCIPAL_DATA": "TRUE",
"TIME_GRANULARITY": "DAILY",
"INCLUDE_RESOURCES": "TRUE",
"INCLUDE_SPLIT_COST_ALLOCATION_DATA": "FALSE"
}
}
}
}'
Key constraints:
- Data begins from activation date — no retroactive backfill
- If activated today, data from before today will have null
line_item_iam_principal - 24-hour propagation delay for data to appear in Athena
- Requires the CUR 2.0 export, not legacy CUR
3.2 Resource Tags (INCLUDE_RESOURCES = TRUE + tag activation)
Two-step process:
- Enable
INCLUDE_RESOURCES = TRUEin the export configuration (populatesline_item_resource_id) - Activate each tag key in AWS Billing → Cost Allocation Tags (populates
resourceTags/columns)
Activation timeline: After activating a tag key, wait 24 hours before the tag appears in CUR data. Historical data before activation date will not have tag values.
Not retroactive: If you activate resourceTags/team on June 18, data from June 17 and earlier shows null for that tag even if the resource was tagged for months.
3.3 Split Cost Allocation Data (INCLUDE_SPLIT_COST_ALLOCATION_DATA)
Splits shared resource costs (e.g., an EKS cluster) across consumer namespaces. For Bedrock, this feature has limited applicability because Bedrock is already attributed per API call via IAM principal. However, if you run Bedrock via a shared SageMaker endpoint, split cost allocation can apportion the endpoint cost by traffic ratio.
Not recommended to enable for pure Bedrock workloads — adds schema complexity without benefit.
Section 4 — Athena DDL with Partition Projection
CREATE EXTERNAL TABLE IF NOT EXISTS bedrock_cur (
-- Bill
bill_invoice_id STRING,
bill_billing_entity STRING,
bill_bill_type STRING,
bill_payer_account_id STRING,
bill_payer_account_name STRING,
bill_billing_period_start_date TIMESTAMP,
bill_billing_period_end_date TIMESTAMP,
-- Line item (core)
line_item_usage_account_id STRING,
line_item_usage_account_name STRING,
line_item_line_item_type STRING,
line_item_usage_start_date TIMESTAMP,
line_item_usage_end_date TIMESTAMP,
line_item_product_code STRING,
line_item_usage_type STRING,
line_item_operation STRING,
line_item_availability_zone STRING,
line_item_resource_id STRING,
line_item_usage_amount DOUBLE,
line_item_normalization_factor DOUBLE,
line_item_normalized_usage_amount DOUBLE,
line_item_currency_code STRING,
line_item_unblended_rate STRING,
line_item_unblended_cost DOUBLE,
line_item_blended_rate STRING,
line_item_blended_cost DOUBLE,
line_item_net_unblended_rate STRING,
line_item_net_unblended_cost DOUBLE,
line_item_iam_principal STRING, -- CUR 2.0 opt-in
-- Product
product_servicecode STRING,
product_region STRING,
product_from_location STRING,
product_from_location_type STRING,
product_product_name STRING,
-- Pricing
pricing_rate_id STRING,
pricing_public_on_demand_cost DOUBLE,
pricing_public_on_demand_rate STRING,
pricing_term STRING,
pricing_unit STRING,
-- Savings Plan (present but null for standard Bedrock)
savings_plan_savings_plan_arn STRING,
savings_plan_savings_plan_rate STRING,
savings_plan_used_commitment DOUBLE,
savings_plan_total_commitment_to_date DOUBLE,
savings_plan_savings_plan_effective_cost DOUBLE,
savings_plan_amortized_upfront_commitment_for_billing_period DOUBLE,
savings_plan_recurring_commitment_for_billing_period DOUBLE,
savings_plan_offering_type STRING,
savings_plan_payment_option STRING,
savings_plan_purchase_term STRING,
savings_plan_start_time STRING,
savings_plan_end_time STRING,
-- Reservation (always null for Bedrock)
reservation_reservation_arn STRING,
reservation_effective_cost DOUBLE,
-- Tags (map — includes resourceTags/, iamPrincipal/, accountTag/, costCategory/)
tags MAP<STRING, STRING>
)
PARTITIONED BY (
-- Partition on year/month for Athena partition projection
year STRING,
month STRING
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION 's3://your-cur-bucket/your-export-prefix/your-export-name/'
TBLPROPERTIES (
-- Partition projection: eliminates need for MSCK REPAIR TABLE
'projection.enabled' = 'true',
'projection.year.type' = 'integer',
'projection.year.range' = '2024,2030',
'projection.year.digits' = '4',
'projection.month.type' = 'integer',
'projection.month.range' = '1,12',
'projection.month.digits' = '2',
'storage.location.template' = 's3://your-cur-bucket/your-export-prefix/your-export-name/year=${year}/month=${month}/',
'classification' = 'parquet',
'parquet.compression' = 'SNAPPY'
);
Notes on the DDL:
- Replace
your-cur-bucket,your-export-prefix,your-export-namewith your actual values - The S3 path format above matches CUR 2.0’s default folder structure from Data Exports
projection.enabled = truemeans new monthly data is automatically queryable withoutMSCK REPAIR TABLE- CUR 2.0 exports to Parquet by default; Parquet is strongly preferred over CSV for Athena performance
- For daily-granularity exports, the partition structure is
year=${year}/month=${month}/— same structure; the files inside cover individual days
Bedrock-specific view (recommended for daily use):
CREATE OR REPLACE VIEW bedrock_usage AS
SELECT
line_item_usage_start_date,
line_item_usage_end_date,
bill_payer_account_id,
line_item_usage_account_id,
line_item_usage_account_name,
line_item_product_code,
line_item_usage_type,
line_item_operation,
line_item_line_item_type,
line_item_usage_amount,
line_item_unblended_cost,
line_item_net_unblended_cost,
line_item_resource_id,
line_item_iam_principal,
pricing_unit,
pricing_public_on_demand_rate,
product_region,
tags,
-- Extracted fields for convenience
REGEXP_EXTRACT(line_item_usage_type, '^([^-]+)-', 1) AS billing_region_code,
REGEXP_EXTRACT(line_item_usage_type, '^[^-]+-(.+?)-(input|output|cache|ModelUnit|mantle)', 1) AS model_name,
CASE
WHEN line_item_usage_type LIKE '%-input-tokens%' THEN 'input'
WHEN line_item_usage_type LIKE '%-output-tokens%' THEN 'output'
WHEN line_item_usage_type LIKE '%-cache-read-input-token%' THEN 'cache_read'
WHEN line_item_usage_type LIKE '%-cache-write-input-token%'THEN 'cache_write'
WHEN line_item_usage_type LIKE '%-ModelUnit%' THEN 'provisioned_throughput'
WHEN line_item_usage_type LIKE '%-TextUnit%' THEN 'guardrails_text'
ELSE 'other'
END AS token_type,
CASE
WHEN line_item_usage_type LIKE '%-flex%' THEN 'flex'
WHEN line_item_usage_type LIKE '%-priority%' THEN 'priority'
WHEN line_item_usage_type LIKE '%-ModelUnit%'THEN 'provisioned'
ELSE 'standard'
END AS inference_tier,
line_item_usage_type LIKE '%-cross-region-global%' AS is_cross_region,
tags['iamPrincipal/team'] AS iam_team,
tags['iamPrincipal/cost-center'] AS iam_cost_center,
tags['iamPrincipal/project'] AS iam_project,
tags['resourceTags/Environment'] AS resource_env
FROM bedrock_cur
WHERE line_item_product_code = 'AmazonBedrock'
AND line_item_line_item_type IN ('Usage', 'SavingsPlanCoveredUsage');
Section 5 — Five Production-Grade Athena Queries
All queries assume the bedrock_usage view above is in place and use your_cur_database as the Glue database name.
Query 1: Complete cost breakdown by model + token direction + tier + account
-- Full attribution matrix: who spent what on which model and token type
-- Recommended: run daily, output to QuickSight or Grafana
SELECT
DATE_TRUNC('day', line_item_usage_start_date) AS usage_date,
line_item_usage_account_id,
line_item_usage_account_name,
product_region AS aws_region,
model_name,
token_type,
inference_tier,
is_cross_region,
line_item_operation,
COUNT(DISTINCT line_item_usage_type) AS distinct_usage_types,
SUM(line_item_usage_amount) AS total_tokens,
SUM(line_item_unblended_cost) AS total_cost_usd,
SUM(line_item_net_unblended_cost) AS net_cost_usd,
-- Effective rate per million tokens
SUM(line_item_unblended_cost)
/ NULLIF(SUM(line_item_usage_amount), 0)
* 1000000 AS effective_rate_per_mtok
FROM your_cur_database.bedrock_usage
WHERE line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
AND line_item_usage_start_date < CURRENT_DATE
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9
ORDER BY usage_date DESC, total_cost_usd DESC;
Query 2: IAM principal attribution with team tag extraction
-- Per-identity cost attribution; requires INCLUDE_IAM_PRINCIPAL_DATA = TRUE
-- session_name extracts the human-readable part from assumed-role ARNs
SELECT
DATE_TRUNC('day', line_item_usage_start_date) AS usage_date,
line_item_usage_account_id,
line_item_iam_principal,
-- For assumed-role ARNs, extract session name (team slug from gateway AssumeRole)
REGEXP_EXTRACT(line_item_iam_principal,
'assumed-role/[^/]+/(.+)$', 1) AS session_name,
-- IAM principal tags (session tags from gateway, or tags on the IAM role)
iam_team,
iam_cost_center,
iam_project,
model_name,
token_type,
SUM(line_item_usage_amount) AS total_tokens,
SUM(line_item_unblended_cost) AS total_cost_usd
FROM your_cur_database.bedrock_usage
WHERE line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
AND line_item_usage_start_date < CURRENT_DATE
AND line_item_iam_principal IS NOT NULL
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9
ORDER BY usage_date DESC, total_cost_usd DESC;
Query 3: Cache efficiency ratio — cache reads vs total input tokens
-- Cache efficiency: what fraction of input tokens are served from cache?
-- High cache hit rate → lower effective cost per input token
-- Formula: cache_read_tokens / (standard_input_tokens + cache_read_tokens + cache_write_tokens)
WITH daily_token_breakdown AS (
SELECT
DATE_TRUNC('day', line_item_usage_start_date) AS usage_date,
line_item_usage_account_id,
model_name,
iam_team,
SUM(CASE WHEN token_type = 'input' THEN line_item_usage_amount ELSE 0 END) AS input_tokens,
SUM(CASE WHEN token_type = 'output' THEN line_item_usage_amount ELSE 0 END) AS output_tokens,
SUM(CASE WHEN token_type = 'cache_read' THEN line_item_usage_amount ELSE 0 END) AS cache_read_tokens,
SUM(CASE WHEN token_type = 'cache_write' THEN line_item_usage_amount ELSE 0 END) AS cache_write_tokens,
SUM(CASE WHEN token_type = 'input' THEN line_item_unblended_cost ELSE 0 END) AS input_cost,
SUM(CASE WHEN token_type = 'output' THEN line_item_unblended_cost ELSE 0 END) AS output_cost,
SUM(CASE WHEN token_type = 'cache_read' THEN line_item_unblended_cost ELSE 0 END) AS cache_read_cost,
SUM(CASE WHEN token_type = 'cache_write' THEN line_item_unblended_cost ELSE 0 END) AS cache_write_cost,
SUM(line_item_unblended_cost) AS total_cost
FROM your_cur_database.bedrock_usage
WHERE line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
AND line_item_usage_start_date < CURRENT_DATE
AND token_type IN ('input', 'output', 'cache_read', 'cache_write')
GROUP BY 1, 2, 3, 4
)
SELECT
usage_date,
line_item_usage_account_id,
model_name,
iam_team,
input_tokens,
output_tokens,
cache_read_tokens,
cache_write_tokens,
-- Cache hit ratio: cache reads / all input-side tokens
ROUND(
cache_read_tokens * 100.0
/ NULLIF(input_tokens + cache_read_tokens + cache_write_tokens, 0),
2
) AS cache_hit_pct,
total_cost AS total_cost_usd,
-- What would cost be with no cache at all (all reads billed at input rate)?
-- Requires knowing the input rate; this uses the ratio from actuals
(cache_read_cost / NULLIF(cache_read_tokens, 0))
* (input_tokens + cache_read_tokens + cache_write_tokens)
+ output_cost AS cost_without_cache_usd,
total_cost - (
(cache_read_cost / NULLIF(cache_read_tokens, 0))
* (input_tokens + cache_read_tokens + cache_write_tokens)
+ output_cost
) AS cache_savings_usd
FROM daily_token_breakdown
ORDER BY usage_date DESC, total_cost DESC;
Query 4: PT idle cost detection — ModelUnit charges with low token throughput
-- Provisioned throughput idle detection
-- ModelUnit-month charges are fixed regardless of utilization
-- Join PT costs against actual inference token volume on the same model
WITH pt_charges AS (
SELECT
DATE_TRUNC('month', line_item_usage_start_date) AS billing_month,
line_item_usage_account_id,
model_name,
line_item_resource_id AS pt_arn,
SUM(line_item_usage_amount) AS model_units_consumed,
SUM(line_item_unblended_cost) AS pt_cost_usd
FROM your_cur_database.bedrock_usage
WHERE token_type = 'provisioned_throughput'
AND line_item_usage_start_date >= DATE_ADD('day', -90, CURRENT_DATE)
GROUP BY 1, 2, 3, 4
),
inference_tokens AS (
SELECT
DATE_TRUNC('month', line_item_usage_start_date) AS billing_month,
line_item_usage_account_id,
model_name,
SUM(CASE WHEN token_type = 'input' THEN line_item_usage_amount ELSE 0 END) AS input_tokens,
SUM(CASE WHEN token_type = 'output' THEN line_item_usage_amount ELSE 0 END) AS output_tokens,
SUM(line_item_unblended_cost) AS inference_cost_usd
FROM your_cur_database.bedrock_usage
WHERE token_type IN ('input', 'output')
AND inference_tier = 'provisioned'
AND line_item_usage_start_date >= DATE_ADD('day', -90, CURRENT_DATE)
GROUP BY 1, 2, 3
)
SELECT
p.billing_month,
p.line_item_usage_account_id,
p.model_name,
p.pt_arn,
p.model_units_consumed,
p.pt_cost_usd,
COALESCE(i.input_tokens, 0) AS input_tokens,
COALESCE(i.output_tokens, 0) AS output_tokens,
COALESCE(i.inference_cost_usd, 0) AS inference_cost_usd,
p.pt_cost_usd + COALESCE(i.inference_cost_usd, 0) AS total_pt_spend_usd,
-- Utilization signal: tokens generated per model-unit-month; low = idle
COALESCE(i.input_tokens + i.output_tokens, 0)
/ NULLIF(p.model_units_consumed, 0) AS tokens_per_model_unit,
-- Flag idle: token throughput below 10M tokens per model-unit-month suggests underutilization
CASE WHEN COALESCE(i.input_tokens + i.output_tokens, 0)
/ NULLIF(p.model_units_consumed, 0) < 10000000
THEN TRUE ELSE FALSE
END AS likely_idle
FROM pt_charges p
LEFT JOIN inference_tokens i
ON p.billing_month = i.billing_month
AND p.line_item_usage_account_id = i.line_item_usage_account_id
AND p.model_name = i.model_name
ORDER BY p.billing_month DESC, p.pt_cost_usd DESC;
Query 5: Cross-account consolidated billing rollup
-- Org-wide Bedrock spend rollup for management account CUR
-- Groups by payer → member account → model family → token type
-- Use for monthly CFO reporting across business units
WITH monthly_by_account AS (
SELECT
DATE_TRUNC('month', line_item_usage_start_date) AS billing_month,
bill_payer_account_id,
line_item_usage_account_id,
line_item_usage_account_name,
-- Model family grouping from model_name segment
CASE
WHEN model_name LIKE '%Claude%' THEN 'Anthropic Claude'
WHEN model_name LIKE '%Nova%' THEN 'Amazon Nova'
WHEN model_name LIKE '%Titan%' THEN 'Amazon Titan'
WHEN model_name LIKE '%llama%' THEN 'Meta Llama'
WHEN model_name LIKE '%mistral%' THEN 'Mistral'
WHEN model_name LIKE '%cohere%' THEN 'Cohere'
WHEN model_name LIKE '%gpt%' THEN 'OpenAI (Bedrock Marketplace)'
ELSE 'Other'
END AS model_family,
token_type,
inference_tier,
is_cross_region,
SUM(line_item_usage_amount) AS total_tokens,
SUM(line_item_unblended_cost) AS total_cost_usd,
SUM(line_item_net_unblended_cost) AS net_cost_usd
FROM your_cur_database.bedrock_usage
WHERE line_item_usage_start_date >= DATE_ADD('month', -3, DATE_TRUNC('month', CURRENT_DATE))
AND line_item_usage_start_date < DATE_TRUNC('month', CURRENT_DATE)
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8
)
SELECT
billing_month,
bill_payer_account_id,
line_item_usage_account_id,
line_item_usage_account_name,
model_family,
token_type,
inference_tier,
is_cross_region,
total_tokens,
total_cost_usd,
net_cost_usd,
-- Month-over-month cost change (self-join via window function)
total_cost_usd - LAG(total_cost_usd) OVER (
PARTITION BY line_item_usage_account_id, model_family, token_type, inference_tier
ORDER BY billing_month
) AS mom_cost_delta_usd,
-- Share of this account's total Bedrock spend
total_cost_usd * 100.0 / NULLIF(SUM(total_cost_usd) OVER (
PARTITION BY billing_month, bill_payer_account_id
), 0) AS pct_of_org_bedrock_spend
FROM monthly_by_account
ORDER BY billing_month DESC, total_cost_usd DESC;
Section 6 — CUR 2.0 vs CUR Legacy Differences for Bedrock
| Capability | Legacy CUR 1.0 | CUR 2.0 (Data Exports) |
|---|---|---|
| Bedrock token-type line items | Yes | Yes |
line_item_usage_type model+token anatomy |
Yes | Yes |
line_item_operation = InvokeModelInference |
Yes (from Jan 2026) | Yes (from Jan 2026) |
line_item_iam_principal column |
No | Yes (opt-in, data from Apr 8, 2026) |
iamPrincipal/ tag prefix in tags map |
No | Yes (opt-in) |
tags as structured map column |
No (flat resource_tags_* columns) |
Yes (single tags MAP<STRING,STRING>) |
| Fixed schema (no monthly column drift) | No — schema can change each month | Yes — 125 fixed columns, stable |
bill_payer_account_name |
No | Yes |
line_item_usage_account_name |
No | Yes |
product_* column naming |
Flat: product_servicecode, product_region |
Map-style: product['servicecode'] OR dot: product.servicecode (flat still works) |
| Athena table name | Varies by export name configuration | COST_AND_USAGE_REPORT (default) |
| Configurable granularity | Hourly or daily | HOURLY, DAILY, or MONTHLY |
| Parquet export | Available | Default (preferred) |
| Delivery lag | 24h for daily files | 24h for daily files (same) |
Bedrock Savings Plan SavingsPlanCoveredUsage rows |
Yes | Yes |
Cross-region inference *-cross-region-global suffix |
Yes | Yes |
| Session tags on IAM roles surfaced to CUR | No | Yes via iamPrincipal/ prefix (opt-in) |
userAttribute/ prefix (IAM Identity Center attributes) |
No | Yes (CUR 2.0 only) |
Migration path from legacy CUR: The most significant breaking change when migrating from legacy CUR to CUR 2.0 is the product_* column naming convention. Legacy CUR has flat columns like product_product_name, product_servicecode. CUR 2.0 supports both the dot accessor (product.servicecode) and the bracket map accessor (product['servicecode']), but the legacy flat column names do not exist in CUR 2.0. Any existing Athena queries using product_product_name must be updated to product['product_name'] or the equivalent dot form.
The second migration change is the tags column. Legacy CUR creates individual columns named resource_tags_user_Environment, resource_tags_user_Team, etc. — one column per activated tag key. CUR 2.0 consolidates all tags into a single MAP<STRING,STRING> column accessed as tags['resourceTags/Environment']. Queries that filter on legacy flat tag columns must be rewritten.
Quick-Reference Cheat Sheet
PRODUCT CODE AmazonBedrock (not AWSMarketplace, not AmazonSageMaker)
LINE ITEM TYPE Usage (add SavingsPlanCoveredUsage if org uses Bedrock SPs)
USAGE AMOUNT UNIT tokens (not hours, not requests)
USAGE TYPE FORMAT {REGION}-{MODEL}-{TOKEN_TYPE}[-TIER][-cross-region-global]
TOKEN TYPES input / output / cache_read / cache_write / provisioned_throughput
PRICING UNIT Token (on-demand) | ModelUnit-month (PT) | TextUnit (guardrails)
IAM PRINCIPAL CUR 2.0 opt-in only | data from Apr 8, 2026 | no retroactive fill
RESOURCE TAGS Activate in Billing Console | 24h delay | not retroactive
PARTITION KEY line_item_usage_start_date — always filter this column in Athena
GRANULARITY LAG 4–8h delay from API call to CUR row; use invocation logs for sub-hour
NULL COLUMNS RI columns, SP columns (except savings_plan_effective_cost), EC2 instance, AZ