Skip to content
TutujinAPI

How to migrate from the official APIs or another OpenAI-compatible service

Moving to Tutujin API usually means changing base_url, api_key and the model names. Check the model list, streaming, error codes and rate limits before launch, then shift traffic in stages and keep the old service until things are stable. A checklist, percentage-based routing code and a rollback plan.

Moving to Tutujin API usually changes three things in your code: base_url becomes https://www.tutujin.com/v1, api_key becomes a key you create in the console, and model becomes a name from this site's model hub. The real work is the checking before launch and the switch after it: confirm the models with the models endpoint, check the differences in streaming, error codes and rate limits, then move traffic over in stages — a small share, half, then all of it. Keep the old service until the full rollout is stable, and keep the share in configuration so a rollback is just setting it back to 0.

Check before you migrate

What to confirmHow
The endpoints you use are supportedCompare with the table below; native APIs are available for the Claude and Gemini SDKs too
Your models exist and the names matchCall the models endpoint with the new key, as in step 2
You know what it will costLook up prices in Models and pricing; estimating is covered in How LLM API pricing works
You have picked a groupThe group decides the line and the price; see How to choose a group

Supported endpoints:

EndpointMethod and pathStatusModels
Chat CompletionsPOST/v1/chat/completionsVerified681
ResponsesPOST/v1/responsesRelayed as-is5
Claude MessagesPOST/v1/messagesVerified49
Gemini generateContentPOST/v1beta/models/{model}:generateContentVerified63
Image generationPOST/v1/images/generationsRelayed as-is54
Image editingPOST/v1/images/editsRelayed as-is31
EmbeddingsPOST/v1/embeddingsVerified18
RerankPOST/v1/rerankRelayed as-is6
Text to speechPOST/v1/audio/speechRelayed as-is8
Speech to textPOST/v1/audio/transcriptionsRelayed as-is1
MidjourneyPOST/mj/submit/imagineRelayed as-is13
Suno musicPOST/suno/submit/musicRelayed as-is2

"Verified" means we have made real calls and checked billing on this site. "Relayed as-is" means requests are forwarded in their original format; parameters follow the model's official documentation. Model counts are the models in the current catalog that support the endpoint.

Step 1: create a key and change the base URL

Create a key just for the migration on the API keys page of the console, with a spending cap and a group. Then change your configuration for the way you call the API today:

How you call the API nowWhat to changeNew value
OpenAI SDK, or another OpenAI-compatible servicebase_url / baseURL, or the OPENAI_BASE_URL environment variablehttps://www.tutujin.com/v1
Anthropic SDKbase_url / baseURLhttps://www.tutujin.com
Google Gen AI SDKbase_url in http_optionshttps://www.tutujin.com
Plain HTTP requestsThe full URLFor example https://www.tutujin.com/v1/chat/completions

Replace api_key with your key from this site in every case. Projects that read environment variables can switch without touching code:

cURL
export OPENAI_BASE_URL="https://www.tutujin.com/v1"
export OPENAI_API_KEY="sk-your-key"

The three header styles and full SDK setups are in Authentication and base URL.

Step 2: check the model names

Call the models endpoint with the new key; it returns exactly the models that key can use:

cURL
curl https://www.tutujin.com/v1/models \
  -H "Authorization: Bearer $TUTUJIN_API_KEY"

Match every model name your code uses against the response. The same model may be named differently on different platforms; this site's name is the one that works here, so map any mismatches in code. If a model is missing, the key's group most likely does not offer it — check the model's page for its groups, and see Models endpoint.

Step 3: check the behavior differences

The API format is compatible, but a few behaviors need handling the way this site works:

