← Agent Frameworks 🕐 15 min read
Agent Frameworks

AWS Cost Categories — Retroactive Attribution for Bedrock AI Spend (2026)

AWS Cost Categories is a rules-based cost allocation service that maps raw billing line items to organizational dimensions — teams, business units, environments, applications — without requiring resou

AWS Cost Categories is a rules-based cost allocation service that maps raw billing line items to organizational dimensions — teams, business units, environments, applications — without requiring resource-level tagging. For AI workloads, and Bedrock in particular, Cost Categories is the primary mechanism for attributing shared inference spend to the teams and BUs that consumed it, because Bedrock usage often flows through centralized gateways where individual resource tags are absent or infeasible.

This note covers the full attribution stack: what Cost Categories are and how the rule engine works, the 12-month retroactive application feature, rule design for Bedrock’s CUR columns, inherited value and split charge methodologies for shared AI platforms, hierarchical category nesting for multi-dimensional attribution, surface differences across Cost Explorer vs. CUR vs. CUDOS, known gaps (FOCUS, Budgets API), and a worked design for a 50-team, 5-BU organization using LiteLLM as a Bedrock gateway.


1. What Cost Categories Are and How They Work

Cost Categories are key-value pairs applied to every cost line item in your AWS billing data. The key is the category name you define (e.g., Team, BusinessUnit, AIProduct). The values are the labels each line item gets after the rule engine runs (e.g., platform-eng, BU-Finance, summarization-service).

The rule engine is first-match, top-down. Rules are evaluated in declaration order; the first rule that matches a line item wins. If no rule matches and you have defined a default value, the line item is labeled with the default. If no default exists, the line item gets no label for that category.

Rule Dimensions

Regular rules support the following dimensions:

Dimension Notes
Account Exact match on account ID; contains/starts with/ends with match on account name
Service AWS service name, e.g., Amazon Bedrock
Usage Type Fine-grained usage descriptor, e.g., USE1-Bedrock:Titan-InputTokens
Charge Type Usage, Tax, Credit, RIFee, SavingsPlanCoveredUsage, etc.
Region AWS region identifier
Tag key Cost allocation tag key applied to resources
Cost Category Value from another Cost Category — enables nesting
Billing Entity AWS vs. AWS Marketplace

Each rule condition supports operators: Is, Is not, Is absent, Contains, Starts with, Ends with. A rule can combine conditions with AND or OR.

Inherited Value Rules

A second rule type, Inherited Value, generates category values dynamically from the underlying dimension rather than assigning a static label. You specify a dimension (Account or Tag key) and the rule absorbs whatever value is present on the line item as the category value.

Example: an Inherited Value rule on Tag key = team produces category values alpha, beta, gamma automatically, matching whatever the team tag says on each resource. No static enumeration needed. When a new team appears and starts tagging resources, their value appears in the category without rule edits.

This is the correct pattern for Bedrock attribution via IAM principal tags — see Section 4.

CUR Output

Once a Cost Category is created, AWS adds a column to the Cost and Usage Report for each category you define. The column header format is:

costCategory/<CategoryName>

In Athena, the column names are normalized to lowercase with underscores:

cost_category_team
cost_category_businessunit
cost_category_aienvironment

Each row carries the matched category value, or null if no rule matched and no default was set.


2. The 12-Month Retroactive Application Feature

What It Does

When creating or editing a Cost Category, you set an Effective Date — the month from which the rules apply. By default this is the current month. You can set it to any month within the previous 12 calendar months. AWS then re-processes all CUR data from that start month using your current rule set and updates Cost Explorer, Budgets, and CUR columns accordingly.

Mid-month changes are automatically back-dated to the first day of the current billing period. If you update rules on June 15, the rules apply retroactively from June 1 without explicit intervention.

How to Trigger

Console: During create or edit flow, at Step 3 (Lookback period), choose the start month from the dropdown. Any month within the prior 12 periods is selectable.

API / CLI:

{
  "Name": "BedrockTeam",
  "Rules": [...],
  "DefaultValue": "Unattributed",
  "EffectiveStart": "2025-07-01T00:00:00Z"
}

Pass EffectiveStart set to the desired historical month start in ISO 8601 format. AWS processes the backfill asynchronously; status transitions from Processing to Applied once complete (up to 24 hours for large accounts).

