Amazon Bedrock
A region, two or three AWS variables, and the pricing rule that turns one model into three different rates depending where you called it.
On this page
Bedrock is the family where the region is not a detail. It selects the endpoint host, it is the scope every request is signed against, and it changes the price: in the committed snapshot the same Claude model bills $0.80 per million input tokens in the United States and $0.25 in Europe. A gateway that treats region as cosmetic will hand you a confident wrong figure.
You need a region, an access key pair, and the Bedrock model id in the form AWS publishes it
(anthropic.claude-3-5-haiku-20241022-v1:0, not claude-3-5-haiku).
The config
upstreams:
bedrock:
provider: bedrock
base_url: https://bedrock-runtime.us-east-1.amazonaws.com
region: us-east-1 # signing scope and price selector
auth:
access_key_id_env: AWS_ACCESS_KEY_ID
secret_access_key_env: AWS_SECRET_ACCESS_KEY
session_token_env: AWS_SESSION_TOKEN # optional; temporary credentials
tiers:
- name: cheap
upstream: bedrock
model: anthropic.claude-3-5-haiku-20241022-v1:0
Every one of those *_env fields holds a variable name. Three variables, none of them in the
file.
region: has its own field rather than living under params:. Writing it as a param is turned away
with params.region: use the dedicated region: field, not params. Leave it out entirely and
validation stops you:
$ tokentriage validate --config tokentriage.yaml
Error: configuration is invalid: upstreams["bedrock"].region: provider "bedrock" requires a region (aws_sigv4 signing scope / region-qualified pricing / a {region} URL template)
The upstream name inside the quotes is yours; the rest of the sentence is the binary’s, and it is worth grepping for. One thing is tidied above: the command prints that message twice, because it returns the error and then prints it again on the way out.
Using a Bedrock API key instead of signing
Set api_key_env alone, with no auth: block, and the key is presented as a bearer token rather
than signing the request. region: is still required, because it still selects the price.
upstreams:
bedrock:
provider: bedrock
base_url: https://bedrock-runtime.us-east-1.amazonaws.com
region: us-east-1
api_key_env: AWS_BEARER_TOKEN_BEDROCKHow the region reaches the price
Two different values come out of the one you wrote. The raw region signs the request, because
SigV4 needs us-east-1. A shorter region class looks up the price, because the catalog keys its
region-qualified rates by continent.
Your region: |
Class used for pricing |
|---|---|
us-* |
us |
eu-* |
eu |
ap-* |
apac |
| anything else | none, and the row is region_unknown |
That last row is the one to plan around. A Bedrock upstream in ca-central-1, sa-east-1 or
me-south-1 signs correctly and gets its answer, and any model whose rates are region-qualified
comes back with no dollar rather than a neighbouring region’s.
The rule that produces a missing number instead of a wrong one
Each region class is a separate key in the price file, and you can read one back without sending a request:
$ tokentriage prices show bedrock/eu.anthropic.claude-3-5-haiku-20241022-v1:0
model: bedrock/eu.anthropic.claude-3-5-haiku-20241022-v1:0
resolved: bedrock/eu.anthropic.claude-3-5-haiku-20241022-v1:0
provider: bedrock
snapshot: litellm@2026-07-14#179affb
source: litellm fetched 2026-07-14 generator pricegen v2
upstream_sha256: 179affb29103fed8b02a83e22e93209c2c65a393cbf9dd785dbaec0f8748d6f7
RATE USD / TOKEN USD / 1M TOKENS
input 2.5e-07 $0.2500
output 1.25e-06 $1.2500
cache_read 2.5e-08 $0.0250
cache_write 3.125e-07 $0.3125
reasoning (fallback = output) $1.2500
Swap eu. for us. and the input rate is $0.80 with output at $4.00. The snapshot id and digest
in that header are the build’s, not your account’s, and the same command on your machine prints the
same figures while you run the same build.
The third class is where it gets interesting. There is no apac. key for this model:
$ tokentriage prices show bedrock/apac.anthropic.claude-3-5-haiku-20241022-v1:0
Error: prices: model "bedrock/apac.anthropic.claude-3-5-haiku-20241022-v1:0" not found in snapshot litellm@2026-07-14#179affb
A region-neutral key for that model does exist, at bedrock/anthropic.claude-3-5-haiku-20241022-v1:0.
It is not used.
Models whose catalog entry is region-neutral throughout are unaffected:
amazon.titan-text-lite-v1 prices at $0.30 per million input in every class, because the file
holds no region-qualified key for it to defeat the neutral one.
Two Bedrock descriptors
bedrock and bedrock_converse speak the identical Converse wire and share the driver. They
differ only in which price bucket they bind, and the buckets do not overlap: bedrock/ carries 267
priced keys and bedrock_converse/ carries 138, and no model key appears in both. Those are counts
of rows in the vendored litellm@2026-07-14#179affb price file rather than claims about what either
descriptor can reach.
Amazon’s own Nova models are the case that catches people. They are keyed under
bedrock_converse/ and not under bedrock/, in all four forms:
$ tokentriage prices show bedrock_converse/us.amazon.nova-lite-v1:0
model: bedrock_converse/us.amazon.nova-lite-v1:0
resolved: bedrock_converse/us.amazon.nova-lite-v1:0
provider: bedrock_converse
snapshot: litellm@2026-07-14#179affb
source: litellm fetched 2026-07-14 generator pricegen v2
upstream_sha256: 179affb29103fed8b02a83e22e93209c2c65a393cbf9dd785dbaec0f8748d6f7
RATE USD / TOKEN USD / 1M TOKENS
input 6e-08 $0.0600
output 2.4e-07 $0.2400
cache_read (fallback = input) $0.0600
cache_write (fallback = input) $0.0600
reasoning (fallback = output) $0.2400
So: use bedrock unless your models resolve under the separate bedrock_converse/ bucket. If a
model comes back model_unknown on one, try the other before concluding it is unsupported. Nova
carries us., eu. and apac. keys as well as a neutral one, so the rule above applies to it in
full.
Reaching Bedrock from an SDK you already have
On the translation axis, two client formats come in and four upstream dialects go out. An
OpenAI-format or an Anthropic-format client reaches a bedrock upstream, streaming and not, and the
same two formats also reach openai, anthropic and gemini upstreams. A Bedrock-format client
reaches only a Bedrock upstream, because bedrock is a translation target rather than an inbound
client format.
That asymmetry is a decision with a reason, and the reason is evidence rather than difficulty: no population of Bedrock-SDK clients pointed at a third-party gateway has been observed, and switching on a direction nobody has verified would leave an unowned surface behind rather than a feature. It behaves like a decision too. A Bedrock-format request is not rejected: the translator returns a named error, and the request is then served byte-faithfully on its own dialect, a Bedrock client reaching a Bedrock upstream, which loses nothing at all.
The full grid is drawn once, on the dialect matrix, rather than redrawn here. What a translated row records about fields that could not cross is on translation loss, and the streaming half of it is on streaming translation.
Prefixes that look missing and are not
Two catalog prefixes in this family sit outside the covered list, and neither one is a driver gap.
bedrock_mantle holds 10 synthetic or future model ids on Bedrock. amazon_nova holds 4 shorthand
ids such as nova-lite-v1, which is not the Bedrock model id you send: that is
amazon.nova-lite-v1:0, and it prices under bedrock_converse/ as shown above.
Both belong to the published gap partition’s alias-or-sub-namespace class, whose own column
heading in the generated table reads “Why it’s not a driver gap”. Binding either would be a
pricing-bucket descriptor, pure data on the same pattern bedrock_converse follows, with no new
wire code. The condition for doing it is an operator who uses that sub-namespace, which is a
condition and not a date or a queue position. The
coverage page carries the whole partition.
What is verified, and what is not
aws_sigv4 is the scheme these two descriptors declare, and they are the only two of the 95
built-in provider descriptors TokenTriage ships that declare it. Seven of the 95 carry no gated
cell at all and neither of these two is among them: each declares one endpoint, chat, and the
generated matrix gates both of those cells at its strongest evidence level, a
row-stamp. The matrix names the gate per cell, so you can run
one. Here they are TestBedrockCostGoldenRegionPriced and TestBedrockConverseDescriptor.
Three sibling tests in the same file carry the rest of this page. TestBedrockSigV4Wire asserts the
AWS4-HMAC-SHA256 header and that the signature covers the forwarded body bytes.
TestBedrockStreamFidelity asserts the binary event stream reaches the client byte-for-byte as the
upstream sent it. TestBedrockCostUnpricedRegionUnknown asserts that a row from an unmapped region
carries no dollar and no borrowed key. Every one of them runs against a mock, not against AWS, and
no claim here is a claim about your account’s negotiated rates.
bedrock declares chat and nothing else. Bedrock’s embedding and image surfaces are not wired, so
those calls are forwarded and ledgered without a cost figure.
What can go wrong
| Symptom | Cause | Fix |
|---|---|---|
requires a region |
no region: field |
add it; params.region is turned away with its own message |
credentials_file_env: not valid for the "aws_sigv4" scheme |
Google-style credentials block | use the AWS pair, or api_key_env alone |
| every request gets a signing error | wrong region for the host, or a stale session token | region must match the host; refresh temporary credentials |
region_unknown on every row |
your region is outside us-, eu- and ap- |
expected; the rate does not exist for that class |
model_unknown with a correct-looking model |
the short model name, or the wrong Bedrock bucket | use the full AWS id; try bedrock_converse |