← Agent Frameworks 🕐 12 min read
Agent Frameworks

AWS Cost Explorer Advanced Patterns for Bedrock AI Workloads (2026)

AWS Cost Explorer is the primary GUI and API surface for operational Bedrock cost management.

AWS Cost Explorer is the primary GUI and API surface for operational Bedrock cost management. It covers 13 months of history plus 18-month forecasting, supports programmatic access via get_cost_and_usage, and integrates with AWS Cost Anomaly Detection. This note covers the filter combinations, API patterns, and operational limits that matter specifically for Bedrock workloads — including the gaps where Cost Explorer falls short and CUR + Athena is the correct tool.


1. Cost Explorer Filter Combinations for Bedrock

Service dimension

The most reliable starting filter. Bedrock charges flow under the service name Amazon Bedrock. In the GUI: Filters → Service → Amazon Bedrock. In the API:

Filter={
    "Dimensions": {
        "Key": "SERVICE",
        "Values": ["Amazon Bedrock"],
        "MatchOptions": ["EQUALS"]
    }
}

Product code

Bedrock’s product code in CUR line items is AmazonBedrock. In Cost Explorer the SERVICE dimension is functionally equivalent for most queries — the product code dimension is more useful in raw CUR/Athena queries than in Cost Explorer API calls, where SERVICE is the canonical key.

Usage type dimension

Usage types for Bedrock follow the pattern <region-prefix>-<usage>. Examples:

  • USE1-InputTokens-Claude — Claude input tokens, us-east-1
  • USE1-OutputTokens-Claude — Claude output tokens, us-east-1
  • USE1-CacheWriteInputTokens-Claude — prompt cache write tokens
  • USE1-CacheReadInputTokens-Claude — prompt cache read tokens
  • USE1-InputTokens-Llama — Llama input tokens

The region prefix is two to four characters (USE1, USW2, EUW1, APN1, etc.). Usage types are enumerable via GetDimensionValues with Dimension: USAGE_TYPE.

Linked account filter

In an AWS Organization, isolate a specific account’s Bedrock spend:

