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

# Anthropic Proxy

> Route Anthropic Messages API requests through MetrixLLM for observability, rate limiting, and provider fallback.

The MetrixLLM gateway provides a drop-in proxy for Anthropic's Messages API. Use your existing Anthropic SDKs or raw HTTP requests by changing only the Base URL and API key.

## Base URL

```text theme={null}
https://gateway.metrixllm.com
```

## Authentication

All requests require a MetrixLLM API key. The gateway accepts both formats:

```text theme={null}
x-api-key: sk-metrix-YOUR_API_KEY
```

```text theme={null}
Authorization: Bearer sk-metrix-YOUR_API_KEY
```

<Warning>
  Do **not** use your Anthropic API key. The gateway authenticates requests using MetrixLLM keys (`sk-metrix-*` prefix). The gateway then uses its own provider keys (or your BYOK keys) to call Anthropic.
</Warning>

### Internal Authentication

For backend-to-gateway communication, the gateway supports internal authentication:

| Header                  | Required | Description                                                           |
| ----------------------- | -------- | --------------------------------------------------------------------- |
| `x-metrix-internal`     | Yes      | Shared secret matching `HEALTH_CHECK_SECRET` (timing-safe comparison) |
| `x-metrix-workspace-id` | Yes\*    | Workspace UUID (use `x-workspace-id` as fallback)                     |

Internal auth skips the standard API key validation. Used by the MetrixLLM backend for playground, replay, and batch operations.

## Endpoints

| Method | Path                     | Description                                |
| ------ | ------------------------ | ------------------------------------------ |
| `POST` | `/anthropic/v1/messages` | Messages (returns Anthropic-native format) |

***

## Request Headers

Every request supports these headers:

| Header              | Type     | Required | Description                                                       |
| ------------------- | -------- | -------- | ----------------------------------------------------------------- |
| `x-api-key`         | `string` | Yes\*    | `sk-metrix-{key}` — MetrixLLM API key                             |
| `Authorization`     | `string` | Yes\*    | `Bearer sk-metrix-{key}` — alternative to `x-api-key`             |
| `Content-Type`      | `string` | Yes      | Must be `application/json`                                        |
| `anthropic-version` | `string` | No       | Anthropic API version (e.g. `2023-06-01`). Forwarded to upstream. |

**Session Tracing**

Use these headers to group related requests into sessions and trace spans:

| Header                | Description                                                                  | Stored In                   |
| --------------------- | ---------------------------------------------------------------------------- | --------------------------- |
| `metrix-session-id`   | Unique session identifier — groups related requests into a traceable session | `workspace_logs.session_id` |
| `metrix-parent-id`    | Links a request to a parent span in the session trace                        | `workspace_logs.parent_id`  |
| `metrix-session-name` | Human-readable name for the span (for dashboard display)                     | `workspace_logs.span_name`  |

**Custom Properties**

Attach up to 10 custom metadata properties per request:

| Header Pattern          | Description                                                                        |
| ----------------------- | ---------------------------------------------------------------------------------- |
| `metrix-property-{key}` | Custom metadata property. Used for rate limit targeting, analytics, and filtering. |

Example: `metrix-property-team: engineering`, `metrix-property-environment: production`

**Worker-to-Gateway**

These headers are set by the Cloudflare Edge Worker when routing through the Durable Object balance system:

| Header                 | Description                                                                        |
| ---------------------- | ---------------------------------------------------------------------------------- |
| `x-balance`            | Workspace credit balance from Durable Object                                       |
| `x-internal-signature` | HMAC-SHA256 signature of `{workspace_id}:{balance}` verified with `DO_SYNC_SECRET` |

**IP Detection**

| Header            | Description                                                                                                          |
| ----------------- | -------------------------------------------------------------------------------------------------------------------- |
| `x-forwarded-for` | Client IP address (first IP in comma-separated list). Trusted only when peer is a private/local address.             |
| `x-real-ip`       | Fallback client IP header. Same trust rules as `x-forwarded-for`.                                                    |
| `cf-ipcountry`    | Cloudflare-specific country code of the connecting client (e.g. `US`, `IN`). Used for request logging and analytics. |

***

## Messages

### Request Body

Send standard Anthropic Messages API requests. The gateway accepts all standard Anthropic fields plus MetrixLLM-specific extensions.

