Bedrock economics, wired up
Chapter 11 said inference profiles are routing and quotas are physics; this chapter makes them concrete, ARNs you create, model units you commit to, service-quota names you request increases against, and a per-tenant cost-attribution setup you can deploy. This is the SME layer under the model plane's economics: not "caching saves money" but which ARN you pass, which tag carries the cost, and which quota throttles first. Every follow-along here is labeled illustrative because this box has no AWS account, but the shapes are the shapes.
Inference profiles: two kinds, two ARNs
An inference profile is the thing you
actually pass as modelId, and there are two species that solve two
different problems.
System-defined (cross-region) profiles solve capacity. AWS publishes them per model with a region-prefix id, and passing one routes each request across a pool of regions so a single region's bad day is not yours:
us.anthropic.claude-opus-4-8 # routes across US regions
eu.anthropic.claude-opus-4-8 # across EU regions (a residency choice)
apac.anthropic.claude-opus-4-8 # across APAC regions
global.anthropic.claude-opus-4-8 # the largest pool, all regions
You do not create these; you reference them, and for the newest Claude
models a bare regional id often is not offered at all, so the profile is
the only door. The ARN form is
arn:aws:bedrock:us-east-1:ACCOUNT:inference-profile/us.anthropic.claude-opus-4-8.
Application inference profiles solve cost attribution. You create one, wrapping a system-defined profile or a foundation model, and tag it, and every invocation through it is attributable to those tags in Cost Explorer:
# illustrative (boto3 bedrock control plane)
bedrock.create_inference_profile(
inferenceProfileName="tenant-acme-opus",
modelSource={"copyFrom":
"arn:aws:bedrock:us-east-1::inference-profile/us.anthropic.claude-opus-4-8"},
tags=[{"key": "tenant", "value": "acme"},
{"key": "team", "value": "search"}],
)
# -> returns an application-inference-profile ARN you pass as modelId
The resulting ARN is arn:aws:bedrock:us-east-1:ACCOUNT:application-inference-profile/ID,
and that is what your per-tenant worker passes
as modelId. This is the wire-level answer to the multi-tenancy
chapter's "who spent this": create one application profile per tenant,
tag it, and the showback breakdown falls out
of Cost Explorer keyed by the tenant tag, no custom metering required.
The both-ARNs IAM gotcha. An application inference profile that routes across regions needs
bedrock:InvokeModelpermission on two resources: the profile ARN and the underlying foundation-model ARNs in every region it can route to. Grant only the profile and you get anAccessDeniedExceptionthat names a region you never explicitly called, which is baffling until you know the routing is doing it. The IAM policy must listarn:aws:bedrock:*::foundation-model/anthropic.claude-opus-4-8alongside the profile. This one trips up nearly every first cross-region deployment.
A per-tenant cost-attribution scenario
Concretely, for Hive's multi-tenant platform: at
onboarding, a setup step creates an application inference profile named
tenant-<id>-<tier>, tagged with the tenant id and their plan, and
stores the returned ARN in the tenant's config. Every worker for that
tenant loads the ARN and passes it as modelId. Now three things are
true at once: Cost Explorer shows spend per tenant tag (finance is
happy), the budget ledger cross-checks its
own token accounting against the AWS bill per profile (drift is
detectable), and a tenant's model access can be revoked by archiving one
profile (offboarding is one call). The application profile is where the
book's tenant-isolation and cost-attribution chapters become one AWS
resource.
Provisioned throughput: buying a floor
On-demand pricing is per-token with shared quota; provisioned throughput buys dedicated capacity by the hour, and the unit is a model unit (MU), each delivering a published per-model throughput of tokens per minute. You create it, optionally with a commitment for a discount:
# illustrative
resp = bedrock.create_provisioned_model_throughput(
modelUnits=2,
provisionedModelName="checkout-agent-floor",
modelId="arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-opus-4-8",
commitmentDuration="OneMonth", # or SixMonths, or omit for no-commit
)
provisioned_arn = resp["provisionedModelArn"] # pass this as modelId
The economics: no-commitment provisioned throughput is billable by the hour and cancelable, so it is a way to guarantee a floor of capacity for a latency-sensitive path without the shared-quota lottery; committed (one or six month) trades flexibility for a lower rate. The decision rule from Chapter 14, sharpened: a steady predictable base of interactive traffic worth an SLA gets a provisioned floor; the spiky overnight fleet stays on-demand, because paying for idle committed MUs overnight is exactly the waste provisioning is supposed to prevent. A common production shape is both: a small provisioned floor under the interactive product, the fleet on-demand and governed.
Quotas and throttling, by name
The governor shapes demand against quotas; here
are the quotas it shapes against. Bedrock's on-demand limits are Service
Quotas with per-model names of the form "requests per minute for
Anthropic Claude Opus 4.8" and "tokens per minute for Anthropic Claude
Opus 4.8", set per account per region. Exceed either and the API returns
a ThrottlingException (HTTP 429), which is the exact signal the
token-bucket lab had the fleet avoid producing.
Two SME facts:
- Cross-region inference raises effective throughput. Because a system-defined profile spreads load across regions, its aggregate ceiling is higher than any single region's quota, which is a capacity lever and the reason your quota math must be done against the profile's behavior, not one region's number.
- Increases are per-model, per-region, via Service Quotas. A quota increase request names the specific model and region; there is no global "more Bedrock" knob. Plan the request against the fleet-sizing arithmetic: you know the tokens per minute your fleet needs before you ask.
Observability of the spend
The last wire-level piece: how spend and errors reach a dashboard. Model invocation logging, enabled once per account, delivers every request and response to CloudWatch Logs and/or S3:
# illustrative
bedrock.put_model_invocation_logging_configuration(loggingConfig={
"cloudWatchConfig": {"logGroupName": "/bedrock/invocations",
"roleArn": LOG_ROLE},
"s3Config": {"bucketName": "hive-bedrock-logs"},
"textDataDeliveryEnabled": True,
})
Each log record carries the model id, region, the input and output
bodies, token counts, and the inference region actually used (which for
a cross-region profile tells you where a request landed, invaluable
when a residency question or a regional slowdown appears). Alongside the
logs, the AWS/Bedrock CloudWatch metrics namespace publishes
InputTokenCount, OutputTokenCount, Invocations,
InvocationLatency, InvocationClientErrors, and InvocationThrottles,
and the last one is the metric your on-call quota-storm
alarm watches: a rising InvocationThrottles is the fleet brushing the
quota, exactly the page the governor exists to
prevent.
Don't be confused: application inference profile vs provisioned throughput. Both create an ARN you pass as
modelId, and both are "a thing you set up in advance", but they do opposite jobs. An application inference profile changes nothing about capacity or price; it is a tagging wrapper for cost attribution over on-demand (or cross-region) inference. Provisioned throughput changes the capacity and billing model: dedicated MUs billed by the hour. You can even combine them conceptually, attribute cost with a profile, guarantee capacity with provisioning, because they operate on different axes. Confusing them leads to teams "provisioning" when they only wanted per-tenant cost reports, and paying by the hour for a tagging feature.
👉 Next: Guardrails, batch, and Knowledge Bases, the three Bedrock surfaces an agent platform reaches for most after the model itself, with their real config shapes and a scenario each.