Mizan

Anthropic Messages API

Point the native Anthropic SDK straight at Mizan — same key, same wallet, same 11 providers as /v1/chat/completions, reached through Anthropic's own request/response/SSE shape instead of OpenAI's. No translating proxy required.

Make a request

Get a key from Mizan Keys and fund your wallet exactly as in the Quickstart. Auth uses x-api-key — the Anthropic SDK's default header — so a plain baseURL override is all that's needed. Authorization: Bearer mizan_rt_... also works, on this endpoint and on /v1/chat/completions.

Base URLhttps://api.app-mizan.comEndpointPOST /v1/messagesAuthx-api-key: mizan_rt_...

One real difference from /v1/chat/completions: max_tokens is required on every request, matching the native Anthropic Messages API.

cURL
curl https://api.app-mizan.com/v1/messages \
  -H "x-api-key: mizan_rt_..." \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "messages": [{ "role": "user", "content": "Say hi in five words." }]
  }'
TypeScript (@anthropic-ai/sdk)
import Anthropic from "@anthropic-ai/sdk"

const mizan = new Anthropic({
  baseURL: "https://api.app-mizan.com",
  apiKey: process.env.MIZAN_KEY, // "mizan_rt_..."
})

const message = await mizan.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Say hi in five words." }],
})

console.log(message.content)
Python (anthropic)
from anthropic import Anthropic

mizan = Anthropic(
    base_url="https://api.app-mizan.com",
    api_key=os.environ["MIZAN_KEY"],  # "mizan_rt_..."
)

message = mizan.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Say hi in five words."}],
)

print(message.content)

Model routing

A bare model id (no provider prefix) — exactly what the Anthropic SDK sends by default — defaults to the anthropic provider, as in the examples above. Prefix the model with any other supported provider to route through the Anthropic wire format to a different one — the response still comes back Anthropic-shaped either way.

cURL — explicit provider
curl https://api.app-mizan.com/v1/messages \
  -H "x-api-key: mizan_rt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.1",
    "max_tokens": 1024,
    "messages": [{ "role": "user", "content": "Say hi in five words." }]
  }'

auto and mizan/switchboard work here too, exactly as documented for Auto Router and Switchboard.

Streaming

Set stream: true and Mizan streams real Anthropic SSE events — message_start, content_block_start/delta/stop, message_delta, message_stop— no matter which underlying provider serves the request. The Anthropic SDK's own stream helpers work unmodified.

cURL — streaming
curl https://api.app-mizan.com/v1/messages \
  -H "x-api-key: mizan_rt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "stream": true,
    "messages": [{ "role": "user", "content": "Count to five." }]
  }'

Tool use

tools and tool_choice are fully supported, both streaming and non-streaming — tool_use blocks in a response, and tool_result blocks on a follow-up turn. This works whether the request routes to Anthropic or to any other provider through this endpoint.

Request body
{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get the current weather for a city.",
      "input_schema": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  ],
  "messages": [{ "role": "user", "content": "What's the weather in Beirut?" }]
}

Cost & balance

Every response carries the resolved provider/model slug and per-call cost — same convention as /v1/chat/completions, just inside the standard Anthropic usage object instead of OpenAI's. The same X-Mizan-Cost-Usd/X-Mizan-Balance-After-Usd response headers are set too.

Response (non-streaming)
{
  "id": "msg_...",
  "type": "message",
  "role": "assistant",
  "model": "anthropic/claude-sonnet-4-5",
  "content": [{ "type": "text", "text": "Hi there, hope you're well!" }],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 12,
    "output_tokens": 9,
    "cost": 0.000267,
    "balance_after_usd": 47.499733
  }
}

Known limitations

Image content blocks (vision) and extended-thinking blocks aren't supported yet — a request containing either is rejected with a 400 rather than silently dropped, so you're never billed for content that wasn't actually forwarded. Your own cache_control hints are accepted but not preserved through the translation — Mizan applies its own automatic caching on top of your system prompt and tools regardless, see Prompt caching. Gemini's image-generation models are also rejected with a 400 here — they're only reachable through /v1/chat/completions.

Errors

Errors use Anthropic's own envelope shape — the same HTTP status codes documented for /v1/chat/completions, just wrapped differently so the Anthropic SDK's typed exceptions resolve correctly.

Error shape
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "..."
  }
}
CodeWhen
400Missing model/max_tokens, an unsupported content block (image, thinking, etc.), or an invalid "models" fallback list.
401Missing, invalid, or revoked Mizan key.
402Wallet balance is insufficient, or the wallet is frozen (top up to resume).
403This key's own usage limit has been reached (independent of wallet balance).
422The requested model has no pricing configured for it — check GET /v1/models for the current active model IDs before guessing.
429Rate limit exceeded for this key (300 requests/minute by default).
502The upstream provider request itself failed (network/timeout).
503That provider isn't configured on this deployment yet.