Vercel AI SDK
The Vercel AI SDK’s createOpenAICompatible adapter connects to any OpenAI-compatible endpoint, including MirAPI, and streamText consumes the SSE stream. You only change the base URL, the key, and the model.
What you need
Section titled “What you need”| Element | Value |
|---|---|
| Base URL | https://api.mirapi.ai/v1 |
| API key | MIRAPI_API_KEY |
| Model | deepseek-chat |
Install
Section titled “Install”Install the core SDK and the OpenAI-compatible adapter:
npm install ai @ai-sdk/openai-compatibleConfigure
Section titled “Configure”Then create the provider pointing at MirAPI:
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";import { streamText } from "ai";
const mirapi = createOpenAICompatible({ name: "mirapi", baseURL: "https://api.mirapi.ai/v1", apiKey: process.env.MIRAPI_API_KEY!,});The adapter options are:
| Option | Purpose |
|---|---|
name |
Provider label used to build model handles such as mirapi("deepseek-chat") |
baseURL |
https://api.mirapi.ai/v1 — the adapter appends /chat/completions itself |
apiKey |
Your MirAPI key, typically from process.env.MIRAPI_API_KEY |
Verify with a message
Section titled “Verify with a message”const result = streamText({ model: mirapi("deepseek-chat"), prompt: "Reply with exactly: connected to MirAPI",});
for await (const textPart of result.textStream) { process.stdout.write(textPart);}The streamed output should end with connected to MirAPI.
Tool calling
Section titled “Tool calling”Pass tools in the streamText options using the OpenAI tools/tool_choice format. The upstream model must support tool calling — check the capability flags in the catalogue.
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";import { streamText } from "ai";import { z } from "zod";
const mirapi = createOpenAICompatible({ name: "mirapi", baseURL: "https://api.mirapi.ai/v1", apiKey: process.env.MIRAPI_API_KEY!,});
const result = streamText({ model: mirapi("deepseek-chat"), prompt: "What is the weather in Paris?", tools: { getWeather: { description: "Get the current weather for a city", parameters: z.object({ city: z.string().describe("City name"), }), execute: async ({ city }) => `Sunny, 18°C in ${city}`, }, },});
for await (const textPart of result.textStream) { process.stdout.write(textPart);}Streaming
Section titled “Streaming”Streaming is standard SSE — data: lines terminated by data: [DONE] — and streamText maps it to text parts without special flags. With reasoning models, the reasoning content arrives before the answer. Non-streaming usage reports token counts only, never a dollar amount.
Protocol variants
Section titled “Protocol variants”MirAPI speaks three protocols natively, and one key covers all of them:
| Protocol | Base URL | Authentication |
|---|---|---|
| OpenAI-compatible | https://api.mirapi.ai/v1 |
Authorization: Bearer |
| Anthropic | https://api.mirapi.ai |
x-api-key or Authorization: Bearer |
| Gemini | https://api.mirapi.ai or https://api.mirapi.ai/v1beta |
x-goog-api-key or ?key= |
createOpenAICompatible uses the OpenAI protocol. The Anthropic protocol has its own SDK, covered in Anthropic SDK; the Gemini protocol is documented in Configuration.
Errors and troubleshooting
Section titled “Errors and troubleshooting”| Status | Meaning | What to do |
|---|---|---|
401 |
Key missing, invalid, or unknown | Fix your credentials |
403 |
Key valid but rejected (balance, whitelist, quota, IP) | Resolve the cause, then retry |
413 |
Payload too large | Reduce the request size |
429 |
Rate limited | Back off; no Retry-After header is sent |
500 |
Server error | Safe to retry idempotent requests |
A 403 with a valid key means the model is not whitelisted or the balance is exhausted — top up and retry. Every error message ends with a request ID; include it in support tickets. See Errors.
Billing
Section titled “Billing”Chat is billed per token, in three tiers per 1M tokens (input, output, cache read). Reasoning tokens are billed as output, and cache reads cost less than fresh input. The balance is prepaid and never expires; when it runs out, requests return 403 and recover immediately after you top up. Reconcile cost in the console billing log or GET /api/log/token — see Billing.
Troubleshooting
Section titled “Troubleshooting”- 401? The key is missing, invalid, or unknown — verify it and retry.
- 403 on a request that used to work? Check the balance and the key constraints (whitelist, quota, IP). Topping up restores the key immediately.
- 429? Back off with jittered exponential delay (1s → 2s → 4s, capped around 30s); no
Retry-Afterheader is sent. - Model not found? Use the exact
idfromGET /v1/modelsand drop anyvendor/prefix.