Documentation
BedrockOps tracks what your AWS Bedrock workloads cost, how fast they respond, and how often they are throttled or fail — per model, region, and account — and suggests where to save.
How it works
There are three ways to get usage into BedrockOps. You can combine them.
- AWS connection (recommended): BedrockOps reads the model invocation logs Bedrock already writes to CloudWatch Logs or S3, through a read-only IAM role in your account. Nothing sits in your request path. Logs are imported every 5 minutes; a new connection backfills the last 24 hours.
- SDK or ingest API: your application reports each call. Useful when invocation logging is off, or to attach your own tags (team, feature, customer).
- Converse proxy: send Converse requests to BedrockOps, which calls Bedrock with your role and records the call.
Connect an AWS account
1. Turn on model invocation logging
In the Bedrock console open Settings → Model invocation logging, enable it, and choose CloudWatch Logs, S3, or both, in every region where you use Bedrock (logging is configured per region). Text data is enough. See the AWS guide. Logs written to S3 must stay under the default AWSLogs/<account>/BedrockModelInvocationLogs/ layout; any key prefix you set there goes into the connection's S3 prefix field.
2. Add the connection
In Settings → AWS Connections, add the account ID, the ARN of the role you are about to create (for example arn:aws:iam::123456789012:role/BedrockOpsReadOnly), the regions, and the log group and/or bucket. The connection gets its own external ID.
3. Create the IAM role
Under IAM setup on the connection, the app shows the exact trust policy — the BedrockOps principal plus your external ID — to paste into the role. Then attach read-only permissions for the sources you use:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadBedrockInvocationLogs",
"Effect": "Allow",
"Action": "logs:FilterLogEvents",
"Resource": [
"arn:aws:logs:*:123456789012:log-group:/aws/bedrock/modelinvocations",
"arn:aws:logs:*:123456789012:log-group:/aws/bedrock/modelinvocations:*"
]
},
{
"Sid": "ListInvocationLogBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bedrock-logs"
},
{
"Sid": "ReadInvocationLogFiles",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bedrock-logs/*"
}
]
}If you also use the Converse proxy, allow bedrock:InvokeModel on the models you call.
4. Test
Test assumes the role and reads each source with the same calls ingestion makes, and tells you which step fails and which permission is missing. A passing test activates the connection. If imports keep failing for a day, the connection is paused and you get an email; fix the cause and press Test to resume.
SDKs
Both SDKs are single files with no dependencies. Create an API key under Settings → API Keys (keys start with bo_ and are shown once). Events are batched and sent in the background.
Python
curl -O https://bedrockops.site/sdk/bedrockops.py
import boto3, bedrockops
bo = bedrockops.Client(api_key="bo_...")
bo.patch_boto3() # every bedrock-runtime call is now tracked
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
bedrock.converse(modelId="us.anthropic.claude-haiku-4-5-20251001-v1:0", messages=[...])
# or record calls yourself, with tags:
with bo.trace("us.anthropic.claude-sonnet-4-6", tags={"team": "search"}) as t:
t.record(bedrock.converse(...))patch_boto3 records tokens (including prompt-cache tokens) for Converse and InvokeModel. For streaming calls it records latency to the first byte only; use trace() and pass the stream's final usage to record().
Go
curl -O https://bedrockops.site/sdk/bedrockops.go # into your module
bo := bedrockops.New(bedrockops.Config{APIKey: "bo_..."})
defer bo.Close()
track := bo.Middleware("us-east-1")
start := time.Now()
out, err := bedrockClient.Converse(ctx, input)
track(input.ModelId, out, err, time.Since(start))Ingest API
Report calls from any language.
POST https://api.bedrockops.site/ingest/events
X-API-Key: bo_...
Content-Type: application/json
{
"events": [{
"model_id": "anthropic.claude-sonnet-4-6", // required
"region": "us-east-1",
"operation": "Converse",
"request_id": "7f9c...", // used to drop duplicates
"input_tokens": 1500,
"output_tokens": 420,
"cache_read_tokens": 0,
"cache_write_tokens": 0,
"latency_ms": 1830,
"ttft_ms": 240,
"status_code": 200,
"tags": {"team": "search"},
"timestamp": "2026-09-23T12:00:00Z", // default: now
"connection_id": "<optional connection UUID>"
}]
}- Up to 1,000 events per request and 1,000 requests per minute per account.
- Events older than 28 days, or with invalid fields, are skipped and counted in
rejected; the rest are recorded. The response is{"recorded": n, "rejected": n, "costs": {...}}. - Cost is computed from Bedrock on-demand prices for the model (inference-profile IDs such as
eu.anthropic...are priced as the underlying model). - On the Free plan, a request that would exceed 10,000 tracked events in the month is refused with 402.
Converse proxy
Send a Bedrock Converse request body to BedrockOps. It is signed with the role of the connection you name (which needs bedrock:InvokeModel), sent to Bedrock, and recorded.
POST https://api.bedrockops.site/proxy/converse
X-API-Key: bo_...
{
"connection_id": "<connection UUID>",
"model_id": "us.anthropic.claude-haiku-4-5-20251001-v1:0",
"region": "us-east-1",
"tags": {"feature": "chat"},
"body": {
"messages": [{"role": "user", "content": [{"text": "Hello"}]}],
"inferenceConfig": {"maxTokens": 256}
}
}The response carries Bedrock's status code and body plus _bo_input_tokens, _bo_output_tokens, _bo_cost_usd, _bo_latency_ms, and _bo_request_id. Streaming is not supported through the proxy; use the SDK for streaming calls.
Alerts
Rules watch cost per hour, total cost, request count, token throughput, p95/p99 latency, throttle rate, or error rate over a window you choose, and notify by email and/or a Slack incoming webhook. Rules are evaluated every minute.
Plans & data retention
| Plan | Tracked events | History | AWS connections |
|---|---|---|---|
| Free | 10,000 / month | 7 days | 1 |
| Pro — $29/mo | Unlimited | 90 days | 10 |
| Team — $99/mo | Unlimited | 1 year | Unlimited, plus an organization with team rollups |
Events count toward the monthly allowance when they come from the SDK, the ingest API, or the proxy; log imports from an AWS connection do not. Usage data is deleted after the plan's history window (on Free, after 30 days, so an upgrade brings back the whole month). Plan changes are prorated. Prices exclude VAT.
Your account & data
Settings → Your data downloads everything stored about your account as JSON, or deletes the account: any subscription is cancelled immediately and all connections, keys, alerts, and usage data are removed. Questions: info@nubri.co.