| Field                 | Type                | Required | Description                                                                       |
| --------------------- | ------------------- | -------- | --------------------------------------------------------------------------------- |
| `model`               | `string`            | Yes      | Model identifier (e.g. `claude-opus-4-20250514`, `claude-sonnet-4-20250514`)      |
| `messages`            | `array`             | Yes      | Array of message objects                                                          |
| `max_tokens`          | `integer`           | Yes      | Maximum output tokens (Anthropic requires this; defaults to 8192 if not provided) |
| `system`              | `string \| array`   | No       | System prompt (top-level field, not a message)                                    |
| `temperature`         | `number`            | No       | Sampling temperature (0-1)                                                        |
| `top_p`               | `number`            | No       | Nucleus sampling parameter                                                        |
| `stop_sequences`      | `array`             | No       | Stop sequences                                                                    |
| `stream`              | `boolean`           | No       | Enable streaming                                                                  |
| `tools`               | `array`             | No       | Tool definitions (Anthropic format)                                               |
| `tool_choice`         | `object`            | No       | Tool selection strategy                                                           |
| `thinking`            | `object`            | No       | Extended thinking configuration (see [Thinking](#thinking))                       |
| `mode`                | `string`            | No       | Key selection: `"auto"` (default), `"byok"`, or `"native"`                        |
| `web_search`          | `boolean \| string` | No       | Enable web search: `true` (standard), `"native"` (Anthropic built-in)             |
| `web_search_max_uses` | `integer`           | No       | Max web search queries per request (native mode)                                  |
| `prompt_id`           | `string`            | No       | Use a stored prompt template                                                      |
| `inputs`              | `object`            | No       | Template variables for prompt placeholders                                        |

### Message Format

Messages follow the standard Anthropic format with content blocks:

```json theme={null}
{
  "role": "user",
  "content": [
    {"type": "text", "text": "What is in this image?"},
    {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": "..."}}
  ]
}
```

### Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://gateway.metrixllm.com/anthropic/v1/messages \
    -H "Content-Type: application/json" \
    -H "x-api-key: sk-metrix-YOUR_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "metrix-session-id: session_abc123" \
    -H "metrix-session-name: Document Analysis" \
    -d '{
      "model": "claude-sonnet-4-20250514",
      "max_tokens": 1024,
      "system": "You are a helpful assistant.",
      "messages": [
        {"role": "user", "content": "What is the capital of France?"}
      ]
    }'
  ```

  ```python Python theme={null}
  import anthropic

  client = anthropic.Anthropic(
      base_url="https://gateway.metrixllm.com/anthropic",
      api_key="sk-metrix-YOUR_API_KEY",
  )

  message = client.messages.create(
      model="claude-sonnet-4-20250514",
      max_tokens=1024,
      system="You are a helpful assistant.",
      messages=[
          {"role": "user", "content": "What is the capital of France?"},
      ],
  )

  print(message.content[0].text)
  ```

  ```javascript Node.js theme={null}
  import Anthropic from "@anthropic-ai/sdk";

  const client = new Anthropic({
    baseURL: "https://gateway.metrixllm.com/anthropic",
    apiKey: "sk-metrix-YOUR_API_KEY",
  });

  const message = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    system: "You are a helpful assistant.",
    messages: [
      { role: "user", content: "What is the capital of France?" },
    ],
  });

  console.log(message.content[0].text);
  ```
</CodeGroup>

### Response Body

```json theme={null}
{
  "id": "msg_aK3x9mP2",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-20250514",
  "content": [
    {
      "type": "text",
      "text": "The capital of France is Paris."
    }
  ],
  "stop_reason": "end_turn",
  "usage": {
    "input_tokens": 25,
    "output_tokens": 8
  }
}
```

### Response Headers

Every successful response includes these headers:

| Header                           | Description                                                             |
| -------------------------------- | ----------------------------------------------------------------------- |
| `Content-Type`                   | Always `application/json`                                               |
| `X-Metrix-Provider`              | Resolved upstream provider (e.g. `anthropic`, `openai`)                 |
| `X-Metrix-Model`                 | Actual model used for the request                                       |
| `X-Metrix-Cache-Status`          | `"HIT"` or `"miss"` (semantic cache)                                    |
| `X-Metrix-Retry-Count`           | Number of retry attempts (0 if none)                                    |
| `X-Metrix-Retry-Status`          | `"success"`, `"failed"`, or `"not_triggered"`                           |
| `X-Metrix-Fallback`              | `"true"` if a fallback provider was used (only present when applicable) |
| `X-RateLimit-Limit`              | Configured limit for the rate window                                    |
| `X-RateLimit-Remaining`          | Remaining quota in the current window                                   |
| `X-RateLimit-Requests-Remaining` | Remaining requests in the current rate window                           |
| `X-RateLimit-Tokens-Remaining`   | Remaining tokens in the current rate window                             |

On cache HIT responses, only `Content-Type` and `X-Metrix-Cache-Status: HIT` are returned.

On rate limited responses (`429 Too Many Requests`):

| Header                         | Description                                                           |
| ------------------------------ | --------------------------------------------------------------------- |
| `Retry-After`                  | Seconds to wait before retrying                                       |
| `X-RateLimit-{type}-Limit`     | Maximum limit (e.g. `X-RateLimit-rpm-Limit`, `X-RateLimit-tpm-Limit`) |
| `X-RateLimit-{type}-Remaining` | Remaining count in the current window                                 |
| `X-RateLimit-Reset`            | Seconds until the rate window resets                                  |

***

## Streaming

Set `"stream": true` to receive Server-Sent Events (SSE) in Anthropic's streaming format.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://gateway.metrixllm.com/anthropic/v1/messages \
    -H "Content-Type: application/json" \
    -H "x-api-key: sk-metrix-YOUR_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -d '{
      "model": "claude-sonnet-4-20250514",
      "max_tokens": 1024,
      "messages": [
        {"role": "user", "content": "Write a haiku about coding."}
      ],
      "stream": true
    }'
  ```

  ```python Python theme={null}
  import anthropic

  client = anthropic.Anthropic(
      base_url="https://gateway.metrixllm.com/anthropic",
      api_key="sk-metrix-YOUR_API_KEY",
  )

  with client.messages.stream(
      model="claude-sonnet-4-20250514",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Write a haiku about coding."}],
  ) as stream:
      for text in stream.text_stream:
          print(text, end="", flush=True)
  ```

  ```javascript Node.js theme={null}
  import Anthropic from "@anthropic-ai/sdk";

  const client = new Anthropic({
    baseURL: "https://gateway.metrixllm.com/anthropic",
    apiKey: "sk-metrix-YOUR_API_KEY",
  });

  const stream = client.messages.stream({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Write a haiku about coding." }],
  });

  for await (const event of stream) {
    if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
      process.stdout.write(event.delta.text);
    }
  }
  ```
