genmux API is now generally available. Read the docs →
genmux API

Blog

Use the OpenAI SDK with DeepSeek, Qwen and GLM: one base_url

5 min readWritten by genmux API Team


If your codebase already calls openai.chat.completions.create(...), switching to DeepSeek, Qwen3 or Doubao through genmux is a two-line change: point base_url at https://api.genmux.tech/v1 and swap the API key. Everything downstream — request shape, response shape, streaming — stays the same. This post is the field-by-field reference for what "OpenAI-compatible" actually covers here. (GLM and Kimi are temporarily off the roster while their upstream is being fixed — and more models come online over time.)

The two-line migration

python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.genmux.tech/v1",   # was https://api.openai.com/v1
    api_key="nb_live_...",                    # your genmux key
)
javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.genmux.tech/v1",
  apiKey: process.env.GENMUX_API_KEY,
});

Every call after this looks exactly like it did against OpenAI's own API — same method names, same response objects — with model pointing at one of genmux's ids instead. Since v0.4.0 that includes function calling and JSON mode, which is what makes the endpoint a drop-in backend for coding agents.

What's supported

POST /v1/chat/completions accepts the standard fields:

FieldSupportedNotes
modelyesAny id in the pricing table, e.g. deepseek-v4-pro, qwen3-max, doubao-seed-2-pro, and more as they come online
messagesyesrole: system | user | assistant, content
max_tokensyesCaps the reservation; a value above the model's max_output_tokens is clamped, not rejected
temperature, top_p, stopyesStandard sampling controls
streamyesServer-sent events; final chunk carries usage; tool calls arrive as delta.tool_calls
tools, tool_choiceyes, 7 modelsFunction calling, emulated by genmux; assistant tool_calls and role: tool messages round-trip as with OpenAI. Other models: 400 tools_unsupported
response_formatyesjson_object and json_schema ({name, schema}); non-streamed replies are validated, one automatic retry, then 502 json_invalid. Not combinable with tools
reasoning_effortyesnone/minimal turn thinking off, low…xhigh turn it on, on thinking-capable models
enable_thinkinggenmux extensionThinking-capable models only (see below)
web_searchgenmux extensionDoubao Seed 2.0 Pro only

Function calling and JSON mode

tools works on the seven models flagged params_schema.tools: true in GET /v1/models: deepseek-v4-pro, deepseek-v4-flash, deepseek-v3, qwen3-7-max, qwen3-max, doubao-seed-2-pro and doubao-seed-2-code. The upstream gateway drops the tools field, so genmux renders the definitions into the system prompt and parses the reply back into standard tool_calls — the definitions count as prompt tokens (about 40 per tool plus schema), and tool_choice: required is an instruction the model can ignore. Sending tools to qwen3-6-flash, doubao-seed-2-lite or doubao-seed-2-mini returns 400 tools_unsupported. The engineering and the measured pass rates are in the tool-calling post.

response_format takes {"type":"json_object"} and {"type":"json_schema","json_schema":{"name":…,"schema":{…}}}. Non-streamed replies are validated (JSON Schema 2020-12); a failure triggers one automatic retry with the validator's error fed back, and a second failure ends in 502 json_invalid with both attempts billed. Streams pass through unvalidated, and JSON mode cannot be combined with tools in one request.

What isn't supported

Three things return 400 unsupported_parameter rather than being silently ignored:

  • n > 1 — one completion per request only.
  • legacy functions — use tools; the deprecated functions/function_call pair is rejected.
  • logprobs — not returned by any upstream.

Accepted and ignored, so old client code keeps working: stream_options, parallel_tool_calls, seed, user, metadata, store, logit_bias and the per-message name.

Reading usage and cost

The response is a standard chat.completion (or a stream of chat.completion.chunk), extended with billing fields on usage:

json
{
  "id": "gnx_9f2c...",
  "object": "chat.completion",
  "model": "qwen3-max",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }],
  "usage": {
    "prompt_tokens": 128,
    "completion_tokens": 342,
    "total_tokens": 470,
    "cost": 0.276,
    "cost_source": "vendor"
  }
}

id is the genmux job id — an OpenAI SDK will happily ignore it if you don't need it, but it's there if you want to look the call up later via GET /v1/generations/{id}, or list every chat call on the account with GET /v1/generations?capability=text.chat. cost is in credits; cost_source is vendor when the upstream reported exact usage, or estimated when genmux computed it from token counts (always the case for streamed calls).

Errors worth handling explicitly

Errors on the chat route come in the OpenAI envelope — {"error":{"message","type","code","param","request_id"}} with type one of invalid_request_error, authentication_error, permission_error, insufficient_quota, rate_limit_error, not_found_error, server_error — so the OpenAI SDK's exception classes map onto them unchanged. (Other genmux routes keep RFC 7807 problem+json.)

StatusCodeMeaning
400unsupported_parameterOne of the rejected fields above (n>1, functions, logprobs) was sent
400tools_unsupportedtools sent to one of the three models without the flag
400context_length_exceededPrompt + max_tokens exceeds the model's context window
402insufficient_creditsBalance can't cover the reservation; nothing was generated or billed
502json_invalidJSON mode reply failed schema validation twice; both attempts billed

Thinking, if the model supports it

enable_thinking works on DeepSeek V4 (Pro and Flash), Qwen3.7-Max, Qwen3-Max, and Doubao Seed 2.0 Pro/Code/Lite, with more models gaining it as they come online — sending it to a model that doesn't support reasoning is simply ignored. Reasoning tokens are billed as completion tokens; see how thinking is billed before flipping it on for high-traffic endpoints.

Streaming stays the same shape

stream: true returns the same SSE format your OpenAI integration already parses — data: {...}\n\n lines, data: [DONE] to close. The one thing to add: the last chunk before [DONE] has an empty choices array and a populated usage object, which is where your cost accounting should read from. Full parsing code for Python and Node is in the streaming guide.

Getting started

Change base_url, drop in a genmux key, and the rest of your OpenAI integration keeps working — tools and JSON mode included, as long as it isn't relying on n>1, legacy functions or logprobs. Agent setups (OpenCode, aider, Cline, Roo Code, Continue) are on the coding agents page. Full endpoint reference and the current model catalogue are in the docs; prices for every model are on the pricing page.

About the author

Written by genmux API Team — genmux API.

More about us