Mizan

Quickstart

One Mizan key, one endpoint, every supported provider. This gets you from zero to a working call in a few minutes. For the concepts behind wallet vs. BYOK routing, see BYOK.

1. Get a Mizan key

Sign up and create a key from Mizan Keys. It looks like mizan_rt_... and is shown once — store it like any other API secret.

2. Fund it

Top up your wallet to pay Mizan directly, or connect BYOK for providers where you already have your own account — Mizan detects it automatically per request, no code changes needed either way.

3. Make a request

One endpoint for every provider. Prefix the model with the provider, separated by / — e.g. openai/gpt-4o. The request and response bodies are OpenAI chat-completion shaped, so the OpenAI SDK works as-is with the base URL swapped.

Base URLhttps://api.app-mizan.comEndpointPOST /v1/chat/completionsAuthAuthorization: Bearer mizan_rt_...
cURL
curl https://api.app-mizan.com/v1/chat/completions \
  -H "Authorization: Bearer mizan_rt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o",
    "messages": [{ "role": "user", "content": "Say hi in five words." }]
  }'
TypeScript (OpenAI SDK)
import OpenAI from "openai"

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

const completion = await mizan.chat.completions.create({
  model: "anthropic/claude-sonnet-4-5",
  messages: [{ role: "user", content: "Say hi in five words." }],
})

console.log(completion.choices[0].message.content)
Python (OpenAI SDK)
from openai import OpenAI

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

completion = mizan.chat.completions.create(
    model="gemini/gemini-2.5-flash",
    messages=[{"role": "user", "content": "Say hi in five words."}],
)

print(completion.choices[0].message.content)

4. Request parameters

The request body accepts the standard OpenAI chat-completion fields — model, messages, max_tokens, temperature, stream, and so on — and passes them through to whichever provider serves the request.

One exception: some models spend part of max_tokens on internal reasoning before producing any visible output, so a small explicit value can come back with empty content. If you set max_tokens below 2048, Mizan raises it to 2048 so this never happens silently. Leaving max_tokens unset is unaffected — every provider's own default already leaves enough room.

The other direction: every model has its own maximum output — Mizan has seen real values from 8,192 to 196,608 tokens across the catalog. Check a model's exact limit via GET /v1/models/{provider}/{model} (the maxCompletionTokens field, also shown on the model's Models page). If your max_tokens exceeds it, Mizan reduces it to the model's limit rather than forwarding a value the provider would reject — you don't need to discover the ceiling by triggering an error first. When this happens, the response carries an X-Mizan-Warning header explaining what changed. That header is also used for other request adjustments Mizan makes on your behalf — see the Qwen note below.

5. Streaming

Set stream: true and Mizan streams back OpenAI-shaped SSE chunks as they arrive from the provider. The connection simply closes when the response is done.

cURL — streaming
curl https://api.app-mizan.com/v1/chat/completions \
  -H "Authorization: Bearer mizan_rt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o",
    "stream": true,
    "messages": [{ "role": "user", "content": "Count to five." }]
  }'

Model catalog & pricing

GET /v1/models returns every model Mizan can actually route to right now, each with its per-token pricing — no need to guess a model ID and see if it 400s. The active set per provider is resolved dailyfrom upstream and can change (a provider retiring or renaming a model, for example) — this endpoint, not a hardcoded list in a blog post or this doc, is the source of truth for what's callable right now. The response shape follows OpenAI's /v1/models convention (mizan.models.list() via the OpenAI SDK works as-is), with pricing added per model.

cURL
curl https://api.app-mizan.com/v1/models \
  -H "Authorization: Bearer mizan_rt_..."
Response (abridged)
{
  "object": "list",
  "data": [
    {
      "id": "deepseek/deepseek-v4-pro",
      "object": "model",
      "created": 1755000000,
      "owned_by": "deepseek",
      "name": "DeepSeek V4 Pro",
      "description": "...",
      "context_length": 131072,
      "input_modalities": ["text"],
      "output_modalities": ["text"],
      "pricing": { "prompt_per_m_usd": 0.55, "completion_per_m_usd": 2.19 }
    }
  ]
}

Supported providers

12 providers, one endpoint — all reachable today. Query GET /v1/models above (or browse Models in the dashboard) for the exact, currently-active model IDs per provider — several providers below only accept a small set of current model names (not every common alias you might expect), so check the catalog rather than guessing.

The prefix below is the onlyspelling Mizan accepts, too — it's not always the same string other gateways or the provider's own docs use (e.g. Z.AI is zai/, not z-ai/).

PrefixExample model
openai/openai/gpt-4o
anthropic/anthropic/claude-sonnet-4-5
gemini/gemini/gemini-2.5-flash
mistral/mistral/mistral-large-latest
grok/grok/grok-4
deepseek/deepseek/deepseek-chat
moonshot/moonshot/kimi-k2
qwen/qwen/qwen3-235b-a22b
minimax/minimax/minimax-m1
cerebras/cerebras/llama-3.3-70b
zai/zai/glm-4.6
meta/meta/muse-spark-1.1

Provider-specific notes

Mizan passes request fields through as-is in almost every case. The exceptions, where a provider's own requirements force Mizan to adjust something on your behalf:

Qwen: enable_thinking and reasoning_effort disable prompt caching

Qwen does not support prompt caching at all on a request with thinking enabled — an identical prompt prefix that caches thousands of tokens with thinking off caches zero tokens with it on. Separately, Qwen rejects reasoning_effort outright unless enable_thinking is true — and many client SDKs (Claude-family clients in particular) send reasoning_effort on every request by default, whether or not you asked for it. If your request sets reasoning_effort without an explicit enable_thinking, Mizan forces thinking on so the call doesn't 400 — and the response carries an X-Mizan-Warning header telling you caching was skipped as a result. If you explicitly set enable_thinking: false, Mizan honors that and drops reasoning_effort instead (also warned). To get caching on Qwen, don't send reasoning_effort and set enable_thinking: false explicitly.