</CodeGroup>

***

## Thinking (Extended Thinking)

Enable extended thinking for deeper reasoning. The gateway converts between Anthropic's thinking format and other provider formats automatically.

```json theme={null}
{
  "model": "claude-opus-4-20250514",
  "max_tokens": 16000,
  "thinking": {
    "type": "enabled",
    "budget_tokens": 10000
  },
  "messages": [
    {"role": "user", "content": "Explain quantum entanglement in detail."}
  ]
}
```

| Field                    | Type      | Description                                                      |
| ------------------------ | --------- | ---------------------------------------------------------------- |
| `thinking.type`          | `string`  | Must be `"enabled"` to activate thinking                         |
| `thinking.budget_tokens` | `integer` | Maximum tokens allocated to thinking (higher = deeper reasoning) |

<Note>
  When using the gateway's generic proxy (`/v1/chat/completions`), you can use a simplified `thinking` field: `"low"` (1000 tokens), `"medium"` (5000 tokens), or `"high"` (10000 tokens). The gateway converts these to the appropriate provider-specific format.
</Note>

***

## Tool Use

Define tools using Anthropic's native format. The gateway automatically converts between tool formats when routing to different providers.

```json theme={null}
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a location",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "City name"
          }
        },
        "required": ["location"]
      }
    }
  ],
  "messages": [
    {"role": "user", "content": "What is the weather in San Francisco?"}
  ]
}
```

### Tool Choice

Control tool selection with `tool_choice`:

| Value                                     | Behavior                                 |
| ----------------------------------------- | ---------------------------------------- |
| `{"type": "auto"}`                        | Model decides whether to use a tool      |
| `{"type": "any"}`                         | Model must use one of the provided tools |
| `{"type": "none"}`                        | Model must not use any tools             |
| `{"type": "tool", "name": "get_weather"}` | Model must use the specified tool        |

***

## Web Search

Enable web search to give the model access to real-time information.

