Revoke Amazon Bedrock API keys automatically when Bedrock spend crosses a budget.
A scheduled Lambda estimates your Bedrock spend over a day/week/month from
near-real-time AWS/Bedrock token metrics, and deactivates or deletes the
targeted API keys once the limit is hit.
EventBridge (every N min) → Lambda
1. sum AWS/Bedrock token metrics over the period → price map → USD
2. spend ≥ limit?
3. if yes: deactivate/delete the keys (IAM service-specific credentials)
4. optional SNS email
Bedrock long-term API keys are IAM service-specific credentials on
bedrock.amazonaws.com(console keys live onBedrockAPIKey-*users), so "revoke" meansUpdateServiceSpecificCredential Status=Inactive(reversible) orDeleteServiceSpecificCredential(permanent). Short-term keys aren't managed — they expire on their own.
| Parameter | Default | Description |
|---|---|---|
BudgetLimit |
(required) | Spend threshold in USD. |
BudgetPeriod |
MONTHLY |
DAILY, WEEKLY (ISO Mon→today), or MONTHLY. Calendar-aligned. |
PriceMapJson |
"" |
Optional JSON overriding the built-in price map. |
FallbackInputPricePer1M |
3 |
USD/1M input tokens for unmatched models. |
FallbackOutputPricePer1M |
15 |
USD/1M output tokens for unmatched models. |
PollingIntervalMinutes |
10 |
How often the check runs (2–1440). |
RevokeAction |
DEACTIVATE |
DEACTIVATE (reversible) or DELETE (permanent). |
TargetUsers |
* |
Comma-separated IAM user names, or */ALL for all users. |
DryRun |
false |
Log what would be revoked without changing anything. |
NotificationEmail |
"" |
Optional SNS email for revocation alerts. |
sam build
sam deploy --guided --stack-name bedrock-key-revoker --capabilities CAPABILITY_IAMStart with DryRun=true, check the logs, then redeploy with DryRun=false.
Without the SAM CLI, aws cloudformation package + deploy works too (the
template uses the AWS::Serverless-2016-10-31 transform).
Cost is tokens × rate per model. InputTokenCount excludes cached tokens, so
cache read (~0.1× input) and cache write (~1.25× input) are priced separately.
Rates are picked by the first case-insensitive substring of the model id that
matches a price-map key (e.g. opus matches
us.anthropic.claude-opus-4-...). Built-ins: opus, sonnet, haiku,
nova-premier, nova-pro, nova-lite, nova-micro, llama, mistral,
command/cohere, deepseek, titan. Defaults are mid-2026 list prices and
err high so the budget trips sooner; override with PriceMapJson (USD per 1M,
[in, out] or [in, out, cacheRead, cacheWrite]):
{ "opus": [15, 75], "my-model": [1.0, 2.0, 0.1, 1.25] }cloudwatch:GetMetricData/ListMetrics, iam:ListUsers, the three
iam:*ServiceSpecificCredential* actions (scoped to :user/*), sns:Publish,
and CloudWatch Logs.
# Trigger a check now
aws lambda invoke --function-name <stack-name>-revoker /dev/stdout
# Re-enable a deactivated key
aws iam update-service-specific-credential \
--user-name BedrockAPIKey-abc --service-specific-credential-id ACCA... --status ActiveEach run logs a JSON summary (bedrock_spend_usd, cost_breakdown,
over_budget, revoked).
uv run --group dev python -m pytest -q- Spend is an estimate — it ignores batch/provisioned discounts, cross-region surcharges, and non-token charges. Reconcile with the Cost and Usage Report.
- Only long-term keys are revoked.
- Token metrics are regional — deploy per region you use Bedrock in.