Earlier posts in this series covered Structured Output in general, Gemini-specific configuration, and Tool Calling validation. If you integrate both OpenAI and Google Gemini, the recurring question is: how do their Structured Output features differ, and can one JSON Schema be reused as-is?
In 2026 both vendors treat JSON Schema constrained decoding as a first-class feature, but request field names, Schema subsets, strict semantics, and SDK shapes differ. This article aligns concepts, compares APIs, Schema compatibility, code samples, selection, and migration so you can maintain one Schema and call two providers.
Concept alignment: Structured Outputs vs Structured Output
The names differ by one letter but are often conflated:
- OpenAI Structured Outputs (plural): the Chat Completions / Responses API capability via
response_format: { type: "json_schema", ... }; GA ongpt-4o-2024-08-06and later, still the main OpenAI path for production extraction in 2026. - Gemini Structured Output (singular): Google’s term for MIME + Schema constrained generation —
responseMimeType: application/jsonplusresponseJsonSchemaor SDKresponse_schema.
Shared goal: the model’s final reply (not tool arguments) matches JSON Schema, excluding illegal JSON paths during token generation. Both are L3 hard constraints vs JSON Mode — see the four-level model in the Prompt to Structured Output guide.
Neither replaces Tool Calling: Structured Output governs JSON for users/downstream programs; Tool Calling governs tool argument JSON — see the data-flow article.
2026 API field comparison
Invoice extraction object — conceptual REST/SDK fields (verify paths in current docs):
| Dimension | OpenAI | Gemini |
|---|---|---|
| JSON Mode only | response_format: { "type": "json_object" } | responseMimeType: "application/json" (no Schema) |
| Structured + Schema | response_format: { "type": "json_schema", "json_schema": { "name", "schema", "strict": true } } | MIME + responseJsonSchema (REST) or response_json_schema / response_schema (SDK) |
| Schema source | Standard JSON Schema dict; stricter subset when strict: true | Standard JSON Schema (responseJsonSchema) or OpenAPI 3.0 subset (legacy responseSchema) |
| Parse entry | message.content string → JSON.parse; some SDKs expose parsed | response.text; Python google-genai may expose response.parsed (Pydantic) |
| Recommended models (2026) | gpt-4o, gpt-4.1 family | gemini-2.5-flash, gemini-3.7-flash, other 2.5+ / 3.x |
| Enum-only shortcut | enum inside Schema | Also responseMimeType: text/x.enum (enum string only) |
When migrating, do not mix OpenAPI 3.0 uppercase types with JSON Schema lowercase: legacy Gemini responseSchema uses OBJECT / STRING; OpenAI and Gemini JSON Schema channels use object / string.
JSON Schema subset and strict differences
Both use constrained decoding but accept different keyword sets. Cross-vendor Schemas should use the intersection:
| Keyword / behavior | OpenAI (strict: true) | Gemini (responseJsonSchema) |
|---|---|---|
type / properties / required | ✓ required pattern | ✓ |
enum, minimum / maximum | ✓ | ✓ (per current docs) |
additionalProperties: false | explicit false on all objects in strict | ✓ recommended |
anyOf / oneOf | limited under strict — simplify | limited support |
$ref depth | strict expects inline-friendly | deep nesting may be rejected — flatten |
| Field order | not guaranteed vs Schema | 2.5+ tries to preserve Schema order |
| Semantic guarantee | structure only | same — re-validate in app |
OpenAI strict: true additionally requires a documented subset (e.g. every object has additionalProperties: false, required covers all properties) or the API may return 400. Gemini has no identical flag but the same flat-object practices apply — aligned with the JSON Schema Contract article.
Either way, re-run the same Schema with ajv / jsonschema / Pydantic in production — constrained decoding reduces syntax errors, not business truth.
OpenAI Structured Outputs example
Pydantic Schema with strict: true (2026 mainstream pattern):
from openai import OpenAI
from pydantic import BaseModel, Field
client = OpenAI()
class LineItem(BaseModel):
name: str
qty: int = Field(ge=1)
unit_price_cents: int = Field(ge=0)
class Invoice(BaseModel):
vendor: str
currency: str = Field(description="ISO 4217, e.g. CNY")
items: list[LineItem]
total_cents: int
schema = Invoice.model_json_schema()
schema["additionalProperties"] = False
for prop in schema.get("properties", {}).values():
if isinstance(prop, dict) and prop.get("type") == "object":
prop["additionalProperties"] = False
response = client.chat.completions.create(
model="gpt-4o-2024-08-06",
messages=[{
"role": "user",
"content": "Extract invoice: Acme sold 2 keyboards, total 39800 cents.",
}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "invoice",
"strict": True,
"schema": schema,
},
},
)
import json
invoice = json.loads(response.choices[0].message.content)
json_object is JSON Mode only; Schema requires type: json_schema. On 400, check strict subset (missing additionalProperties, incomplete required, etc.).
Gemini Structured Output example
Same Invoice model with google-genai (2026 recommended):
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
client = genai.Client()
class LineItem(BaseModel):
name: str
qty: int = Field(ge=1)
unit_price_cents: int = Field(ge=0)
class Invoice(BaseModel):
vendor: str
currency: str
items: list[LineItem]
total_cents: int
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Extract invoice: Acme sold 2 keyboards, total 39800 cents.",
config=types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=Invoice,
),
)
invoice = response.parsed
# or json.loads(response.text)
REST sets responseMimeType + responseJsonSchema in generationConfig. New models like Gemini 3.7 Flash use the same fields — see the 3.7 Flash intro.
Reusing one Schema across vendors
Recommended repo layout:
schemas/
invoice.v1.json
samples/
invoice.valid.json
schema = json.loads(Path("schemas/invoice.v1.json").read_text())
openai_body = {"type": "json_schema", "json_schema": {"name": "invoice", "strict": True, "schema": schema}}
gemini_config = {"response_mime_type": "application/json", "response_json_schema": schema}
Three adaptation steps: (1) intersect keywords only; (2) OpenAI strict preprocessor for additionalProperties and required; (3) CI samples against both providers with the same ajv validator.
When to choose OpenAI vs Gemini
| Scenario | Common pick | Why |
|---|---|---|
| Existing OpenAI Agent stack | OpenAI Structured Outputs | Integrated with tools, evals, SDK |
| Long multimodal docs + JSON extract | Gemini 2.5+ / 3.x | Million-token context, PDF/video in one request |
| Field order matters (CSV alignment) | Gemini 2.5+ | Schema field order preservation |
| Hard strict contract, no extra fields | OpenAI strict: true | Explicit subset, 400 on bad Schema |
| Enum-only classification | Gemini text/x.enum | Less token overhead than single-field object |
| Dual cloud / failover | Shared Schema + two adapters | Same Schema version on provider switch |
Dual-vendor migration checklist
- Migrated Schema from OpenAPI 3.0 uppercase to JSON Schema lowercase?
- OpenAI side satisfies
strict: truesubset? - Gemini uses
responseJsonSchema, not MIME-only JSON? - Separated Structured Output Schema from Tool Calling Schema?
- Same jsonschema re-validation on both outputs?
- Removed Prompt instructions for markdown JSON fences?
- Logs include schema version for rollback?
FAQ
Are OpenAI Structured Outputs and Gemini Structured Output the same API?
No. Same concept — JSON Schema constrained final reply — but OpenAI uses response_format.json_schema + strict; Gemini uses responseMimeType + responseJsonSchema. Share Schema bodies; write separate request wrappers.
Can one Pydantic model feed both?
Yes. Export with model_json_schema(); add OpenAI strict preprocessing; pass to Gemini as response_json_schema or response_schema=Model. CI both before production.
Which supports a “fuller” Schema?
Neither guarantees full JSON Schema Draft. OpenAI strict is best documented; Gemini improved in 2.5+ but prefer flat Schemas. Use intersection, not maximal Draft.
How do JSON Mode vs Structured Output differ on each?
OpenAI: json_object vs json_schema. Gemini: application/json only vs JSON + Schema. JSON Mode guarantees syntax only on both.
Still need app-level JSON validation?
Yes. Both mainly guarantee structure and types, not business correctness. Re-validate with the same Schema; retry or escalate on failure.
Can Tool Calling Schema be merged with Structured Output?
Share generators (Pydantic/Zod) but separate call sites: response_format / generationConfig vs tools[].parameters or MCP inputSchema. Structured Output does not execute tools.
Summary
In 2026 OpenAI Structured Outputs and Gemini Structured Output are two vendor implementations of the same idea: lock JSON shape at decode time, harder than JSON Mode. Differences are API fields, strict subsets, SDK parse paths, and engineering extras like multimodal context.
Practice path: one Schema source → OpenAI strict prep → Gemini responseJsonSchema → unified runtime validation. Validate Schema and samples locally in JSON Toolbox before dual-provider rollout.