The Short Answer
Send response_format: {"type":"json_object"}. Also tell the model to return JSON. Parse the content and validate the result in your application.
- 1
Confirm that the endpoint accepts the
response_formatparameter. - 2
Remove an optional markdown fence before parsing.
- 3
Parse the complete assistant content as JSON.
- 4
Validate the object, required keys, and value types.
Send a Minimal JSON Mode Request
Keep the first request small. Ask for one object with two string fields. This request separates parameter support from complex prompt behavior.
curl https://api.example.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "your-model-id",
"messages": [{
"role": "user",
"content": "Return one JSON object with city and country string fields. Use JSON only."
}],
"response_format": {"type": "json_object"}
}'OpenAI defines JSON Mode as json_object and states that it does not enforce a schema. Read the official JSON Mode comparison. Source checked 12 Aug 2026.
Do Not Confuse JSON Mode with Structured Outputs
| Method | Syntax guarantee | Schema guarantee |
|---|---|---|
| Prompt only | No | No |
json_object | Expected when supported | No |
json_schema with strict mode | Yes when supported | Yes for supported schemas |
The current probe checks valid JSON, the top-level object, and requested keys. It does not claim strict schema compatibility.
Map Each Result to the Failed Contract
| Symptom | Likely layer | Action |
|---|---|---|
400 names response_format | Server capability | Use prompt constraints and client validation, or change runtime |
| Plain prose | Ignored parameter | Treat JSON Mode as unavailable |
| JSON inside a code fence | Model behavior | Strip the fence before parsing |
| Valid array or scalar | Shape mismatch | Reject values that are not objects |
| Object has wrong keys | Instruction following | Validate keys and retry with clear errors |
| Truncated JSON | Generation limit | Check token limits and the finish reason |
const raw = response.choices[0].message.content;
const normalized = raw
.replace(/^\s*```(?:json)?\s*/i, "")
.replace(/\s*```\s*$/, "");
const value = JSON.parse(normalized);
if (!value || Array.isArray(value) || typeof value !== "object") {
throw new Error("Expected one JSON object");
}Verify Support with Raw Evidence
LLMCompat stores the request, response, raw content, parse result, and each failed check. Use that evidence before you change prompts or SDK code.