Last updated 2026-05-17

Azure AI Foundry — Anthropic

The azureanthropic provider talks to Claude models hosted on Azure AI Foundry. The transport layer is Azure-shaped (deployment-scoped URL or /openai/v1/messages, api-version query parameter, api-key header OR an AAD bearer token), but the request body and the SSE event stream are byte-identical to direct Anthropic. Application code that already targets the anthropic provider runs unchanged here — only the constructor changes.

Picking the right Anthropic surface:

Import path

import (
"github.com/elloloop/llmrouter"
"github.com/elloloop/llmrouter/providers/azureanthropic"
)

Two URL variants

Foundry exposes Claude under two endpoint shapes. Pick by whether you provisioned a Foundry deployment for a specific Claude variant, or you're hitting the resource-scoped messages endpoint that picks the variant from the request body.

  1. Deployment-scoped (recommended). Opt in by passing WithDeployment("..."). The constructed URL is:
    https://<resource>.services.ai.azure.com/openai/deployments/<deployment>/messages?api-version=<v>
  2. Resource-scoped (/openai/v1/messages). Omit WithDeployment. The constructed URL is:
    https://<resource>.services.ai.azure.com/openai/v1/messages?api-version=<v>
    The model field in the request body selects the Claude variant.

Authentication

Auth is XOR: provide exactly one of llmrouter.WithAPIKey(...) or azureanthropic.WithAADToken(...). Providing both — or neither — fails at New() with ErrInvalidConfig.

  • API key. Flows as the Azure-specific api-key request header (not x-api-key, the direct-Anthropic header — Foundry uses Azure conventions).
  • AAD bearer. Mint a token from your AAD source (e.g. azidentity) and return it from the AADTokenSource callback. The source is called once per request, so it's the right place to implement caching and refresh.

The anthropic-version header is never sent — Foundry manages the API version via the api-version query parameter. WithAPIVersion is mandatory.

Construction

p, err := azureanthropic.New(
llmrouter.WithAPIKey(os.Getenv("AZURE_FOUNDRY_KEY")),
llmrouter.WithBaseURL("https://my-resource.services.ai.azure.com"),
azureanthropic.WithDeployment("claude-3-5-sonnet"),
azureanthropic.WithAPIVersion("2024-10-21"),
)
if err != nil {
log.Fatal(err)
}

For AAD-bearer auth instead:

p, err := azureanthropic.New(
llmrouter.WithBaseURL("https://my-resource.services.ai.azure.com"),
azureanthropic.WithAPIVersion("2024-10-21"),
azureanthropic.WithAADToken(func(ctx context.Context) (string, error) {
// Implement caching + refresh in your token source.
tok, err := myAzureCredential.GetToken(ctx, policy.TokenRequestOptions{
Scopes: []string{"https://cognitiveservices.azure.com/.default"},
})
if err != nil {
return "", err
}
return tok.Token, nil
}),
)

Capabilities

Because the body and SSE event stream are identical to direct Anthropic, every feature of the Anthropic provider works without code changes:

  • Typed tool use via ChatRequest.Tools and translation to tool_use / tool_result blocks.
  • Extended thinking deltas for Claude 3.7+ (surfaced via Choice.Delta.Thinking).
  • Prompt caching via Message.CacheControl.
  • Multipart content (text + image blocks) via ImageURLContent / ImageBytesContent.
  • Mid-stream error surfacing on Stream.Err().
  • Structured outputs via forced tool-use (set ChatRequest.ResponseSchema — see Structured outputs on Anthropic).
  • Tool-result messages via llmrouter.ToolResultMessage(toolCallID, content).

Full example

package main
import (
"context"
"fmt"
"log"
"os"
"github.com/elloloop/llmrouter"
"github.com/elloloop/llmrouter/providers/azureanthropic"
)
func main() {
p, err := azureanthropic.New(
llmrouter.WithAPIKey(os.Getenv("AZURE_FOUNDRY_KEY")),
llmrouter.WithBaseURL(os.Getenv("AZURE_FOUNDRY_ENDPOINT")),
azureanthropic.WithDeployment("claude-3-5-sonnet"),
azureanthropic.WithAPIVersion("2024-10-21"),
)
if err != nil {
log.Fatal(err)
}
stream, err := p.CompletionStream(context.Background(), llmrouter.ChatRequest{
Model: "claude-3-5-sonnet-20241022",
MaxTokens: 1024,
Messages: []llmrouter.Message{
llmrouter.TextMessage("system", "You are concise."),
llmrouter.TextMessage("user", "In one sentence, what is Azure AI Foundry?"),
},
})
if err != nil {
log.Fatal(err)
}
for chunk := range stream.Chunks() {
for _, c := range chunk.Choices {
fmt.Print(c.Delta.Content)
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
fmt.Println()
}

Error handling

Non-2xx responses surface as *llmrouter.ErrUpstream with Provider == "azureanthropic". Common cases:

  • 401 — missing or wrong api-key / expired AAD token.
  • 404 — deployment not found, or wrong API version for this Foundry region.
  • 400 — Azure content-filter trigger, or malformed body (the body shape must match Anthropic /v1/messages; check MaxTokens).
  • 429 — Foundry capacity throttle; honour Retry-After.

Caveats

  • Translation code is duplicated. The body and SSE translation lives in providers/azureanthropic/translation.go as a byte-identical copy of the logic in providers/anthropic. This is intentional — Foundry's transport layer is different but the wire protocol is the same, and keeping the translation here removes the cross-package coupling. The source file documents this with a sync target comment; if you find a wire-level fix in providers/anthropic, mirror it here too.
  • Pin api-version. Azure rolls api-version forward and old values eventually 404. Pin to a known-good value and bump deliberately.
  • No x-api-key header. Direct Anthropic uses x-api-key; Foundry uses api-key. The provider sets the right one — you do not need to do this yourself.
  • Auth is mutually exclusive. Providing both an API key and an AAD source — or neither — fails fast in New().

When to use this vs other Anthropic surfaces

  • Use azureanthropic when your organisation has standardised on Azure AI Foundry (single billing surface, Azure AAD for auth, regional data-residency requirements that need Microsoft as the data processor).
  • Use direct Anthropic when you want the fastest path to the latest model variants and you don't need an Azure billing path. Anthropic ships new Claude variants on api.anthropic.com first; Foundry lags by days to weeks.
  • Use Bedrock when you're on AWS and want a single IAM-based auth story shared with the rest of your AWS workloads.
  • Use vertexanthropic when you're on GCP and want ADC-based auth alongside the rest of your Vertex usage.

See also