FAQ
Getting started
Section titled “Getting started”Do I need a subscription?
Section titled “Do I need a subscription?”No. MirAPI is prepaid: you top up any amount and pay per request. There is no monthly fee and no minimum spend.
Which base URL should I use?
Section titled “Which base URL should I use?”It depends on the protocol your tool speaks:
| Protocol | Base URL |
|---|---|
| OpenAI-compatible | https://api.mirapi.ai/v1 |
| Anthropic | https://api.mirapi.ai |
| Gemini | https://api.mirapi.ai or https://api.mirapi.ai/v1beta |
Rule of thumb: if a tool appends paths like /chat/completions to the base URL itself, enter https://api.mirapi.ai/v1. Anthropic tools expect the bare origin https://api.mirapi.ai (no /v1).
Where do I find models?
Section titled “Where do I find models?”In the Platform catalog or via GET /v1/models. Each entry reports the model id/name (use the exact name in requests), context_length, pricing (USD per 1M tokens), supported_parameters, and output_modalities.
Catalog entries are Available (real channel pricing, callable now), Coming soon (released within the last 3 months, vendor list price, not callable yet), or Not supported (released more than 3 months ago and not offered on MirAPI, vendor list price for reference, not callable). A discount badge compares MirAPI’s combined input+output price against the vendor’s official price. It says nothing about quality.
Billing
Section titled “Billing”How is billing calculated?
Section titled “How is billing calculated?”You pay from a prepaid USD balance, with no subscription. Top-ups are converted to USD at the exchange rate on the day you pay, and that rate is locked in.
- Text and multimodal chat: per token, with input, output, and cache read billed separately.
- Image generation: per image.
- Video generation: per second of output.
Reasoning/thinking tokens are billed as output. Cache reads are cheaper than ordinary input. All prices are shown in the catalog and the Platform before you call.
Does my balance expire?
Section titled “Does my balance expire?”No. A prepaid balance never expires; it stays until you use it.
What happens when the balance runs out?
Section titled “What happens when the balance runs out?”Requests fail with 403 and a message that the balance is insufficient. Top up and the same key works again immediately.
Where can I see what I spent?
Section titled “Where can I see what I spent?”Three places show the same data: the Platform billing log, GET /api/usage/token (account-level summary), and GET /api/log/token (per-request detail).
A non-streaming response’s usage field reports token counts only and never includes a dollar amount. Dollar amounts come from the Platform and /api/log/token.
A request failed but was charged. What do I do?
Section titled “A request failed but was charged. What do I do?”Open a ticket with the request ID from the error or response. Support uses it to look up the exact request and its billing entry. Every error message ends with the request ID, so paste the whole message when filing a ticket.
How do I pay less for repeated prompts?
Section titled “How do I pay less for repeated prompts?”Use prompt caching. It is automatic on OpenAI-compatible models; on Anthropic-protocol requests, use cache_control. Keep your system prompt and shared context as a stable prefix so requests hit the cache; cache reads are billed at the model’s cache-read price. See Prompt caching.
API keys
Section titled “API keys”My key leaked. What now?
Section titled “My key leaked. What now?”Revoke it in the Platform and create a new one. Key changes take effect immediately, with no propagation delay.
Can I restrict a key to certain models?
Section titled “Can I restrict a key to certain models?”Yes. Set a model whitelist when creating the key. Calls outside the whitelist return 403.
What else can I control on a key?
Section titled “What else can I control on a key?”Each key can carry four kinds of limits:
- Model whitelist: models outside the whitelist return 403.
- Validity period: the key stops working after it expires.
- Quota: an upper bound on usage.
- IP allowlist: requests from other addresses are rejected.
MirAPI also accepts an OpenAI-Organization header but ignores it (there is no organization isolation).
Why do I get 401 instead of 403?
Section titled “Why do I get 401 instead of 403?”- 401: the key is missing, unknown, or invalid. Check that
MIRAPI_API_KEYis set, that the key has no trailing newline or spaces, and that you send it in the right header for your protocol. - 403: the key is valid but the request was rejected (insufficient balance, model whitelist, quota, or IP allowlist).
See Authentication for the headers each protocol expects.
Errors and retries
Section titled “Errors and retries”What should I do on 429?
Section titled “What should I do on 429?”Retry with exponential backoff. The gateway does not retry for you, and responses carry no Retry-After header. A jittered schedule such as 1s → 2s → 4s (capped around 30s) works well.
What about 413 and 500?
Section titled “What about 413 and 500?”- 413: the request body is too large; shrink it (fewer tokens, smaller attachment) and retry.
- 500: a server-side failure. Idempotent requests are safe to retry.
If you have set a backup channel, requests switch to it automatically when the primary channel fails, so a transient 500 does not always mean the model is down. The gateway never silently swaps in a different model or moves the request to a channel you didn’t choose. If both your primary and backup channels are unavailable, the request fails.
What does an error response look like?
Section titled “What does an error response look like?”OpenAI-protocol errors use an envelope with these fields:
| Field | Meaning |
|---|---|
message |
Human-readable error text; it ends with the request ID. |
type |
new_api_error (gateway side) or upstream_error (upstream failure, message sanitized). |
param |
The parameter that caused the error, when applicable. |
code |
A machine-readable error code. |
Anthropic-protocol errors use:
{ "type": "error", "error": { "type": "permission_error", "message": "..." }}Every error message ends with a request ID; include it in any support ticket.
Privacy
Section titled “Privacy”Where does my data go?
Section titled “Where does my data go?”Requests are forwarded to the model vendor, which sees whatever its model needs to process them. MirAPI does not train on your traffic and does not use your prompts for anything other than serving the request.
Do you log my prompts?
Section titled “Do you log my prompts?”Only the request metadata needed for billing (timestamp, model, token counts, cost) is retained in the log. When you send sensitive data, assume the upstream vendor can see your prompts.
MCP and agents
Section titled “MCP and agents”Do MCP servers work with MirAPI?
Section titled “Do MCP servers work with MirAPI?”Yes. MCP servers are tools that your agent (Claude Code, Cursor, Cline, …) invokes, and only the model API goes through MirAPI. Configure the agent with the MirAPI base URL and key as usual; its MCP tools keep working unchanged. See Claude Code or Cursor for the setup.
Redemption codes
Section titled “Redemption codes”How do I use a redemption code?
Section titled “How do I use a redemption code?”Enter it under Billing → Redeem in the Platform. The code’s value is added to your USD balance.
Video billing
Section titled “Video billing”How is video charged?
Section titled “How is video charged?”Per second of generated video output, billed when the job completes. A failed or canceled job is not charged.
Model names
Section titled “Model names”The catalogue shows deepseek/deepseek-chat but my call fails. Why?
Section titled “The catalogue shows deepseek/deepseek-chat but my call fails. Why?”The vendor/-prefixed name is a catalog-browsing ID. API calls use the bare name (for example deepseek-chat) exactly as returned by GET /v1/models.
How do I call Gemini image models (Nano Banana)?
Section titled “How do I call Gemini image models (Nano Banana)?”They are multimodal chat models, not image-endpoint models: send POST /v1beta/models/{model}:generateContent with generationConfig.responseModalities: ["TEXT", "IMAGE"], and the picture comes back as a base64 inlineData part. Image generation has the request and a script.