Image generation

Google's Gemini image models (gemini/gemini-2.5-flash-image, gemini/gemini-3.1-flash-image, gemini/gemini-3.1-flash-image-preview, gemini/gemini-3.1-flash-lite-image, gemini/gemini-3-pro-image, and gemini/gemini-3-pro-image-preview) can return a generated image through the same /v1/chat/completions endpoint — no separate image endpoint. Ask for one in plain language; the image comes back inline as a content part on the assistant message, alongside any text the model also generated.

cURL
curl https://api.app-mizan.com/v1/chat/completions \
  -H "Authorization: Bearer mizan_rt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini/gemini-3.1-flash-image",
    "messages": [{ "role": "user", "content": "A watercolor fox reading a book." }]
  }'
Response (abridged)
{
  "id": "gemini-",
  "object": "chat.completion",
  "model": "gemini/gemini-3.1-flash-image",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": [
        { "type": "text", "text": "Here's your watercolor fox." },
        { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } }
      ]
    },
    "finish_reason": "stop"
  }],
  "usage": { "prompt_tokens": 12, "completion_tokens": 1290, "total_tokens": 1302, "cost": 0.000811 }
}
  • Images are billed as tokens, same as text — Gemini counts a generated image as a fixed number of output tokens, so there's no separate per-image charge and usage.cost works exactly as it does for any other model.
  • Images are returned inline only, as a base64 data:URI — Mizan doesn't host or store generated images anywhere, so there's nothing to clean up and no signed URL to expire.
  • stream: trueis supported, but since image generation isn't incremental, the whole response (text and image together) arrives as a single SSE chunk rather than token-by-token deltas.
  • Image input must be a base64 data: URI too — a remote http(s) image URL is rejected with a 400. tools aren't supported on these models either.
  • These models aren't reachable through /v1/messages — use /v1/chat/completions for image generation.

OpenAI's dedicated image models (openai/gpt-image-1 and openai/gpt-image-2) work the same way — same endpoint, same inline base64 content-part response — with a few real differences from Gemini's image models:

cURL
curl https://api.app-mizan.com/v1/chat/completions \
  -H "Authorization: Bearer mizan_rt_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-image-1",
    "size": "1024x1024",
    "quality": "auto",
    "messages": [{ "role": "user", "content": "A watercolor fox reading a book." }]
  }'
Response (abridged)
{
  "id": "img-",
  "object": "chat.completion",
  "model": "openai/gpt-image-1",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": [
        { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } }
      ]
    },
    "finish_reason": "stop"
  }],
  "usage": { "prompt_tokens": 15, "completion_tokens": 1120, "total_tokens": 1135, "cost": 0.04495 }
}
  • Still billed per token, but at a rate specific to generated images — separate from (and higher than) the model's ordinary completion-token rate. Check Models for the current per-model rate rather than assuming it matches text pricing.
  • Optional top-level size, quality, and n fields on the request body — default "1024x1024", "auto", and 1 respectively. n is capped at 4 per call; a higher value is silently reduced, with the adjustment noted in the X-Mizan-Warning header.
  • Image input (edits/references) isn't supported yet — a request with an image_url content part is rejected with a 400 rather than silently ignored.

Wallet & usage

Every chat completion response includes its own cost, so you can attribute spend per request without a separate lookup. Non-streaming responses carry it in usage.cost (USD) and the x-mizan-cost-usd response header, plus usage.balance_after_usd / x-mizan-balance-after-usd when the call was billed to your Mizan wallet (not BYOK). Streaming responses append one extra SSE chunk carrying usage.cost right before [DONE]— same shape as OpenAI's own stream_options.include_usage chunk, so it's safe for any client already handling that.

Non-streaming response (abridged)
{
  "id": "chatcmpl-...",
  "model": "openai/gpt-4o",
  "choices": [ /* ... */ ],
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 18,
    "total_tokens": 60,
    "cost": 0.000267,
    "balance_after_usd": 47.499733
  }
}

For account-level and historical views: GET /v1/mereturns this key's identity, limits, and current wallet balance; GET /v1/wallet returns the wallet balance plus recent top-ups; and GET /v1/usage returns spend aggregated by provider and model over a days window (7, 30, or 90 — 30 by default).

cURL — usage
curl "https://api.app-mizan.com/v1/usage?days=30" \
  -H "Authorization: Bearer mizan_rt_..."

Errors

Mizan-originated errors use one shape:

{
  "error": {
    "message": "...",
    "type": "mizan_gateway_error"
  }
}
400Missing model field, a model string not prefixed with a supported provider (e.g. missing the "openai/" part), 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 per provider before guessing; the active set is resolved daily from upstream and can change.
429Rate limit exceeded for this key (300 requests/minute by default).
502The upstream provider request itself failed (network/timeout) — not a provider-returned error, which is forwarded as-is.
503That provider isn't configured on this deployment yet.

Compared to OpenRouter

Mizan is a much smaller surface today: no dedicated client SDK (the OpenAI SDK works as a compatible client, and so does the Anthropic SDK), no agent SDK, no MCP integration. Model routing and fallbacks are supported — see Auto Router and Model Fallbacks. Providers that speak an OpenAI-compatible API (OpenAI, Mistral, xAI, Gemini, DeepSeek, Moonshot, Qwen, MiniMax, Cerebras, Z.AI) get full request/response fidelity. Anthropic goes through a translation layer — text chat and tool calling are both fully normalized; vision isn't yet, so verify before relying on it.