First integration
What you’ll build: one Python file that sends a chat request with the OpenAI SDK, then streams the same request token by token. Along the way you’ll see the two other protocols MirAPI speaks, how to read errors, and how to check what a call cost.
Prerequisites
Section titled “Prerequisites”- Python 3.10+
pip install openai— the official OpenAI SDK, used against MirAPI’s OpenAI-compatible endpoint- A
MIRAPI_API_KEYcreated in the console: https://console.mirapi.ai → API Keys. The full key is shown only once, so copy it into your environment or a secret manager immediately.
pip install openaiexport MIRAPI_API_KEY=sk-...1. Send your first request
Section titled “1. Send your first request”Create chat.py and point the SDK at MirAPI’s OpenAI-compatible base URL, https://api.mirapi.ai/v1:
import osfrom openai import OpenAI
client = OpenAI( base_url="https://api.mirapi.ai/v1", api_key=os.environ["MIRAPI_API_KEY"],)
response = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello! Introduce yourself in one sentence."}],)
print(response.choices[0].message.content)Run it:
python chat.pyYou get a plain text response. Its usage object reports token counts only — the dollar amount is never included; you reconcile it in the console or via the usage endpoints below.
The fields you’ll touch most often:
| Parameter | What it does |
|---|---|
model |
The exact catalogue id to call — here deepseek-chat |
messages |
The conversation as role / content pairs |
stream |
Set true to receive SSE deltas instead of one response |
max_tokens / max_completion_tokens |
Cap on generated tokens |
temperature |
Sampling randomness, 0–2 |
response_format |
Structured output (JSON mode / json_schema) — JSON-capable models only |
reasoning_effort |
Reasoning strength — reasoning-capable models only |
tools / tool_choice |
Tool calling — tool-capable models only |
Capability-gated parameters (tools, response_format, reasoning_effort) only work on models whose catalogue entry lists that capability under supported_parameters.
2. Stream the response
Section titled “2. Stream the response”Change only the call — add stream=True and iterate the deltas:
import osfrom openai import OpenAI
client = OpenAI( base_url="https://api.mirapi.ai/v1", api_key=os.environ["MIRAPI_API_KEY"],)
stream = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello! Introduce yourself in one sentence."}], stream=True,)
for chunk in stream: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True)print()Text now prints as it is generated. Streaming is standard SSE — data: lines ending with the data: [DONE] sentinel — and the SDK parses it for you: no special flags or custom parsing. On reasoning models, thinking tokens arrive before the final answer.
3. Other protocols
Section titled “3. Other protocols”MirAPI exposes three protocol front-ends to the same catalogue. Use whichever your client already speaks; the model name is the same catalogue id.
| Protocol | Base URL | How the key is sent |
|---|---|---|
| OpenAI-compatible | https://api.mirapi.ai/v1 |
Authorization: Bearer sk-... |
| Anthropic | https://api.mirapi.ai (no /v1) |
x-api-key: sk-... (or Authorization: Bearer) |
| Gemini | https://api.mirapi.ai or https://api.mirapi.ai/v1beta |
x-goog-api-key: sk-... (or ?key=sk-...) |
The OpenAI-compatible base URL already ends in /v1, so the SDK appends /chat/completions. The Anthropic client points at the bare origin and appends /v1/messages itself. Gemini clients accept either the bare origin or /v1beta.
OpenAI-compatible
Section titled “OpenAI-compatible”curl https://api.mirapi.ai/v1/chat/completions \ -H "Authorization: Bearer $MIRAPI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"Hi"}]}'Anthropic
Section titled “Anthropic”curl https://api.mirapi.ai/v1/messages \ -H "x-api-key: $MIRAPI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"deepseek-chat","max_tokens":256,"messages":[{"role":"user","content":"Hi"}]}'Gemini
Section titled “Gemini”curl "https://api.mirapi.ai/v1beta/models/deepseek-chat:generateContent" \ -H "x-goog-api-key: $MIRAPI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"contents":[{"parts":[{"text":"Hi"}]}]}'Full header details and key constraints live in Authentication.
4. Handle errors
Section titled “4. Handle errors”| Status | Meaning | What to do |
|---|---|---|
| 401 | Key missing, invalid, or unknown | Fix the credential |
| 403 | Key valid, request rejected — balance, whitelist, quota, or IP | Read the message, fix the cause, retry |
| 413 | Request body too large | Reduce the size |
| 429 | Rate limited | Retry with backoff — there is no Retry-After header |
| 500 | Gateway or upstream failure | Safe to retry idempotent requests |
An empty balance returns 403, not 401. OpenAI-protocol errors use the envelope {"error": {"message", "type", "param", "code"}}; type is new_api_error (gateway-side) or upstream_error (sanitized upstream failure).
{"error": {"message": "…", "type": "new_api_error", "param": null, "code": null}}Every error message ends with a request ID — include it in any support or billing ticket. MirAPI does not retry on your behalf; for 429s use jittered exponential backoff (1s → 2s → 4s, capped around 30s).
5. Check usage and spend
Section titled “5. Check usage and spend”The usage object in a non-streaming response counts tokens only — it carries no dollar amount. Reconcile spend in three places:
- the console billing log;
GET /api/usage/token— account-level summary;GET /api/log/token— per-request detail.
curl https://api.mirapi.ai/api/usage/token -H "Authorization: Bearer $MIRAPI_API_KEY"A short hello costs well under a cent — a few hundred tokens at the model’s per-1M-token rate. Text is billed per 1M tokens in three tiers (input / output / cache read); images are per image and video per second of output. Billing runs on a prepaid USD balance that never expires; when it empties, requests return 403 and the same key works again immediately after a top-up. See Billing & top-ups.
Troubleshooting
Section titled “Troubleshooting”- 401 on a fresh key — the environment variable isn’t set, or the key was copied incompletely. The
Bearerscheme is case-insensitive, and a bare key without the scheme also works. - 403 with a valid key — most often an empty balance, a model outside the key’s whitelist, a quota cap, or an IP restriction. Top up or adjust the key.
- A “coming soon” model errors — catalogue entries marked Coming soon can’t be called yet; pick one marked Available.
- Model not found — send the exact
idthe catalogue returns. Thevendor/prefix is for browsing only and is not part of a request. - Streaming prints nothing — you’re iterating
delta.content, which isNoneuntil text arrives (and reasoning arrives first on reasoning models). - 429 without
Retry-After— that’s expected; back off manually as described above.