> ## Documentation Index
> Fetch the complete documentation index at: https://docs.promptguard.co/llms.txt
> Use this file to discover all available pages before exploring further.

# AWS Bedrock

> Scan Amazon Bedrock Runtime calls made with boto3 or the AWS SDK for JavaScript, or send them through the PromptGuard proxy with a Bedrock API key

There are two ways in. If your code calls Bedrock with an AWS SDK, use [auto-instrumentation](/integrations/auto-instrumentation): your AWS SDK keeps signing requests with your own AWS credentials and calling Bedrock directly; PromptGuard scans each prompt before the call leaves your process, whichever model on Bedrock you call. If your code uses an OpenAI client, send it [through the proxy](#through-the-proxy) with a Bedrock API key instead.

## Python (boto3)

`init()` hooks the `InvokeModel`, `Converse` and `ConverseStream` operations of every Bedrock Runtime client.

```python theme={"system"}
import boto3
import promptguard

promptguard.init()  # reads PROMPTGUARD_API_KEY

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")

response = bedrock.converse(
    modelId="amazon.nova-lite-v1:0",
    messages=[{"role": "user", "content": [{"text": "Hello!"}]}],
)
print(response["output"]["message"]["content"][0]["text"])
```

In enforce mode, the default, a blocked prompt raises `PromptGuardBlockedError` from the `converse` call before AWS is called.

## Node.js (AWS SDK v3)

`init()` hooks `send` on `BedrockRuntimeClient`, for `InvokeModelCommand`, `ConverseCommand` and their streaming variants. For a streaming command the prompt is scanned and the streamed output is not.

```typescript theme={"system"}
import { init } from 'promptguard-sdk';
import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime';

init(); // reads PROMPTGUARD_API_KEY

const bedrock = new BedrockRuntimeClient({ region: 'us-east-1' });

const response = await bedrock.send(
  new ConverseCommand({
    modelId: 'amazon.nova-lite-v1:0',
    messages: [{ role: 'user', content: [{ text: 'Hello!' }] }],
  }),
);
```

## Through the proxy

The proxy takes OpenAI's chat-completions request for a `bedrock/` model, calls Bedrock's [Converse](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html) operation with your Bedrock API key, and answers in OpenAI's shape. The model id after `bedrock/` is Bedrock's own: a model id, an inference profile such as `us.amazon.nova-lite-v1:0`, or an ARN.

### 1. Store a Bedrock API key with its region

Create a **long-term** [Bedrock API key](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html) (its value begins `ABSK`) for an IAM user that may call only the models you use, and give it an expiry. Store it with the AWS region it is for: in **Dashboard → Gateway → Provider Keys → Add Provider Key**, choose **AWS Bedrock**, paste the key and enter the region under **AWS region**. Or through the [provider keys API](/gateway/provider-keys#storing-a-key), as a `bedrock` key with the region in `base_url`:

```bash theme={"system"}
curl -X POST https://api.promptguard.co/dashboard/provider-keys \
  -H "Authorization: Bearer $DASHBOARD_TOKEN" \
  -H "X-CSRF-Token: $CSRF" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "bedrock",
    "name": "Bedrock eu-west-1",
    "key": "<your Bedrock API key>",
    "base_url": "eu-west-1"
  }'
```

PromptGuard sends the key to that region's Bedrock Runtime endpoint and nowhere else. `base_url` takes a region or the endpoint itself (`https://bedrock-runtime.eu-west-1.amazonaws.com`, a `-fips` endpoint, or a China-region `.amazonaws.com.cn` one; a `cn-` region is sent to the `.amazonaws.com.cn` endpoint); any other host is refused, and so is a `bedrock` key without one. A short-term key (`bedrock-api-key-…`) is refused too, because it expires within 12 hours.

### 2. Send the request

<Note>
  **Proxy Base URL:** `https://api.promptguard.co/api/v1`

  Point your LLM SDK's `base_url` / `baseURL` to this endpoint. Your existing provider API key goes in the `Authorization` header as usual; add your PromptGuard key via `X-API-Key`.
</Note>

<CodeGroup>
  ```python Python theme={"system"}
  import os
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.promptguard.co/api/v1",
      api_key="unused",  # PromptGuard replaces it with the stored Bedrock key
      default_headers={"X-API-Key": os.environ["PROMPTGUARD_API_KEY"]},
  )

  response = client.chat.completions.create(
      model="bedrock/amazon.nova-lite-v1:0",
      messages=[{"role": "user", "content": "Hello!"}],
  )
  print(response.choices[0].message.content)
  ```

  ```typescript Node.js theme={"system"}
  import OpenAI from 'openai';

  const client = new OpenAI({
    baseURL: 'https://api.promptguard.co/api/v1',
    apiKey: 'unused', // PromptGuard replaces it with the stored Bedrock key
    defaultHeaders: { 'X-API-Key': process.env.PROMPTGUARD_API_KEY },
  });

  const response = await client.chat.completions.create({
    model: 'bedrock/amazon.nova-lite-v1:0',
    messages: [{ role: 'user', content: 'Hello!' }],
  });
  ```
</CodeGroup>

Without a stored `bedrock` key, the proxy forwards the Bedrock API key you send in `Authorization` only if the deployment sets a default region in `AWS_BEDROCK_REGION`. PromptGuard Cloud does not, so on Cloud store the key: a request with no region is refused, rather than sent to a region nobody chose.

### What the proxy translates

| OpenAI request field | Converse |
| - | - |
| `system` and `developer` messages | `system` |
| `user` and `assistant` messages, text content | `messages` (consecutive turns from one role are joined, as Converse requires) |
| `max_tokens` or `max_completion_tokens`, `temperature`, `top_p`, `stop` | `inferenceConfig` |
| `stream: true` | Answered as OpenAI server-sent events, built from one Converse call |

Anything else — tools and tool calls, images and other non-text content, `response_format`, `n` greater than 1, `logprobs`, penalties, `seed` — is refused with a 400 that names the field, and nothing is sent to Bedrock. Those change what the model does, so dropping them would answer a different request. Tool calls through the proxy's `bedrock/` route are out of scope today, not an oversight: for them, call Bedrock with your AWS SDK and auto-instrumentation.

The reply's `finish_reason` comes from Bedrock's `stopReason`: `end_turn` and `stop_sequence` are `stop`, `max_tokens` is `length`, and a guardrail or content-filter stop is `content_filter`. A stop reason Bedrock adds later is answered as `stop` until PromptGuard maps it. Bedrock's request id is returned as `x-request-id`.

### Errors

| What happened | Status | What you get |
| - | - | - |
| No Bedrock API key: none stored and none in `Authorization` | 401 | `error.code` `missing_api_key` |
| No region: no stored key, and the deployment sets no `AWS_BEDROCK_REGION` | 400 | A message saying to store the key with its region |
| A field Converse cannot take | 400 | A message naming the field |
| Bedrock refused the request | Bedrock's own | Bedrock's message, prefixed `[bedrock]`; a rate limit, an unknown model or a Bedrock outage gets the proxy's standard message for that status |
| Something in front of Bedrock answered 200 with a body that is not a Converse reply | 502 | `error.code` `upstream_invalid_response` |

Every error is in OpenAI's `{"error": {...}}` shape, so an OpenAI client raises it as it would any other. Nothing is sent to Bedrock for the first three.

### Usage and spend caps

Each answer, streamed or not, records the input and output tokens Bedrock reported, so they appear in your events and usage. A stream you send without `stream_options` still records them; you receive the usage chunk only if you ask for it with `stream_options: {"include_usage": true}`. PromptGuard does not price Bedrock models, because their price depends on your AWS agreement, so a Bedrock answer has no cost figure.

For the same reason, a project with a [spend cap](/gateway/spend-caps) refuses `bedrock/` models with a 400 `upstream_spend_cap_unpriced_model` and sends nothing to Bedrock: the cap cannot count what it cannot price. Use `bedrock/` from a project without a cap.

### Testing a stored key

**Send test request** on the Provider Keys page tests a stored Bedrock key with `amazon.nova-micro-v1:0`, Amazon's smallest text model, in the key's region. The test fails if your account has no access to that model in that region; enable it in the Bedrock console first.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.