> ## Documentation Index
> Fetch the complete documentation index at: https://gomodel-fix-guardrail-enforcement-gaps.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Anthropic Messages API

> Accept Anthropic-style /v1/messages requests in GoModel and route them to any configured provider, with budgets, failover, and usage tracking applied.

## Overview

GoModel accepts the **Anthropic Messages API** request dialect at `POST /v1/messages`,
in addition to its OpenAI-compatible API. Clients and SDKs that speak the Anthropic
format can point at GoModel unchanged.

The request is translated to GoModel's canonical chat type at ingress and runs through
the same pipeline as `/v1/chat/completions` — so virtual models, workflow policy,
budgets, failover, the response cache, usage/cost tracking, and audit logging all
apply. Because every provider implements chat completion, an Anthropic-format request
can be routed to **any** configured provider (OpenAI, Gemini, Bedrock, and others),
not only Anthropic.

This differs from the [passthrough API](/features/passthrough-api): `/p/anthropic/v1/messages`
forwards bytes verbatim to the Anthropic upstream only, while the managed `/v1/messages`
endpoint routes anywhere and is fully managed.

## Native forwarding to Anthropic

When a `/v1/messages` request resolves to an **Anthropic** provider, GoModel
skips the translation round-trip and forwards the original request body
verbatim (rewriting only the `model` field when an alias resolved to a
different name), then relays the provider-native response or SSE stream
unchanged. This preserves everything the canonical translation cannot —
`cache_control` breakpoints, thinking-block signatures, `anthropic-beta`
headers — which coding agents like Claude Code depend on. Rate limits,
budgets, audit logging, and usage tracking still apply, for both streaming
and non-streaming responses.

Native forwarding is automatic. Requests fall back to the translated pipeline
when a feature that operates on the canonical request is in play: guardrails
request patching, the response cache, or failover routing. Requests resolving
to any non-Anthropic provider always translate.

