Migration Guide
Move an existing OpenAI, Anthropic, or OpenRouter integration to Yunxin — usually just a base URL and key change.
Yunxin speaks the wire formats you already use. For most codebases, migrating is a two-line change:
point your SDK at https://api.yuhuanstudio.com/v1 and swap in a Yunxin API key. Your existing request/response handling,
streaming loops, and tool-calling code keep working.
Before you start
- Get a key. Create one in the Dashboard → API Keys. Yunxin keys begin with
sk-(see Authentication). - Pick the closest format. Yunxin natively supports three request formats — keep using the one that matches your current code so the migration stays a config-only change:
| Your current API | Keep using | Yunxin endpoint |
|---|---|---|
| OpenAI Chat Completions | OpenAI SDK (Chat) | POST /v1/chat/completions |
| OpenAI Responses | OpenAI SDK (Responses) | POST /v1/responses |
| Anthropic Messages | Anthropic SDK | POST /v1/messages |
- Note the base URL difference between SDKs. The OpenAI SDK expects the
/v1suffix; the Anthropic SDK appends/v1/messagesitself, so its base URL has no/v1:
| SDK | base_url |
|---|---|
| OpenAI | https://api.yuhuanstudio.com/v1 |
| Anthropic | https://api.yuhuanstudio.com |
Model ids on Yunxin can be written as provider/model (for example openai/gpt-4o,
anthropic/claude-..., zai/glm-...) to pin a specific provider, or left to auto-routing. Discover
the exact ids and per-model capabilities with GET /v1/models — don't hardcode a
list that goes stale.
From OpenAI
Change the base_url and api_key. Everything else — Chat Completions, streaming, function calling,
vision, embeddings, image/audio generation, files, and batches — works unchanged.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["YUNXIN_API_KEY"], # an sk-... key
base_url="https://api.yuhuanstudio.com/v1", # was https://api.openai.com/v1
)
response = client.chat.completions.create(
model="model-id",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.YUNXIN_API_KEY, // an sk-... key
baseURL: "https://api.yuhuanstudio.com/v1", // was https://api.openai.com/v1
});
const response = await client.chat.completions.create({
model: "model-id",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);curl https://api.yuhuanstudio.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $YUNXIN_API_KEY" \
-d '{"model": "model-id", "messages": [{"role": "user", "content": "Hello!"}]}'Switching via environment variables
If your code reads the standard OpenAI environment variables, you can migrate without touching the source at all:
# Before (direct OpenAI)
export OPENAI_API_KEY="sk-..."
export OPENAI_BASE_URL="https://api.openai.com/v1"
# After (Yunxin) — still an sk-... key, just a different base URL
export OPENAI_API_KEY="sk-your-yunxin-key"
export OPENAI_BASE_URL="https://api.yuhuanstudio.com/v1"Responses API
If you already use OpenAI's Responses API, keep using it — Yunxin implements POST /v1/responses
natively, including previous_response_id for server-side multi-turn and the built-in tool list at
GET /v1/responses/tools. See Responses API.
From Anthropic
Keep the Anthropic SDK and the Messages format. Set the base URL to https://api.yuhuanstudio.com without
/v1 — the SDK appends /v1/messages for you.
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["YUNXIN_API_KEY"], # an sk-... key
base_url="https://api.yuhuanstudio.com", # no /v1 — the SDK adds /v1/messages
)
message = client.messages.create(
model="model-id",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
print(message.content[0].text)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.YUNXIN_API_KEY, // an sk-... key
baseURL: "https://api.yuhuanstudio.com", // no /v1 — the SDK adds /v1/messages
});
const message = await client.messages.create({
model: "model-id",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello!" }],
});
console.log(message.content[0].text);Extended thinking, vision/PDF input, tool use, and prompt caching all pass through. The optional
anthropic-version header is accepted. See Messages API.
You aren't locked to one format per model. Yunxin converts between formats when a provider doesn't
natively support the one you sent — for example, a Messages request to a Chat-Completions-only provider
is transparently converted, including document blocks to file_url content parts.
From OpenRouter
OpenRouter users are already accustomed to provider/model ids and a single key fronting many
providers — both carry over directly. Point the OpenAI SDK at https://api.yuhuanstudio.com/v1 with your sk- key:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["YUNXIN_API_KEY"],
base_url="https://api.yuhuanstudio.com/v1", # was https://openrouter.ai/api/v1
)
response = client.chat.completions.create(
model="provider/model", # same provider/model convention you already use
messages=[{"role": "user", "content": "Hello!"}],
)As a migration nicety, Yunxin also exposes OpenRouter-shaped usage endpoints (GET /v1/credits,
GET /v1/key, GET /v1/activity) so dashboards and CLIs you already point at OpenRouter keep working
unchanged. See Usage Monitoring Tools.
From other OpenAI-compatible providers
Any provider that exposes an OpenAI-compatible Chat Completions API migrates the same way: change
api_key and base_url, and keep your model-id (use the provider/model form to pin a provider).
What you gain
| Direct provider | Yunxin | |
|---|---|---|
| Keys to manage | One per provider | One key for every provider |
| Switching models | Code/SDK change per provider | Change the model id string |
| Reliability | Manual failover | Automatic fallback & routing |
| Billing | Separate bills | Unified credit balance |
| Usage tracking | Per-provider dashboards | One analytics surface + tool-compatible shells |
After migrating
- Billing switches to Yunxin's credit system: 1 credit = $0.001 USD, drawn down
per request based on the model's per-token prices. A
402 insufficient_balanceerror means you need to top up. - Review Error Handling — the error envelope uses
error.code, which differs slightly from each upstream provider's shape. - Tune retries to honor
Retry-Afteron429responses; see Rate Limits.
How is this guide?