Skip to main content
When you call compatible mode, every model — OpenAI, Claude, Gemini, Grok, Qwen, GLM and others — returns the same OpenAI schema. Almost all of your parsing logic is shared: follow the patterns below and switching models needs no code changes. This page helps you get response handling right the first time: commonality first, then a single table of the few differences you must tolerate (none of which block integration).
The request side (base_url, auth, switching models) is covered in Compatible Mode Calls. This page is purely about the response side: how to parse what comes back.

Two modes, one endpoint

The same /v1/chat/completions endpoint; only the stream flag changes the shape:

Non-streaming response

Stable structure — just read choices[0].message.content:
Non-streaming output is highly consistent across all major models — choices[0].message.content works everywhere. Some models (e.g. the OpenAI family) also add annotations and refusal on message; read them if you need them, ignore them otherwise.

Streaming response (SSE)

Streaming pushes chunks as Server-Sent Events, one per line as data: {...}, ending with data: [DONE]:
With the official SDK just iterate; the core is accumulating delta.content:

Integration notes: a few differences, handled uniformly

Streaming details vary slightly between models, but following the rules below lets one code path cover them all.
The final chunk’s choices may be an empty array. The last chunk that carries usage is "choices":[] on some models (gpt-4.1-mini, grok, qwen, glm); indexing choices[0] there throws. Check that choices is non-empty before reading it.

Robust reference parser

When you handle the raw SSE yourself (no SDK), this covers every difference above:
Reasoning models (grok, qwen, glm, etc.) first stream delta.reasoning_content (the chain of thought), then delta.content (the answer). The parser above reads only content, so the thinking is skipped automatically. To display the thinking, see Reasoning Model Output.

Usage and billing

  • usage comes back inline in non-streaming responses; in streaming it arrives in a trailing chunk (location per the table above — “record whenever present”).
  • Field breakdowns differ: the OpenAI family adds completion_tokens_details, Gemini/Claude add input_tokens/output_tokens, reasoning models add reasoning_tokens. Rely on the three standard fields: prompt_tokens / completion_tokens / total_tokens.
Don’t trust the streamed total_tokens. In testing, a few models (e.g. gpt-5.4-mini) emit a trailing frame where total ≠ prompt + completion, while the same model is correct non-streaming. Bill from your account statement, not from that streamed frame.