Because the body is forwarded verbatim, none of the [translation limitations](#limitations)
apply on this path: server-tool history (`server_tool_use`, `web_search_tool_result`, …),
container uploads, and any other block the canonical request cannot represent reach
Anthropic unchanged.

## Supported endpoints

| Endpoint                                | Behavior                                                                                                                                                                                                        |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /v1/messages`                     | Creates a message through translated model routing. Supports streaming (`stream: true`) with Anthropic-format SSE events.                                                                                       |
| `POST /v1/messages/count_tokens`        | Counts input tokens. Exact for models whose provider has a counting endpoint (Anthropic); a calibrated estimate otherwise.                                                                                      |
| `GET /v1/models`                        | Returns the catalog in the Anthropic list shape (`type: "model"`, `display_name`, `created_at`) when the request carries the `anthropic-version` header the Anthropic SDKs always send; OpenAI shape otherwise. |
| `GET /v1/models/{model}`                | Retrieves one model, in the Anthropic model shape when the request carries `anthropic-version`; OpenAI shape otherwise.                                                                                         |
| `POST /v1/messages/batches`             | Creates a Message Batch. Inline `requests[].params` are translated per item, so a batch can target any provider with native batch support (Anthropic, OpenAI, …), not only Anthropic.                           |
| `GET /v1/messages/batches`              | Lists batches (`limit`, `after_id`).                                                                                                                                                                            |
| `GET /v1/messages/batches/{id}`         | Retrieves a batch (`processing_status`, `request_counts`, `results_url` once ended).                                                                                                                            |
| `POST /v1/messages/batches/{id}/cancel` | Cancels a running batch.                                                                                                                                                                                        |
| `DELETE /v1/messages/batches/{id}`      | Deletes an ended batch. Deleted upstream too when the provider's batch API supports it (Anthropic); otherwise removed from the gateway only.                                                                    |
| `GET /v1/messages/batches/{id}/results` | Streams results as JSONL. Successful items are returned in the Anthropic Messages shape regardless of the provider that served them.                                                                            |

## Message Batches

`/v1/messages/batches` shares the gateway's native-batch pipeline with the
OpenAI-compatible `/v1/batches` route — the two are dialect views of the same
resource. Batch IDs are interchangeable across the two dialects (`msgbatch_<uuid>`
here, `batch_<uuid>` there). All requests in one batch must resolve to a single
provider; per-item `custom_id` values are required and must be unique.

Providers whose batch API is file-based (OpenAI-compatible) receive inline
requests as an automatically uploaded JSONL input file. `request_counts` maps the
provider's aggregate counts: while a batch runs, unfinished requests are reported
as `processing`; once it ends, any remainder is attributed by the batch outcome
(`canceled`, `expired`, or `errored`).

```python theme={null}
batch = client.messages.batches.create(requests=[
    {"custom_id": "q1", "params": {"model": "openai/gpt-4o-mini", "max_tokens": 64,
        "messages": [{"role": "user", "content": "What is 2+3?"}]}},
])
# poll client.messages.batches.retrieve(batch.id) until "ended", then:
for entry in client.messages.batches.results(batch.id):
    print(entry.custom_id, entry.result.type)
```

## Authentication

Both credential styles work, so the official Anthropic SDKs are drop-in:

* `Authorization: Bearer <key>` — GoModel's primary scheme.
* `x-api-key: <key>` — the Anthropic-native header, accepted as a fallback when no
  `Authorization` header is present.

```python theme={null}
import anthropic

client = anthropic.Anthropic(
    api_key="<your GoModel key>",       # sent as x-api-key — works as-is
    base_url="https://your-gateway",
)
client.messages.create(
    model="openai/gpt-4o-mini",          # any configured provider's model
    max_tokens=256,
    messages=[{"role": "user", "content": "Hello"}],
)
```

## Example

<CodeGroup>
  ```bash curl theme={null}
  curl https://your-gateway/v1/messages \
    -H "Authorization: Bearer $GOMODEL_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "claude-sonnet-4-6",
      "max_tokens": 256,
      "system": "Be concise.",
      "messages": [{"role": "user", "content": "Hello"}]
    }'
  ```

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

  import anthropic

  client = anthropic.Anthropic(
      base_url="https://your-gateway",
      api_key=os.environ["GOMODEL_KEY"],
  )

  message = client.messages.create(
      model="claude-sonnet-4-6",
      max_tokens=256,
      system="Be concise.",
      messages=[{"role": "user", "content": "Hello"}],
  )

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

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

  const client = new Anthropic({
    baseURL: "https://your-gateway",
    apiKey: process.env.GOMODEL_KEY,
  });

  const message = await client.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 256,
    system: "Be concise.",
    messages: [{ role: "user", content: "Hello" }],
  });

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

The response uses the Anthropic Messages shape (`type: "message"`, `content` blocks,
`stop_reason`, `usage`). Errors use the Anthropic error envelope
(`{"type": "error", "error": {...}}`). `max_tokens` is required, as in the Anthropic API.

Streaming responses emit the Anthropic SSE event sequence (`message_start`,
`content_block_start`/`content_block_delta`/`content_block_stop`, `message_delta`,
`message_stop`).

## Cost tracking and audit logs

`/v1/messages` requests are tracked and audited exactly like the OpenAI-compatible
routes. Cost is computed from the actual provider that served the request, and usage
is recorded under the `/v1/messages` endpoint so it can be filtered in the dashboard.

## Limitations

These limitations apply to the **translated** pipeline — requests routed to a
non-Anthropic provider, or to Anthropic with guardrails, response cache, or
failover engaged. Requests [natively forwarded to Anthropic](#native-forwarding-to-anthropic)
are preserved byte-for-byte apart from the `model` value when an alias
resolved to a different name, and none of the below applies.

`/v1/messages` translates through GoModel's canonical chat type. Anthropic-specific
features that have no canonical equivalent are not preserved end to end:

* **`cache_control`** is preserved on the request, system/content blocks,
  custom tools, and tool-use/tool-result history when routed to Anthropic.
* **`{"role": "system"}` messages inside `messages`** keep their position and
  `cache_control` breakpoints when routed to a Claude 4.8+/5-family model
  (Claude Code appends system reminders this way, and its prompt caching
  depends on them staying in place). On older Claude models, which reject the
  role, their text is hoisted into the top-level system prompt instead.
* **`thinking` and `redacted_thinking` blocks** travel both ways verbatim,
  signatures included: responses carry the `signature` Anthropic issued (as a
  `signature_delta` when streaming), and an assistant turn echoed back is
  replayed unchanged, so thinking-enabled conversations and tool-use loops
  continue correctly. Other providers never see them.
* **`tool_result.is_error`** is preserved when routed to Anthropic. Other
  providers have no equivalent flag and receive the result content only.
* **`tool_use.extra_content`** carries another provider's replay state, such as
  a Gemini 3 thought signature, back to that provider. GoModel sets it on
  `tool_use` blocks it returns; echo it unchanged. See
  [Extra content, thinking blocks, and thought signatures](/advanced/extra-content).
* **Server/built-in tools** (web search, code execution, …) and their history
  blocks (`server_tool_use`, `web_search_tool_result`, …) are rejected with a clear
  `400`; only custom tools (`type` absent or `"custom"`) translate.
* **`top_k`** is dropped — it has no portable OpenAI-compatible equivalent, and
  OpenAI-family providers reject unknown request fields. `temperature` and `top_p`
  are forwarded.
* **Images and documents inside `tool_result` blocks** (screenshots, image and
  PDF files read by a tool — Claude Code returns them this way) are forwarded as
  native `image` and `document` blocks when routed to Anthropic. Other providers
  receive only the text portion of the tool result.
* **`document` blocks** (PDF, plain text, URL, or Files API `file_id` sources)
  translate to the OpenAI-style `file` content part (`file_data` for inline
  content, `file_url` for remote URLs, `file_id` for uploads). Anthropic gets the
  document back natively with its `title`; Gemini receives inline data only;
  OpenAI-compatible providers receive the `file` part as-is. Citation settings and `context` are
  dropped. The custom-content variant (`source.type: "content"`) degrades to text.
* **`search_result` blocks** degrade to text (title, source URL, and content);
  citation metadata is dropped.
* **Other content blocks** (`container_upload`, `tool_reference`, …) are rejected
  with a clear `400` error rather than silently dropped.
* **`stop_sequences`** are honored on every provider. Providers that report the
  matched sequence natively (Anthropic) get the full contract back:
  `stop_reason: "stop_sequence"` plus the `stop_sequence` value. OpenAI-family
  providers conflate stop-parameter hits with natural stops in `finish_reason`,
  so completions there report `stop_reason: "end_turn"` (output is still truncated
  correctly).
* **`thinking`** on a non-Anthropic model becomes a reasoning effort (budget
  under 10000 tokens: `low`, under 20000: `medium`, otherwise `high`; adaptive:
  `medium`), sent as each provider's own field: `reasoning_effort` on OpenAI,
  Groq, Fireworks, and xAI. Models that reject the field (OpenAI `gpt-4*`, Groq
  models other than gpt-oss and qwen3, xAI's `-non-reasoning`, `grok-build`,
  `grok-2` and `grok-3` except `grok-3-mini`) get the request without it
  instead of an error.
  Reasoning comes back as `thinking` blocks whichever member the provider uses
  for it (`reasoning_content` or `reasoning`).
* **`count_tokens`** is exact when the model's provider can count tokens itself:
  Anthropic models are counted by Anthropic's own `count_tokens` endpoint. The
  forwarded request carries the resolved model (an alias is resolved first)
  and only the members that endpoint accepts: `messages`, `system`, `tools`,
  `tool_choice`, `thinking`, `cache_control`, and `output_config`; `max_tokens`,
  `stream`, `metadata`, and the sampling controls are left out because Anthropic
  rejects them there. For every other provider, and whenever
  that call fails, the gateway answers with an estimate that weights text by
  how densely it tokenizes (prose, code and JSON, CJK, emoji), adds per-message
  and per-tool framing plus the tool-use system prompt, and prices images by
  their pixel area. On ordinary agent traffic the estimate lands within about
  ten percent of a tokenizer-exact count; raw base64 in text stays
  under-counted, and an image whose size cannot be read is charged the largest
  size the provider keeps. The same estimate seeds `usage.input_tokens` in the
  streaming `message_start` event; the authoritative counts arrive in the final
  `message_delta` event, which SDK accumulators prefer.

For byte-exact Anthropic fidelity beyond the supported cache controls, use the
`/p/anthropic/v1/messages` passthrough route instead.

See [ADR-0007](https://github.com/ENTERPILOT/GoModel/blob/main/docs/adr/0007-anthropic-messages-ingress.md)
for the design rationale and tradeoffs.
