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.
https://api.app-mizan.comEndpointPOST /v1/chat/completionsAuthAuthorization: Bearer mizan_rt_...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." }]
}'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)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 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 https://api.app-mizan.com/v1/models \
-H "Authorization: Bearer mizan_rt_..."{
"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/).
| Prefix | Example 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 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." }]
}'{
"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.costworks 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 remotehttp(s)image URL is rejected with a 400.toolsaren't supported on these models either. - These models aren't reachable through /v1/messages — use
/v1/chat/completionsfor 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 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." }]
}'{
"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, andnfields on the request body — default"1024x1024","auto", and1respectively.nis capped at4per call; a higher value is silently reduced, with the adjustment noted in theX-Mizan-Warningheader. - Image input (edits/references) isn't supported yet — a request with an
image_urlcontent 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.
{
"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 "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"
}
}| 400 | Missing model field, a model string not prefixed with a supported provider (e.g. missing the "openai/" part), or an invalid "models" fallback list. |
| 401 | Missing, invalid, or revoked Mizan key. |
| 402 | Wallet balance is insufficient, or the wallet is frozen (top up to resume). |
| 403 | This key's own usage limit has been reached (independent of wallet balance). |
| 422 | The 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. |
| 429 | Rate limit exceeded for this key (300 requests/minute by default). |
| 502 | The upstream provider request itself failed (network/timeout) — not a provider-returned error, which is forwarded as-is. |
| 503 | That 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.