| Value             | Behavior                                                         |
| ----------------- | ---------------------------------------------------------------- |
| `true`            | Performs a web search and injects results into the system prompt |
| `"native"`        | Uses Anthropic's built-in `web_search_20250305` server tool      |
| `false` or absent | No web search                                                    |

Additional parameters:

| Field                 | Type      | Default          | Description                                  |
| --------------------- | --------- | ---------------- | -------------------------------------------- |
| `web_search_max_uses` | `integer` | Provider default | Maximum number of search queries per request |

### Native Web Search Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://gateway.metrixllm.com/anthropic/v1/messages \
    -H "Content-Type: application/json" \
    -H "x-api-key: sk-metrix-YOUR_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -d '{
      "model": "claude-sonnet-4-20250514",
      "max_tokens": 1024,
      "messages": [
        {"role": "user", "content": "What are the latest developments in AI?"}
      ],
      "tools": [
        {"type": "web_search_20250305", "name": "web_search", "max_uses": 5}
      ]
    }'
  ```

  ```python Python theme={null}
  import anthropic

  client = anthropic.Anthropic(
      base_url="https://gateway.metrixllm.com/anthropic",
      api_key="sk-metrix-YOUR_API_KEY",
  )

  message = client.messages.create(
      model="claude-sonnet-4-20250514",
      max_tokens=1024,
      messages=[{"role": "user", "content": "What are the latest developments in AI?"}],
      tools=[
          {"type": "web_search_20250305", "name": "web_search", "max_uses": 5}
      ],
  )

  print(message.content)
  ```

  ```javascript Node.js theme={null}
  import Anthropic from "@anthropic-ai/sdk";

  const client = new Anthropic({
    baseURL: "https://gateway.metrixllm.com/anthropic",
    apiKey: "sk-metrix-YOUR_API_KEY",
  });

  const message = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: "What are the latest developments in AI?" }],
    tools: [
      { type: "web_search_20250305", name: "web_search", max_uses: 5 },
    ],
  });

  console.log(message.content);
  ```
</CodeGroup>

***

## Prompt Management

Use `prompt_id` to reference a stored prompt template. The gateway fetches the template, renders `{{variable}}` placeholders with values from `inputs`, and sends the rendered messages to the provider.

```json theme={null}
{
  "model": "claude-sonnet-4-20250514",
  "prompt_id": "prompt_abc123",
  "inputs": {
    "user_name": "Alice",
    "topic": "quantum computing"
  }
}
```

***

## Error Responses

Errors return a JSON body following Anthropic's error format. The HTTP status code indicates the error type.

| Status | Meaning                                                  |
| ------ | -------------------------------------------------------- |
| `400`  | Invalid request (missing model, malformed body)          |
| `401`  | Invalid or missing API key                               |
| `402`  | Insufficient credits                                     |
| `403`  | Workspace disabled, IP not allowed, or model not enabled |
| `404`  | Model not recognized                                     |
| `429`  | Rate limit exceeded                                      |
| `446`  | Guardrail block (request blocked by content policy)      |
| `500`  | Internal gateway error                                   |
| `502`  | Upstream provider error                                  |
| `503`  | Provider circuit breaker open                            |

### Error Response Format

```json theme={null}
{
  "error": {
    "message": "Model 'claude-5' is not recognized.",
    "type": "invalid_request_error",
    "code": "model_not_found"
  }
}
```

***

## Retry and Fallback

The gateway automatically retries failed requests and falls back to alternative providers:

* **Retry**: Retries on `429`, `500`, `502`, `503`, `504` status codes with exponential backoff
* **Fallback**: If the primary provider fails, the gateway tries the next configured provider in the chain
* **Circuit Breaker**: Providers with repeated failures are temporarily skipped (circuit opens for 60 seconds)
* **Cerebras Fallback**: If all configured providers fail on system errors, the gateway falls back to Cerebras (if enabled)

These features are transparent to your application -- you receive a single successful response.

***

## Mode Override

Control provider key selection with the `mode` field:

| Value      | Behavior                                                                        |
| ---------- | ------------------------------------------------------------------------------- |
| `"auto"`   | Default. Uses your BYOK key if configured, otherwise uses MetrixLLM native key. |
| `"byok"`   | Forces use of your own API key. Fails if no key is configured for the provider. |
| `"native"` | Forces use of MetrixLLM's managed key. Skips BYOK keys entirely.                |

```json theme={null}
{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "messages": [{"role": "user", "content": "Hello!"}],
  "mode": "native"
}
```
