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

PlanTracked eventsHistoryAWS connections
Free10,000 / month7 days1
Pro — $29/moUnlimited90 days10
Team — $99/moUnlimited1 yearUnlimited, 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.