Errors
HTTP status codes, error envelope shape, and how to recover from each failure mode.
Every error returns the OpenAI-shape envelope so existing client retry logic just works.
Status codes
| Status | Code | When it fires |
|---|---|---|
401 | invalid_api_key | Missing, malformed, or revoked key. |
400 | model_not_found | model ≠ deepseek-v3.2. Mikan is hard-pinned. |
402 | insufficient_balance | Wallet < $0.01. Top up at /dashboard/billing. |
429 | rate_limit_exceeded | Per-key sliding window cap (default 60 rpm). |
502 | upstream_error | DeepSeek upstream returned 5xx — Mikan retries once, then surfaces. |
Envelope shape
Every non-2xx response uses this body, even when streaming hasn't begun:
{
"error": {
"message": "Your balance is too low to make this request.",
"type": "insufficient_balance",
"param": null,
"code": "insufficient_balance"
}
}If you're using the OpenAI Python or Node SDK, this is parsed for you — try/except openai.APIError catches everything below.
Recovery patterns
- 1
401
- 2
Rotate the key in Dashboard → API Keys. The old hash is invalidated server-side; no further requests with the old secret will succeed.
- 3
402
- 4
Open Dashboard → Billing and top up. Stripe Checkout completes in seconds; the webhook credits your wallet within 1–2 s. Resume requests with no client changes.
- 5
429
- 6
Back off exponentially. The
Retry-Afterheader tells you the seconds remaining in the current window. If you genuinely need more headroom, email support — verified accounts get 600 rpm. - 7
502
- 8
Mikan retries upstream errors once internally before surfacing. If you see this, DeepSeek is having a bad minute — retry after 30 s. We track upstream availability on a status page (coming soon).