AreaHow it works hereWhat to do in code
Insufficient balance403 with the code insufficient_user_quota; OpenAI returns 429Handle 403 on its own: prompt a top-up, do not queue a retry
The group lacks the model503 with the code model_not_foundDo not retry forever; fix the model name or the group
Error bodycode in the error object is often an empty string; message ends with a request idDecide by HTTP status, log the full error response, and send the request id to support when you need help
Rate limits429; limits vary with the line and the load, and no fixed numbers are publishedBack off and retry, cap concurrency; see Rate limits
StreamingStreaming for Chat Completions has been tested on this site; an error after data has started shows up as the stream ending early without data: [DONE]Treat a stream that never sent its end marker as failed
Parameter supportNon-OpenAI models differ in how they support response_format, tools, reasoning_effort and similar parametersUnsupported parameters may be ignored or return 400; test with a few requests after switching models
Usage fieldsusage in each response returns input and output tokens, plus cached tokens on a cache hitRecord usage from usage in your own system and reconcile it with the console

The full status code table is in Errors and retries, and the billing fields are explained in Billing and usage.

Step 4: roll out in stages

Do not switch everything at once. Route by percentage and look at the data before each next stage:

StageShare sent hereWhat to watchMove on when
Offline checkNo live trafficReplay a set of real past requests and compare output quality, latency and costThe results are acceptable
Small shareAround 5%Error rate, latency, cost per requestIt has run steadily for a while with no new kinds of errors
Half50%The same, plus any 429s at peak hoursPeak hours are stable too
All100%The sameRetire the old service once it is stable

Pin each user or session to one side, so comparisons and debugging stay clear. Keep the share in an environment variable or a config service, so changing it or rolling back needs no code change:

import os
import zlib

from openai import OpenAI

tutujin = OpenAI(base_url="https://www.tutujin.com/v1", api_key=os.environ["TUTUJIN_API_KEY"])
legacy = OpenAI(base_url=os.environ["OLD_BASE_URL"], api_key=os.environ["OLD_API_KEY"])

# old service's model name -> this site's model name, e.g. names with a vendor prefix
MODEL_MAP = {"openai/gpt-4.1-mini": "gpt-5.4-mini"}


def rollout() -> float:
    # 0 = everything to the old service, 1 = everything here; read on every call so changes apply at once
    return float(os.environ.get("TUTUJIN_ROLLOUT", "0"))


def chat(user_id: str, model: str, messages: list):
    bucket = zlib.crc32(user_id.encode()) % 100  # the same user always lands in the same bucket
    if bucket < rollout() * 100:
        return tutujin.chat.completions.create(model=MODEL_MAP.get(model, model), messages=messages)
    return legacy.chat.completions.create(model=model, messages=messages)

The Python and Node.js versions put the same user in the same bucket, so mixed services stay consistent. At every stage, filter Logs in the console by the migration key's name to check request counts, errors and spend, and compare them with what you recorded from usage in your own system.

Rolling back

  • If something goes wrong, set the share back to 0 first. All requests return to the old service at once, with no code change and no redeploy.
  • After the full rollout, keep the old service's account and keys for a while until the new setup has proven stable.
  • After a rollback, use the time, model and error details in Logs to find the cause; if you need our help, send the request id from the error to WeChat support at JsnonoChat.

After the switch

FAQ

How much code has to change?

For projects on the OpenAI SDK, usually just base_url, api_key and the model names. One difference needs its own handling, though. An insufficient balance returns 403 here, not the 429 OpenAI uses, so keep it out of your retry queue.

Can we run the old service and Tutujin API side by side during the migration?

Yes. During a staged rollout both run in parallel. Send a share of requests here and the rest to the old service, then raise the share once things are stable.

What if the model names differ from the old platform?

Use the names from this site's model hub and models endpoint, and keep a name mapping in your code that you apply when switching.

What are the rate limits here?

There are no fixed published numbers; limits vary with the line and the load. Design for backoff and a concurrency cap. If you need more throughput long term, contact WeChat support at JsnonoChat with the models, groups and request volume.

How do we roll back?

Set the rollout share back to 0 and every request goes to the old service again. Keep the share in an environment variable or a config service, so rolling back needs no code change and no redeploy.