Filter={
    "And": [
        {
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        {
            "Dimensions": {
                "Key": "LINKED_ACCOUNT",
                "Values": ["123456789012"],
                "MatchOptions": ["EQUALS"]
            }
        }
    ]
}

For multi-account reporting, omit the LINKED_ACCOUNT filter and add it as a GroupBy dimension instead (see section 2).

Compound filter pattern — Bedrock by account and token type

import boto3
from datetime import date

ce = boto3.client("ce", region_name="us-east-1")

response = ce.get_cost_and_usage(
    TimePeriod={
        "Start": "2026-06-01",
        "End":   "2026-06-18"
    },
    Granularity="DAILY",
    Filter={
        "And": [
            {
                "Dimensions": {
                    "Key": "SERVICE",
                    "Values": ["Amazon Bedrock"],
                    "MatchOptions": ["EQUALS"]
                }
            },
            {
                "Dimensions": {
                    "Key": "USAGE_TYPE",
                    "Values": ["USE1-InputTokens", "USE1-OutputTokens"],
                    "MatchOptions": ["STARTS_WITH"]
                }
            }
        ]
    },
    GroupBy=[
        {"Type": "DIMENSION", "Key": "USAGE_TYPE"},
        {"Type": "DIMENSION", "Key": "LINKED_ACCOUNT"}
    ],
    Metrics=["BlendedCost", "UsageQuantity"]
)

2. Grouping Dimensions for Bedrock in Cost Explorer

Dimensions that surface actionable signal

GroupBy key What it shows Practical use
SERVICE Bedrock vs SageMaker vs Marketplace Cross-product rollup; baseline
USAGE_TYPE Model + token type + region Per-model cost breakdown
LINKED_ACCOUNT Per-team or per-env account Chargeback / showback
TAG Custom cost allocation tags Application, team, project
OPERATION InvokeModel, Converse, InvokeModelWithResponseStream API surface breakdown
REGION us-east-1, eu-west-1, etc. Cross-region inference overhead

Most useful two-dimension grouping

USAGE_TYPE + LINKED_ACCOUNT gives the most actionable dashboard view for a multi-account organization: you see per-model spend per account without any tag setup overhead.

TAG grouping unlocks organizational taxonomy (department, product, environment) but requires:

  1. Application inference profiles configured per workload
  2. Cost allocation tags activated in the Billing console
  3. Up to 24-hour propagation lag before tags appear in Cost Explorer

OPERATION dimension for Bedrock

InvokeModel and Converse are the primary operation values. InvokeModelWithResponseStream is present in CUR but may not be available as a distinct grouping key in all Cost Explorer API versions — validate with GetDimensionValues before building reports that depend on it.


3. get_cost_and_usage API — Bedrock Filter Reference

Full programmatic example: daily spend by model and account

import boto3
import json
from datetime import date, timedelta

def bedrock_daily_cost_by_model_account(start: str, end: str) -> dict:
    """
    Return daily Bedrock costs grouped by usage type (model + token type)
    and linked account. Covers input, output, and cache tokens.
    """
    ce = boto3.client("ce", region_name="us-east-1")

    response = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="DAILY",
        Filter={
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        GroupBy=[
            {"Type": "DIMENSION", "Key": "USAGE_TYPE"},
            {"Type": "DIMENSION", "Key": "LINKED_ACCOUNT"}
        ],
        Metrics=["UnblendedCost", "UsageQuantity"]
    )
    return response["ResultsByTime"]


def bedrock_cost_by_tag(tag_key: str, start: str, end: str) -> dict:
    """
    Return monthly Bedrock costs grouped by a cost allocation tag.
    Requires tags activated in Billing console.
    tag_key example: "project" or "team"
    """
    ce = boto3.client("ce", region_name="us-east-1")

    response = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="MONTHLY",
        Filter={
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        GroupBy=[
            {"Type": "TAG", "Key": tag_key}
        ],
        Metrics=["UnblendedCost"]
    )
    return response["ResultsByTime"]

Valid Metrics values for Bedrock

  • UnblendedCost — what the account is actually charged; use this for most Bedrock reporting
  • BlendedCost — amortized across consolidated billing; less meaningful for Bedrock since no RI/SP applies
  • UsageQuantity — token counts; pair with USAGE_TYPE grouping to get per-model token volumes
  • NormalizedUsageAmount — not meaningful for Bedrock (relevant only for EC2 normalization)

Pagination

get_cost_and_usage returns a NextPageToken when results exceed one page. Handle it:

def paginated_cost_query(params: dict) -> list:
    ce = boto3.client("ce", region_name="us-east-1")
    results = []
    while True:
        response = ce.get_cost_and_usage(**params)
        results.extend(response["ResultsByTime"])
        token = response.get("NextPageToken")
        if not token:
            break
        params["NextPageToken"] = token
    return results

4. Saved Reports and Scheduled Exports

Cost Explorer saved reports

Cost Explorer allows saving report configurations (filters, groupings, date range, chart type) as named reports accessible in the console. There is no public API to programmatically create or enumerate saved Cost Explorer reports — they are console-only artifacts.

Scheduled dashboard email delivery (GA April 2026)

AWS Billing and Cost Management dashboards now support scheduled email delivery. Configuration:

  • Daily, weekly, or monthly cadence
  • Recipients receive a secure link to a password-protected PDF
  • The PDF is point-in-time and does not contain raw data — it is a rendered screenshot of the dashboard
  • For Bedrock reporting, configure a dashboard with SERVICE filter set to Amazon Bedrock before scheduling

This is distinct from CUR data exports, which are file-based and machine-readable.

Cost and Usage Report (CUR) scheduled exports to S3

For automated ingestion, CUR exports are the correct path:

  • Configure via AWS Data Exports console or aws cur put-report-definition
  • Exports land in S3 as Parquet or CSV, partitioned by billing period
  • Bedrock line items appear with product_product_name = "Amazon Bedrock" and line_item_usage_type carrying the model+token type string
  • Export frequency: daily refresh with ~24-hour lag

Lambda-based automated reporting

A common pattern for Bedrock cost dashboards:

import boto3
import json
from datetime import date, timedelta

def lambda_handler(event, context):
    ce = boto3.client("ce")
    end = date.today().isoformat()
    start = (date.today() - timedelta(days=30)).isoformat()

    data = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="DAILY",
        Filter={
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        GroupBy=[{"Type": "DIMENSION", "Key": "USAGE_TYPE"}],
        Metrics=["UnblendedCost", "UsageQuantity"]
    )

    # Write to S3 for downstream consumption
    s3 = boto3.client("s3")
    s3.put_object(
        Bucket="your-finops-bucket",
        Key=f"bedrock-costs/{end}.json",
        Body=json.dumps(data["ResultsByTime"])
    )
    return {"statusCode": 200, "report_date": end}

Schedule via EventBridge rule (rate(1 day)).


5. Forecasting Accuracy for Bedrock Workloads

Why Bedrock forecasting is structurally unreliable

Cost Explorer’s forecasting engine is calibrated for steady-state infrastructure spend — the canonical use case is EC2 reserved capacity with predictable utilization curves. Bedrock workloads have fundamentally different characteristics:

EC2 (forecasting works well):

  • Hourly billing on persistent instances
  • Usage is bounded by instance count × hours
  • Seasonal patterns are learnable (weekday/weekend, business hours)
  • Mean reversion is strong — a spike from a deployment typically returns to baseline

Bedrock (forecasting degrades quickly):

  • Token-based billing: cost is a function of prompt length × model × call volume
  • Prompt length is opaque to Cost Explorer — it sees aggregate token counts, not per-request structure
  • Agent workloads compound unpredictably: multi-step reasoning chains can amplify token counts 5–20x vs a simple query
  • Model mix changes (switching Claude 3.5 Haiku to Claude 3.5 Sonnet) cause step-function cost increases that break the model’s trend line
  • Cross-region inference adds routing overhead that is not predictable from historical patterns

Practical consequence: Cost Explorer forecasts for Bedrock typically have errors of ±30–60% over a 30-day horizon when workloads are in active development. The forecast becomes more reliable (±10–20%) only after 90+ days of stable, unchanging workload composition.

Workaround: driver-based forecasting

Rather than relying on Cost Explorer’s statistical forecast, derive a forward estimate from application metrics:

  1. Establish a cost-per-inference metric from CloudWatch (invocation count × average tokens × model rate)
  2. Apply business-level growth assumptions (requests/day projection) to that unit cost
  3. Use Cost Explorer history to validate the unit cost, not to project the curve

6. Cost Explorer Anomaly Detection vs AWS Cost Anomaly Detection

These are not two separate products — AWS Cost Anomaly Detection is a feature of the Cost Management suite that sits alongside Cost Explorer. The distinction is architectural, not organizational.

Cost Explorer built-in view

The Cost Explorer console includes an anomaly flag on individual service spend lines when a data point is statistically outside the historical range. This is a read-only visualization layer — there is no alerting, no root-cause analysis, and no subscription model.

AWS Cost Anomaly Detection (standalone feature)

Dedicated ML-based anomaly detection with:

  • Monitors: define the scope (service, linked account, cost category, or tag)
  • Alert subscriptions: SNS topic or email, with configurable thresholds (absolute $ or percentage)
  • Root-cause analysis: identifies which service, usage type, or account drove the anomaly
  • Evaluation cadence: runs approximately 3× per day after billing data refreshes

For Bedrock, the recommended setup is a Service-level monitor scoped to Amazon Bedrock:

import boto3

ce = boto3.client("ce", region_name="us-east-1")

monitor = ce.create_anomaly_monitor(
    AnomalyMonitor={
        "MonitorName": "BedrockAnomalyMonitor",
        "MonitorType": "DIMENSIONAL",
        "MonitorDimension": "SERVICE"
    }
)

subscription = ce.create_anomaly_subscription(
    AnomalySubscription={
        "MonitorArnList": [monitor["MonitorArn"]],
        "SubscriptionName": "BedrockDailyAlert",
        "Threshold": 20.0,  # Alert if anomaly exceeds $20
        "Frequency": "DAILY",
        "Subscribers": [
            {"Address": "finops-team@example.com", "Type": "EMAIL"}
        ]
    }
)

Key operational difference

Cost Explorer anomaly visualization is retrospective and passive. AWS Cost Anomaly Detection with SNS is the alert path — it is what pages your team when a runaway agent runs 200k inference calls overnight. Common Bedrock-specific anomaly triggers: new developer accessing a premium model without rate limits, an agent retry loop hitting an unhandled exception and re-invoking thousands of times, or a prompt cache miss pattern after a model version upgrade.


7. Usage Type Groups for Bedrock in Cost Explorer

Usage type groups are Cost Explorer’s mechanism for aggregating the region-prefixed usage type strings into logical categories. They prevent the proliferation of USE1-InputTokens-Claude, USW2-InputTokens-Claude, EUW1-InputTokens-Claude etc. into separate rows when you want a cross-region total.

Native usage type groups for Bedrock

Cost Explorer does not ship with pre-built usage type groups for Bedrock the way it does for EC2 (where “EC2: Running Hours” aggregates across instance families). Bedrock usage type groups must be constructed by the operator.

The practical approach is to use USAGE_TYPE with MatchOptions: STARTS_WITH filtering:

# Aggregate all input tokens across all models and regions
input_token_filter = {
    "And": [
        {
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        {
            "Dimensions": {
                "Key": "USAGE_TYPE",
                "Values": ["InputTokens"],
                "MatchOptions": ["CONTAINS"]
            }
        }
    ]
}

# Aggregate all Claude-family spend across regions
claude_filter = {
    "And": [
        {
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        {
            "Dimensions": {
                "Key": "USAGE_TYPE",
                "Values": ["Claude"],
                "MatchOptions": ["CONTAINS"]
            }
        }
    ]
}

Cost Categories as a substitute for usage type groups

AWS Cost Categories allow creating rule-based logical groups that appear as a first-class dimension in Cost Explorer:

Rule: IF USAGE_TYPE CONTAINS "InputTokens" THEN category = "Bedrock-Input"
Rule: IF USAGE_TYPE CONTAINS "OutputTokens" THEN category = "Bedrock-Output"
Rule: IF USAGE_TYPE CONTAINS "CacheReadInputTokens" THEN category = "Bedrock-Cache-Read"
Rule: IF USAGE_TYPE CONTAINS "CacheWriteInputTokens" THEN category = "Bedrock-Cache-Write"

Cost Categories are configured in the Billing console and take up to 24 hours to backfill. Once active, use GroupBy: COST_CATEGORY in API calls.


8. Filtering Across Product Codes: Bedrock + Marketplace + SageMaker

Where Bedrock charges appear outside the Amazon Bedrock service line

Two cases cause Bedrock-adjacent spend to appear under different service lines:

Case 1: Bedrock model evaluation Model evaluation jobs in Bedrock are billed under Amazon SageMaker line items, not under Amazon Bedrock. This is a known billing quirk. When reconciling total AI spend, run a parallel query for SageMaker filtered to evaluation usage types.

Case 2: Third-party models via AWS Marketplace If your organization subscribes to models distributed through AWS Marketplace (some Llama variants, specialized fine-tuned models from ISVs), those charges appear under the AWS Marketplace service line, not Amazon Bedrock. The product code is aws-marketplace and the usage type strings vary by vendor.

Multi-product filter for total AI workload cost

def total_ai_workload_cost(start: str, end: str) -> dict:
    """
    Captures native Bedrock + Marketplace model subscriptions +
    SageMaker model evaluation that is billed separately.
    """
    ce = boto3.client("ce", region_name="us-east-1")

    response = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="MONTHLY",
        Filter={
            "Or": [
                {
                    "Dimensions": {
                        "Key": "SERVICE",
                        "Values": ["Amazon Bedrock"],
                        "MatchOptions": ["EQUALS"]
                    }
                },
                {
                    # Marketplace model subscriptions
                    "Dimensions": {
                        "Key": "SERVICE",
                        "Values": ["AWS Marketplace"],
                        "MatchOptions": ["EQUALS"]
                    }
                },
                {
                    # Bedrock model evaluation jobs
                    "And": [
                        {
                            "Dimensions": {
                                "Key": "SERVICE",
                                "Values": ["Amazon SageMaker"],
                                "MatchOptions": ["EQUALS"]
                            }
                        },
                        {
                            "Dimensions": {
                                "Key": "USAGE_TYPE",
                                "Values": ["ModelEvaluation"],
                                "MatchOptions": ["CONTAINS"]
                            }
                        }
                    ]
                }
            ]
        },
        GroupBy=[{"Type": "DIMENSION", "Key": "SERVICE"}],
        Metrics=["UnblendedCost"]
    )
    return response["ResultsByTime"]

SageMaker inference endpoints vs Bedrock

SageMaker real-time inference endpoints (dedicated endpoints for custom models) are a separate architectural choice from Bedrock on-demand inference. Their costs appear under Amazon SageMaker with usage types like ml.g5.xlarge-Hrs. These are not Bedrock charges and should be tracked in a separate cost category.


9. Granularity Options for Bedrock in Cost Explorer

Available granularities

Granularity Available for Bedrock Notes
MONTHLY Yes Default; always available via API and GUI
DAILY Yes Available with no additional configuration
HOURLY Partial See below

Hourly granularity — the Bedrock caveat

Cost Explorer supports hourly granularity, but Bedrock cost data is currently available at daily granularity at finest grain in Cost Explorer. The CUR 2.0 documentation confirms that native Bedrock attribution methods deliver aggregated billed dollars “with the finest grain being per usage type per day.”

Hourly granularity in Cost Explorer is primarily designed for EC2 resource-level data. Enabling it for non-EC2 services like Bedrock typically returns the daily total distributed across hours with uniform distribution — not actual per-hour measurement.

Cost of enabling hourly granularity: $0.00000033 per usage record, billed daily for the trailing 14 days of hourly records hosted in Cost Explorer.

Practical recommendation

For Bedrock operational dashboards, use DAILY granularity. For capacity planning and trend analysis, use MONTHLY. For sub-daily monitoring of Bedrock invocations and token volumes, use CloudWatch metrics (AWS/Bedrock namespace: Invocations, InputTokenCount, OutputTokenCount) which are available at 1-minute resolution.


10. RI/SP Coverage Reports: Bedrock Does Not Appear

Why Bedrock is absent from RI and Savings Plan coverage reports

RI (Reserved Instance) and Savings Plan coverage reports in Cost Explorer track the percentage of your on-demand usage that is covered by a commitment. These reports are scoped to services where commitments are available:

  • RI coverage: EC2, RDS, ElastiCache, OpenSearch, Redshift, DynamoDB
  • Compute Savings Plans coverage: EC2, Fargate, Lambda
  • SageMaker Savings Plans coverage: SageMaker inference endpoints

Amazon Bedrock has no RI or Savings Plan instrument. On-demand Bedrock usage is not eligible for any commitment discount mechanism. If you run a coverage report and filter to Amazon Bedrock, it will either return empty results or show 0% coverage with 100% on-demand — which is technically correct but misleading.

What does show coverage for AI workloads

  • SageMaker Savings Plans: Apply to SageMaker inference endpoints (your own model deployments), not to Bedrock on-demand calls
  • Bedrock Provisioned Throughput: Bedrock’s own commitment mechanism — purchase a model unit (MU) for a defined throughput level at a fixed hourly rate. This is tracked in CUR as ProvisionedThroughput usage type lines, distinct from on-demand token lines. It does not appear in Cost Explorer’s Savings Plan coverage reports; it appears as a usage cost under Amazon Bedrock.

Provisioned Throughput vs on-demand in Cost Explorer

# Separate provisioned throughput from on-demand token costs
provisioned_filter = {
    "And": [
        {
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        {
            "Dimensions": {
                "Key": "USAGE_TYPE",
                "Values": ["ProvisionedThroughput"],
                "MatchOptions": ["CONTAINS"]
            }
        }
    ]
}

11. Cost Explorer API Rate Limits and Caching Strategies

Rate limits

The Cost Explorer API enforces per-account rate limits. The primary operations and their documented limits:

Operation Rate limit Notes
GetCostAndUsage ~5 req/sec sustained Exact TPS not published; observed limit
GetCostForecast Lower than GetCostAndUsage Less critical path
GetDimensionValues ~5 req/sec Use for filter validation

Exceeding limits returns LimitExceededException. This is a hard stop — no burst allowance. AWS does not publish exact TPS for Cost Explorer APIs; the working assumption from production incidents is 5 req/sec per account, with backoff required on LimitExceededException.

Per-call pricing

Each paginated API call costs $0.01. A dashboard making 100 queries/day costs $1/day ($30/month). Design queries to be broad and filter in application code rather than issuing many narrow API calls.

Caching strategy for dashboards

Pattern 1: Lambda + S3 cache with TTL

import boto3
import json
from datetime import datetime, timedelta

CACHE_BUCKET = "your-finops-cache"
CACHE_TTL_HOURS = 6

def get_cached_bedrock_costs(start: str, end: str) -> dict:
    s3 = boto3.client("s3")
    cache_key = f"bedrock-cost-cache/{start}-{end}.json"

    try:
        obj = s3.get_object(Bucket=CACHE_BUCKET, Key=cache_key)
        cached = json.loads(obj["Body"].read())
        cached_at = datetime.fromisoformat(cached["cached_at"])
        if datetime.utcnow() - cached_at < timedelta(hours=CACHE_TTL_HOURS):
            return cached["data"]
    except s3.exceptions.NoSuchKey:
        pass

    # Cache miss: fetch from Cost Explorer
    ce = boto3.client("ce", region_name="us-east-1")
    response = ce.get_cost_and_usage(
        TimePeriod={"Start": start, "End": end},
        Granularity="DAILY",
        Filter={
            "Dimensions": {
                "Key": "SERVICE",
                "Values": ["Amazon Bedrock"],
                "MatchOptions": ["EQUALS"]
            }
        },
        GroupBy=[{"Type": "DIMENSION", "Key": "USAGE_TYPE"}],
        Metrics=["UnblendedCost", "UsageQuantity"]
    )

    to_cache = {
        "cached_at": datetime.utcnow().isoformat(),
        "data": response["ResultsByTime"]
    }
    s3.put_object(
        Bucket=CACHE_BUCKET,
        Key=cache_key,
        Body=json.dumps(to_cache)
    )
    return response["ResultsByTime"]

Pattern 2: EventBridge-scheduled refresh

Run a daily Lambda that pre-populates a DynamoDB or S3 cache at a known time (e.g., 06:00 UTC, after Cost Explorer data refreshes). Dashboard queries hit the cache exclusively. Cost Explorer API is called once per day, not on demand.

Design rules for multi-team dashboards:

  1. Never call Cost Explorer directly from a web frontend — always proxy through a cache layer
  2. Batch multiple date range queries into a single wide query and slice in application code
  3. Use MONTHLY granularity for historical trend views (fewer rows = fewer pages = fewer API charges)
  4. Scope historical lookback to 90 days for operational dashboards; retrieve full 13-month history only for quarterly reviews

12. Cost Explorer vs Athena/CUR: Decision Guide for Bedrock Analysis

Use Cost Explorer when

  • You need a quick operational answer: “What did Bedrock cost last week by model?”
  • You want GUI visualization without infrastructure setup
  • You need anomaly alerts (Cost Anomaly Detection requires Cost Explorer to be enabled)
  • You are building a dashboard that queries weekly or daily, not in real time
  • Multi-account rollup in an AWS Organization is the requirement

Use Athena + CUR when

  • You need per-request-level attribution (CUR 2.0 with IAM principal data provides line items per inference call)
  • You need to join Bedrock billing data with application-level data (request IDs, user IDs, tenant IDs)
  • You need sub-day granularity: CUR 2.0 refreshes hourly, giving ~1-hour resolution vs Cost Explorer’s 24-hour lag
  • You need USAGE_TYPE text patterns beyond what Cost Explorer’s filter operators support (e.g., complex regex against model names)
  • You are running analyses that would require >500 Cost Explorer API calls — Athena at ~$5/TB scanned is cheaper for bulk historical queries
  • You need to audit prompt cache efficiency: CacheReadInputTokens / (CacheReadInputTokens + CacheWriteInputTokens) requires arithmetic across line items, which Cost Explorer cannot do

Hybrid pattern (recommended for production)

CUR 2.0 (Parquet, S3) → Athena (ad-hoc analysis, join with app data)
         ↓
Cost Explorer API (operational dashboards, anomaly detection, budget alerts)
         ↓
AWS Budgets (threshold alerts with forecast-based warnings)

Cost Explorer is the operational surface. CUR/Athena is the analytical surface. They complement each other — Cost Explorer cannot do joins or arithmetic; Athena cannot send anomaly alerts.

CUR 2.0 Bedrock columns of interest

Column Description
line_item_product_code AmazonBedrock
line_item_usage_type e.g., USE1-InputTokens-Claude
line_item_operation InvokeModel, Converse
line_item_usage_amount Token count for the period
line_item_unblended_cost Dollar cost
product_model_id Model identifier (CUR 2.0 only)
resource_tags_user_* Custom cost allocation tags
split_line_item_parent_resource_id Links to inference profile ARN when AIPs are used

CUR 2.0 with IAM principal data enabled adds: | identity_iam_principal_arn | ARN of the caller (user, role, or assumed role) |

This last column is unavailable in Cost Explorer and is the primary reason to reach for CUR/Athena when you need per-team or per-developer attribution at caller identity granularity.


Reference: Filter Dimension Keys

Valid dimension keys for get_cost_and_usage Filter and GroupBy:

AZ
INSTANCE_TYPE
LEGAL_ENTITY_NAME
INVOICING_ENTITY
LINKED_ACCOUNT
LINKED_ACCOUNT_NAME
OPERATION
PLATFORM
PURCHASE_TYPE
SERVICE
SERVICE_CODE
TENANCY
RECORD_TYPE
USAGE_TYPE
USAGE_TYPE_GROUP
REGION
SCOPE
DATABASE_ENGINE
DEPLOYMENT_OPTION
OPERATING_SYSTEM
SUBSCRIPTION_ID
BILLING_ENTITY
RESERVATION_ID
RESOURCE_ID
RIGHTSIZING_TYPE
SAVINGS_PLAN_ARN
SAVINGS_PLANS_TYPE
PAYMENT_OPTION
AGREEMENT_END_DATE_TIME_AFTER
AGREEMENT_END_DATE_TIME_BEFORE
INVOICING_ENTITY

For Bedrock, the operationally useful subset is: SERVICE, USAGE_TYPE, LINKED_ACCOUNT, OPERATION, REGION. TAG is used via GroupBy with {"Type": "TAG", "Key": "<tag-key>"} syntax, not as a Dimension key.


Sources