CUR Backfill (separate step): Cost Category values update in Cost Explorer and future CUR deliveries automatically. For historical CUR files already delivered to S3, you must open a support case specifying the report name and billing periods to be regenerated. This is a manual AWS-side operation; there is no API call to trigger CUR file regeneration.

Limitations

  • Retroactive window is hard-capped at 12 months. You cannot attribute costs older than 12 months from the current date to a new category.
  • The retroactive backfill covers Cost Explorer and CUR data, but split charge rules (Section 5) recalculate only in the Cost Categories details page and are not written back to CUR.
  • Status must reach Applied before inherited-value category values can be used as split charge sources or targets.
  • Editing an existing category mid-month with a new retroactive start does not merge gracefully with prior attribution runs for the same period — the entire period reruns under the new rule set.

3. Cost Category Rules for Bedrock: Account and IAM Principal Mapping

CUR 2.0 Columns Relevant to Bedrock

CUR Column Content
line_item_product_code AmazonBedrock
line_item_usage_account_id AWS account ID where the Bedrock call originated
line_item_usage_type Region + model + token direction, e.g., USE1-Bedrock:Claude3Sonnet-InputTokens
line_item_operation Operation name, e.g., InvokeModel
line_item_iam_principal IAM ARN of the caller (CUR 2.0 only, requires opt-in)
line_item_unblended_cost Dollar cost for the line item
resource_tags/user:* Resource-level tags on inference profiles
resource_tags/iamPrincipal/* Tags inherited from the IAM principal (CUR 2.0 opt-in)

Mapping Account IDs to Business Units

Regular rules on the Account dimension assign static BU labels by account ID:

{
  "Name": "BusinessUnit",
  "Rules": [
    {
      "Value": "BU-Finance",
      "Rule": {
        "Dimensions": {
          "Key": "LINKED_ACCOUNT",
          "Values": ["111122223333", "111122224444"],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "BU-Engineering",
      "Rule": {
        "Dimensions": {
          "Key": "LINKED_ACCOUNT",
          "Values": ["555566667777", "555566668888", "555566669999"],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "BU-Platform",
      "Rule": {
        "Dimensions": {
          "Key": "LINKED_ACCOUNT",
          "Values": ["999900001111"],
          "MatchOptions": ["EQUALS"]
        }
      }
    }
  ],
  "DefaultValue": "BU-Unassigned"
}

This is account-granularity attribution. For organizations where each BU owns its own AWS account(s), this is the cleanest attribution path: no tagging discipline required, retroactive to 12 months, works for all services including Bedrock.

Mapping IAM Principals to Teams

When Bedrock is called through a shared gateway (LiteLLM, Amazon API Gateway, custom proxy) from a centralized account, line_item_usage_account_id resolves to the platform account, not the consuming team. IAM principal attribution is the fallback.

Requirement: CUR 2.0 (AWS Data Exports) with IAM principal tracking enabled. The line_item_iam_principal column is absent from CUR 1.0.

A Cost Category rule on Tag key = iamPrincipal/team with Inherited Value rule type generates team labels from session tags without manual enumeration:

{
  "Name": "AITeam",
  "RuleType": "INHERITED_VALUE",
  "InheritedValue": {
    "DimensionName": "TAG",
    "DimensionKey": "iamPrincipal/team"
  }
}

This works when the LiteLLM gateway (or any gateway) calls sts:AssumeRole with session tags that include team. The tag value (fraud-detection, customer-support, data-science) propagates to resource_tags/iamPrincipal/team in CUR 2.0 and the Inherited Value rule picks it up automatically.

For role-per-team architectures (one IAM role per team that assumes a shared Bedrock execution role), an exact-match rule on Tag key = iamPrincipal/team using Contains or Starts with operators can bucket teams into groups:

{
  "Value": "AI-DataPlatform",
  "Rule": {
    "Tags": {
      "Key": "iamPrincipal/team",
      "Values": ["data-science", "ml-engineering", "feature-store"],
      "MatchOptions": ["EQUALS"]
    }
  }
}

4. Inherited Value Source: Tag Inheritance vs. Split Charge Rules

When to Use Inherited Value

Inherited Value rules are the correct choice when:

  • Teams already tag resources (or IAM principals carry session tags) consistently
  • Team membership changes frequently — avoiding constant rule edits
  • You want dynamic category values that mirror your tag namespace without a static allowlist

For Bedrock via inference profiles: application inference profiles are Bedrock resources that carry standard AWS resource tags. A Cost Category with an Inherited Value rule on Tag key = Application dynamically generates category values from the Application tag on each inference profile, so every new application that creates a profile automatically appears in attribution reports.

Tag Inheritance Mechanics

AWS tag inheritance in Cost Categories refers to the INHERITED_VALUE rule type — it inherits the tag value into the category value space. This is distinct from the separate “tag inheritance” feature (passing tags from parent accounts/OUs to member accounts), which is an AWS Organizations feature unrelated to Cost Categories.

The propagation delay for IAM principal tags to appear in CUR 2.0 is 24–48 hours after the session is established. Cost Category processing adds another up to 24 hours. Budget: up to 72 hours from first inference call to category value appearing in Cost Explorer.

When to Use Split Charge Rules Instead

Split charge rules are appropriate when:

  • Costs genuinely cannot be pre-attributed (shared enterprise support, AWS Config, shared NAT Gateway)
  • Bedrock is accessed through a gateway where per-team IAM session tagging is not feasible
  • You need proportional reallocation of a “Platform” bucket across consuming BUs after the fact

The two mechanisms are composable: use Inherited Value or account-based rules for direct attribution, then add a split charge rule to distribute a residual “Unattributed” or “SharedPlatform” bucket across known BU values.


5. Split Charge Rules for Shared Bedrock Costs

Split charge rules reallocate costs within a Cost Category from a source value to one or more target values. This is the correct mechanism for a centralized Bedrock platform account whose costs cannot be attributed per-team through IAM tagging (legacy architectures, third-party models, Knowledge Base retrievals).

Three Allocation Methods

Proportional Allocates source costs to targets in proportion to each target’s existing categorized costs. If BU-Engineering has $80K in categorized spend and BU-Finance has $20K, a proportional split from “SharedPlatform” assigns 80% to BU-Engineering and 20% to BU-Finance.

Use proportional when: the platform serves teams whose usage volume varies, and you want the cost allocation to reflect consumption weight rather than arbitrary percentages.

Fixed You specify exact percentages per target. Percentages must sum to 100%.

Source: SharedAIPlatform
Targets:
  BU-Engineering: 60%
  BU-Finance:     25%
  BU-Operations:  15%

Use fixed when: platform costs are negotiated up front in budget allocations (“we agreed engineering takes 60% of platform cost”), or when proportional attribution would create perverse incentives (teams reducing their own categorized spend to lower their platform allocation).

Even Split Divides source costs equally across all targets regardless of their individual spend.

Use even split when: the platform provides equal standing capacity to all BUs (each BU gets the same number of provisioned throughput units or token budget), and equal cost allocation matches the service-level agreement.

Configuration Constraints

  • A value can be a source in at most one split charge rule
  • A value used as source cannot simultaneously be a target in another rule
  • A value used as target can appear in multiple rules (multiple sources can allocate to the same target)
  • Maximum 10 split charge rules per Cost Category
  • For rules involving INHERITED_VALUE-generated values as sources, the category must be in Applied status before configuring the split

Split Charge Data Visibility

Split charge results appear only in the Cost Categories details page in the Billing console and the CSV download from that page. They do not flow into:

  • CUR line items (no column shows split-adjusted cost)
  • Cost Explorer filters or group-by results
  • AWS Budgets
  • QuickSight / CUDOS dataset joins

This is a significant limitation for operationalized reporting: split charge is an analysis-layer allocation in the console, not a billing-layer rewrite.


6. Cost Category Nesting: Multi-Dimensional Bedrock Attribution

Cost Categories support hierarchical nesting by using a Cost Category as a dimension in another Cost Category’s rules. This allows you to build an account→team→BU hierarchy where each layer resolves independently.

Three-Layer Hierarchy for Bedrock Attribution

Layer 1: AITeam category (finest grain)

Rules map line_item_usage_account_id (for dedicated accounts) or iamPrincipal/team session tag (for shared account) to team labels:

fraud-detection
customer-support
recommendation-engine
... (up to 50 team values)

Layer 2: AIProduct category (application grain)

Inherits from resource tag Application on inference profiles, or maps IAM role names to product labels:

conversational-assistant
document-summarization
data-extraction

Layer 3: BusinessUnit category (BU grain)

Uses the AITeam Cost Category as a dimension (dimension type: Cost Category):

{
  "Value": "BU-Finance",
  "Rule": {
    "CostCategories": {
      "Key": "AITeam",
      "Values": ["fraud-detection", "risk-modeling", "compliance-automation"],
      "MatchOptions": ["EQUALS"]
    }
  }
}

The resulting CUR has three additional columns:

costCategory/AITeam       → fraud-detection
costCategory/AIProduct    → document-summarization
costCategory/BusinessUnit → BU-Finance

Any Athena query can group by any combination of these three dimensions independently, enabling queries like “what did BU-Finance spend on document-summarization last quarter?” without complex tag joins.

CUR Representation of Nested Categories

Each category is an independent column. Nesting creates no parent-child encoding in the CUR schema — each column is a flat key-value independent of the others. The hierarchical relationship is encoded in the rule logic (Layer 3 rules reference Layer 1 values), but the output is three independent columns. This simplifies downstream Athena queries significantly.


7. Cost Categories in Cost Explorer vs. CUR: Data Surface and Time Delay

Cost Explorer

Cost Categories appear immediately as a Group By and Filter dimension in all Cost Explorer views once the category status is Applied. You can group spend by AITeam or BusinessUnit values, or filter to a single value (e.g., show only BU-Finance spend). The retroactive data loads into Cost Explorer as part of the backfill — historical views reflect the category assignments back to the configured effective date.

Granularity: Cost Explorer supports daily and monthly views for cost categories. Hourly breakdown is not available for category-grouped views.

CUR (Classic, CUR 1.0)

Cost Category columns appear in the report as costCategory/<name>. They are populated for all line items from the effective start date onward (including retroactive periods after backfill). CUR is the data source for Athena queries and CUDOS.

Column naming in Athena after Glue crawler normalization:

  • Spaces in category names become underscores
  • Mixed case is lowercased
  • A category named AI Team becomes column cost_category_ai_team
  • A category named BusinessUnit becomes cost_category_businessunit

CUR 2.0 (AWS Data Exports)

CUR 2.0 uses a consistent column naming schema. Cost Categories appear as cost_category_<name> with the same normalization rules. CUR 2.0 is required for line_item_iam_principal — without CUR 2.0, IAM principal attribution is unavailable.

Time Delay

Event Delay
Category created/edited → Cost Explorer updated Up to 24 hours
Category created/edited → CUR file next delivery Next scheduled delivery (daily)
Historical CUR file regeneration (support case) 24–72 hours after case is resolved
IAM principal session tag → CUR 2.0 column populated 24–48 hours after session creation
Applied status on category Required before using as nested dimension or split source

8. Cost Categories with CUDOS

CUDOS (Cost and Usage Dashboards Operations Solution) is AWS’s open-source QuickSight dashboard framework built on CUR data via Athena views. Cost Categories surface in CUDOS through the CUR columns, but require explicit query and dataset modifications.

How CUDOS Ingests Category Data

CUDOS builds a set of Athena views (summary_view, hourly_view, resource_view) against the raw CUR Athena table. By default, these views do not include cost_category_* columns — they must be added manually to the relevant view definitions.

Adding a category to summary_view:

CREATE OR REPLACE VIEW summary_view AS
SELECT
  bill_billing_period_start_date,
  line_item_usage_account_id,
  line_item_product_code,
  line_item_usage_type,
  sum(line_item_unblended_cost)         AS unblended_cost,
  cost_category_aiteam                  AS ai_team,
  cost_category_businessunit            AS business_unit
FROM
  ${table_name}
WHERE
  line_item_line_item_type NOT IN ('Tax','Credit','Refund','EdpDiscount')
GROUP BY 1,2,3,4,5,6,7;

After modifying the view, refresh the QuickSight dataset and add the new fields as available dimensions in your analyses.

CUDOS Filter Controls for Categories

The Cloud Intelligence Dashboards customization guide provides a pattern for adding category-based filter controls to dashboards:

  1. Add cost_category_<name> to the Athena view
  2. Refresh the QuickSight dataset
  3. Add a filter control bound to the category column
  4. Add the filter to all visuals that should respect it

This gives business users a BU or team filter on standard CUDOS cost visuals without requiring them to understand account IDs or tag structures.

CUDOS and Split Charge

Split charge allocations do not appear in CUDOS because they are not written to CUR. If you need split-adjusted spend in QuickSight, you must implement the split logic in Athena views directly, using the proportional or fixed weights as constants in the SQL.


9. Limitations

No Cost Categories in Budgets API

AWS Budgets supports Cost Category as a filter dimension in the console and the CreateBudget API. However, the DescribeBudgets and budget notification APIs return limited dimension metadata — programmatic pipeline tooling that reads budget state via API will not reliably surface category-filtered budget breach data. Budget alerts by cost category value work in the console but are difficult to wire into automated FinOps workflows through the API.

FOCUS Export: x_CostCategories Extension Column

The FOCUS 1.0 and 1.2 AWS exports include Cost Category data in the column x_CostCategories. This is an AWS-proprietary extension to the FOCUS standard (prefixed x_). It is not part of the FOCUS specification itself and will not be recognized by multi-cloud FOCUS tooling that only consumes the standard columns. If you are building a cloud-agnostic FinOps pipeline on top of FOCUS data, you must explicitly handle x_CostCategories as an AWS-specific field. The column is a JSON object encoding all category key-value pairs for the line item, not individual columns per category as in CUR.

Split Charge Costs Are Not in CUR

As noted in Section 5: split charge reallocation numbers exist only in the console CSV export. They are not written to CUR, Cost Explorer API responses, or any data export format. Any downstream system (CUDOS, custom Athena pipelines, third-party FinOps tools) shows pre-split costs only.

50-Category Limit

You can create a maximum of 50 Cost Categories per AWS management account. For a 50-team, 5-BU organization, this limit is reached with AITeam (1), BusinessUnit (1), AIProduct (1), plus headroom for environment, charge type, and other classification categories. Design the hierarchy to use nesting (3–4 top-level categories that reference each other) rather than parallel flat categories.

No Cross-Account Category Visibility

Cost Categories are defined on the management account and apply to all member accounts in the Organization. Member accounts cannot define their own categories — only management account administrators can create and modify them. Category values are visible to member accounts in their Cost Explorer views, but rule editing is centralized.


10. Practical: Cost Category Rule Design for 50 Teams, 5 BUs, Bedrock via LiteLLM

Architecture Assumptions

  • 1 centralized AWS account (ai-platform) runs LiteLLM proxying to Amazon Bedrock
  • 50 teams access LiteLLM via API keys; LiteLLM maps each API key to a team name and IAM session tag
  • 5 BUs: Finance, Engineering, Operations, Marketing, DataPlatform
  • 10 teams per BU
  • Some Bedrock workloads run in team-owned accounts (direct, not gateway)

LiteLLM IAM Session Tag Configuration

LiteLLM calls sts:AssumeRole per team request, passing session tags:

aws sts assume-role \
  --role-arn arn:aws:iam::AI-PLATFORM-ACCOUNT:role/BedrockExecutionRole \
  --role-session-name litellm-fraud-detection-session \
  --tags Key=team,Value=fraud-detection Key=bu,Value=Finance Key=costcenter,Value=CC-7701

These tags appear in CUR 2.0 as:

resource_tags/iamPrincipal/team       = fraud-detection
resource_tags/iamPrincipal/bu         = Finance
resource_tags/iamPrincipal/costcenter = CC-7701

Session credentials are cached per team for up to 1 hour. STS rate limit (500 calls/second default) is not a concern for 50 teams sharing a gateway.

Category 1: AITeam (Inherited Value)

{
  "Name": "AITeam",
  "Rules": [
    {
      "Type": "INHERITED_VALUE",
      "InheritedValue": {
        "DimensionName": "TAG",
        "DimensionKey": "iamPrincipal/team"
      }
    }
  ],
  "DefaultValue": "platform-unattributed"
}

This generates one category value per team automatically. When a new team onboards to LiteLLM and starts making calls, their value appears within 48 hours. No rule edit required.

Category 2: BusinessUnit (Regular Rules Referencing AITeam)

{
  "Name": "BusinessUnit",
  "Rules": [
    {
      "Value": "BU-Finance",
      "Rule": {
        "CostCategories": {
          "Key": "AITeam",
          "Values": [
            "fraud-detection", "risk-modeling", "compliance-automation",
            "credit-scoring", "sanctions-screening",
            "treasury-ops", "fx-analytics", "audit-assist",
            "regulatory-reporting", "capital-planning"
          ],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "BU-Engineering",
      "Rule": {
        "CostCategories": {
          "Key": "AITeam",
          "Values": [
            "platform-core", "developer-tools", "ci-cd-assist",
            "code-review", "incident-response",
            "infrastructure-ops", "security-eng", "observability",
            "data-engineering", "api-gateway"
          ],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "BU-Operations",
      "Rule": {
        "CostCategories": {
          "Key": "AITeam",
          "Values": [
            "customer-support", "document-processing", "field-ops",
            "supply-chain", "procurement-assist",
            "logistics", "quality-assurance", "contract-review",
            "hr-assist", "facilities"
          ],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "BU-Marketing",
      "Rule": {
        "CostCategories": {
          "Key": "AITeam",
          "Values": [
            "content-generation", "seo-optimization", "campaign-analytics",
            "personalization", "brand-monitoring",
            "social-media", "ad-copy", "market-research",
            "lead-scoring", "email-automation"
          ],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "BU-DataPlatform",
      "Rule": {
        "CostCategories": {
          "Key": "AITeam",
          "Values": [
            "data-science", "ml-engineering", "feature-store",
            "model-training", "experimentation",
            "data-labeling", "evaluation", "knowledge-base",
            "rag-platform", "embedding-service"
          ],
          "MatchOptions": ["EQUALS"]
        }
      }
    }
  ],
  "DefaultValue": "BU-Unassigned"
}

Note on Effective Start: Set both categories to an effective start 12 months back. BusinessUnit depends on AITeam, so AITeam must reach Applied status before BusinessUnit is created.

Category 3: AIEnvironment (Service + Tag)

{
  "Name": "AIEnvironment",
  "Rules": [
    {
      "Value": "production",
      "Rule": {
        "Tags": {
          "Key": "Environment",
          "Values": ["prod", "production"],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "staging",
      "Rule": {
        "Tags": {
          "Key": "Environment",
          "Values": ["staging", "stage", "uat"],
          "MatchOptions": ["EQUALS"]
        }
      }
    },
    {
      "Value": "development",
      "Rule": {
        "Tags": {
          "Key": "Environment",
          "Values": ["dev", "development", "sandbox"],
          "MatchOptions": ["EQUALS"]
        }
      }
    }
  ],
  "DefaultValue": "unknown"
}

Split Charge Rule for Unattributed Platform Costs

For Bedrock spend that lands in platform-unattributed (calls made before session tagging was implemented, model evaluation jobs, shared Knowledge Base queries), define a proportional split against BusinessUnit:

In the console: source = platform-unattributed, targets = all 5 BU values, method = Proportional.

This redistributes unattributed spend proportionally to the BUs based on their already-categorized spend in the same period. The allocation shows in the Cost Categories details CSV but not in CUR or Cost Explorer.


11. Athena Queries: Joining Cost Category Data to Bedrock Line Items

Cost Category columns are native CUR columns after a Glue crawler run — no join is required. They are present directly on each line item row.

Baseline Bedrock Spend by Team and BU

SELECT
    bill_billing_period_start_date,
    cost_category_aiteam         AS team,
    cost_category_businessunit   AS business_unit,
    line_item_usage_type,
    SUM(line_item_unblended_cost) AS total_usd,
    SUM(
      CASE
        WHEN line_item_usage_type LIKE '%InputTokens%'  THEN line_item_usage_amount
        ELSE 0
      END
    ) AS input_tokens,
    SUM(
      CASE
        WHEN line_item_usage_type LIKE '%OutputTokens%' THEN line_item_usage_amount
        ELSE 0
      END
    ) AS output_tokens
FROM
    "athenacurcfn"."cur"
WHERE
    line_item_product_code = 'AmazonBedrock'
    AND line_item_line_item_type = 'Usage'
    AND bill_billing_period_start_date >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '3' MONTH)
GROUP BY 1,2,3,4
ORDER BY total_usd DESC;

Month-Over-Month Bedrock Spend by BU

WITH monthly AS (
  SELECT
      DATE_TRUNC('month', line_item_usage_start_date) AS month,
      cost_category_businessunit                        AS business_unit,
      SUM(line_item_unblended_cost)                     AS total_usd
  FROM
      "athenacurcfn"."cur"
  WHERE
      line_item_product_code = 'AmazonBedrock'
      AND line_item_line_item_type = 'Usage'
      AND line_item_usage_start_date >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '6' MONTH)
  GROUP BY 1,2
),
with_lag AS (
  SELECT
      month,
      business_unit,
      total_usd,
      LAG(total_usd) OVER (PARTITION BY business_unit ORDER BY month) AS prior_month_usd
  FROM monthly
)
SELECT
    month,
    business_unit,
    total_usd,
    prior_month_usd,
    ROUND(
      (total_usd - prior_month_usd) / NULLIF(prior_month_usd, 0) * 100,
      2
    ) AS mom_pct_change
FROM with_lag
ORDER BY business_unit, month;

Team Spend by IAM Principal and Model

SELECT
    cost_category_aiteam                    AS team,
    line_item_iam_principal,
    SPLIT_PART(line_item_usage_type, ':', 2) AS model_and_direction,
    SUM(line_item_unblended_cost)            AS total_usd,
    SUM(line_item_usage_amount)              AS total_units
FROM
    "athenacurcfn"."cur"
WHERE
    line_item_product_code = 'AmazonBedrock'
    AND line_item_line_item_type = 'Usage'
    AND line_item_iam_principal IS NOT NULL
    AND bill_billing_period_start_date = DATE_TRUNC('month', CURRENT_DATE)
GROUP BY 1,2,3
ORDER BY total_usd DESC
LIMIT 100;

Unattributed Bedrock Spend (Rows With No Category Assignment)

SELECT
    line_item_usage_account_id,
    line_item_iam_principal,
    line_item_usage_type,
    SUM(line_item_unblended_cost) AS total_usd
FROM
    "athenacurcfn"."cur"
WHERE
    line_item_product_code = 'AmazonBedrock'
    AND line_item_line_item_type = 'Usage'
    AND (cost_category_aiteam IS NULL OR cost_category_aiteam = 'platform-unattributed')
    AND bill_billing_period_start_date >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1' MONTH)
GROUP BY 1,2,3
ORDER BY total_usd DESC;

Use this query to identify attribution gaps — accounts or IAM principals not yet covered by category rules — and close them before the next billing cycle.

Multi-Category Cross-Tab: Bedrock Spend by BU, Environment, and Model Family

SELECT
    cost_category_businessunit  AS business_unit,
    cost_category_aienvironment AS environment,
    CASE
      WHEN line_item_usage_type LIKE '%Claude3%'   THEN 'Claude 3'
      WHEN line_item_usage_type LIKE '%Sonnet%'    THEN 'Claude Sonnet'
      WHEN line_item_usage_type LIKE '%Haiku%'     THEN 'Claude Haiku'
      WHEN line_item_usage_type LIKE '%Titan%'     THEN 'Amazon Titan'
      WHEN line_item_usage_type LIKE '%Llama%'     THEN 'Meta Llama'
      WHEN line_item_usage_type LIKE '%Mistral%'   THEN 'Mistral'
      ELSE 'Other'
    END AS model_family,
    SUM(line_item_unblended_cost) AS total_usd
FROM
    "athenacurcfn"."cur"
WHERE
    line_item_product_code = 'AmazonBedrock'
    AND line_item_line_item_type = 'Usage'
    AND bill_billing_period_start_date >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '3' MONTH)
GROUP BY 1,2,3
ORDER BY total_usd DESC;

Key Reference Signals

  • Retroactive window: 12 months, triggered at create/edit time by setting Effective Date
  • CUR 2.0 required for line_item_iam_principal; CUR 1.0 has no IAM principal column
  • CUR column name pattern: cost_category_<lowercase_underscore_name>
  • FOCUS 1.x: categories in x_CostCategories JSON column (non-standard extension)
  • Split charge results: console/CSV only, not in CUR, Cost Explorer API, or CUDOS
  • Category processing time: up to 24 hours after create/edit; CUR retroactive regeneration requires support case
  • LiteLLM gateway: use per-team sts:AssumeRole with session tags; cache credentials per team for up to 1 hour
  • Nesting limit: use Cost Category as dimension in another category’s rules; parent must be Applied first
  • Category limit: 50 per management account
  • Session tag to CUR 2.0 propagation delay: 24–48 hours