Skip to main content
When you call Gemini’s native format (/v1beta generateContent), the response uses Google’s candidates / parts structure, different from OpenAI compatible mode. This page explains how to parse both non-streaming (generateContent) and streaming (streamGenerateContent).
The request side (base_url is https://api.apiyi.com without /v1, x-goog-api-key auth, thinking_level control) is covered in the Gemini Native Format Guide. This page is purely about the response side. Examples use the lightweight model gemini-3.1-flash-lite.

Non-streaming response

Endpoint …:generateContent. The answer lives in candidates[0].content.parts[]:
Getting the answer means iterating parts and concatenating each text:
finishReason is uppercase STOP (not OpenAI’s lowercase stop); other values include MAX_TOKENS and SAFETY. A part may contain only thoughtSignature and no text, so filter with if "text" in p when iterating, or you’ll hit a KeyError.

thoughtSignature

Gemini 3-series models attach a thoughtSignature (encrypted reasoning state) to parts — in testing, even the lightweight gemini-3.1-flash-lite returns it.
  • Single turn: not needed; ignore it.
  • Multi-turn / function calling: pass the previous response’s thoughtSignature back verbatim in the next turn’s contents so the model can continue its reasoning chain. The official google-genai SDK handles this automatically; when hand-writing REST, don’t drop the field. See Gemini Function Calling.
This is the key difference from OpenAI compatible mode: in compatible mode reasoning models are stateless and expose no signature; only the native format has thoughtSignature, which must be passed back across turns.

Streaming response (SSE)

Endpoint …:streamGenerateContent. Each line is data: {...}, and each chunk’s increment is in candidates[0].content.parts[0].text:
Through the APIYI gateway, streaming always returns SSE data: lines (with or without ?alt=sse), and there is no [DONE] terminator — end on the chunk whose finishReason == "STOP". That last chunk typically contains only thoughtSignature and no text.
usageMetadata is present in every chunk and is cumulative (candidatesTokenCount grows with output) — just take the last chunk’s value; no manual summing needed.

Key differences from OpenAI compatible mode

Usage and billing

  • thoughtsTokenCount (thinking tokens) is billed at the output rate; use thinking_level to cap it and save cost.
  • For the cache-hit field (cachedContentTokenCount) discount, see Gemini Cache Billing.
  • The full field reference is in the “Usage fields” section of the Gemini Native Format Guide.