Last reviewed: October 10, 2026
DeepSeek JSON Output helps a DeepSeek model return a JSON string instead of a natural-language answer. In Chat Completions, enable it with response_format={"type":"json_object"}, explicitly ask for JSON in the prompt, show the expected shape, and set an output-token limit large enough for the complete object.
That setup is only the generation step. Production code must also verify that the API returned a completed answer, reject empty or truncated content, parse the JSON, confirm that the top-level value has the expected shape, and validate every required field, type, enum, range, and application rule before using the result.
DeepSeek warns that JSON Output can occasionally return empty content. Its Chat Completions reference also says that finish_reason="length" means the message may have been cut off because the request reached the output or context limit. A string can therefore look like JSON without being safe to store or act on. DeepSeek JSON Output guide DeepSeek Chat Completions API
Quick answer: how to use DeepSeek JSON Output safely
- Use a current model ID such as
deepseek-flashordeepseek-v4-pro. - For Chat Completions, set
response_format={"type":"json_object"}. - Use the word JSON in the system or user message and include an example object.
- For simple extraction, explicitly disable Thinking Mode unless testing shows that reasoning improves the result.
- Choose
max_tokensfrom the expected output size plus a safety margin; do not use one fixed number for every schema. - Require a response choice and
finish_reason="stop"before parsing. - Reject
null, non-string, empty, or whitespace-only message content. - Parse with a real JSON parser and require the expected top-level type.
- Reject missing and unexpected fields, then validate field types and allowed values.
- Do not save the data, call another service, or trigger an action until application validation succeeds.
What DeepSeek JSON Output does
DeepSeek JSON Output is designed to make the model generate valid JSON text. It is useful when your application needs structured content that will be parsed by code.
- Extracting fields from support messages, forms, or documents.
- Classifying tickets, feedback, leads, or content.
- Normalizing product data and metadata.
- Producing structured summaries.
- Returning machine-readable labels and routing decisions.
- Preparing data for a separately validated backend workflow.
JSON Output addresses syntax, not truth or business correctness. A response can be parseable JSON while containing the wrong enum, an empty string, a value of the wrong type, an unexpected field, or a decision that violates your application rules.
What JSON Output does not guarantee
| Misconception | More accurate answer |
|---|---|
response_format alone is enough. | No. DeepSeek says the prompt must also explicitly request JSON and should include an example format. |
| Valid JSON means a complete response. | No. Reject a response that ended with length, content_filter, aborted, insufficient_system_resource, or another unexpected finish reason. |
| Valid JSON means the right top-level shape. | No. JSON.parse and json.loads can successfully parse arrays, strings, numbers, booleans, or null. |
| All required fields are guaranteed. | No. Validate required and unexpected keys in application code, or use a documented schema-constrained workflow. |
| Types and values are guaranteed. | No. Validate strings, booleans, numbers, enum values, lengths, ranges, and cross-field rules. |
| JSON Output is the same as JSON Schema. | No. Chat Completions uses json_object. DeepSeek Responses API also documents json_schema. |
| JSON Output is the same as Tool Calls. | No. JSON Output returns final structured content; Tool Calls request a function your application may execute. |
| JSON mode can never be empty. | No. DeepSeek documents that empty content can occasionally occur. |
Chat Completions setup
For Chat Completions, the relevant parameter is response_format. The documented values are text and json_object. JSON Output still requires a prompt that asks for JSON.
response = client.chat.completions.create(
model="deepseek-flash",
messages=messages,
response_format={"type": "json_object"},
max_tokens=800,
extra_body={"thinking": {"type": "disabled"}},
)The value 800 is a deliberately generous example for a small four-field object. It is not a universal recommendation. Estimate the largest legitimate response for your schema, add a reasonable safety margin, monitor actual usage, and reject any result that ends because the limit was reached.
Safe validation order
| Step | What to check | Why |
|---|---|---|
| 1. Response choice | choices contains at least one item. | A missing choice means there is no assistant result to parse. |
| 2. Completion state | finish_reason is exactly stop for this final JSON workflow. | length can mean truncation; other values describe different outcomes. |
| 3. Content | Content is a string and remains nonempty after trimming. | DeepSeek documents occasional empty content, and the field is nullable in the API schema. |
| 4. JSON syntax | The string parses without an exception. | Malformed or incomplete JSON must not continue through the pipeline. |
| 5. Root type | The parsed value is an object, not an array, primitive, or null. | A parser accepts more JSON types than many applications expect. |
| 6. Keys | Required fields exist and unexpected fields are handled explicitly. | Unknown or missing data can break downstream assumptions. |
| 7. Types | Each value has the exact runtime type expected. | A string such as "false" is not a boolean. |
| 8. Values | Enums, lengths, ranges, formats, and cross-field rules are valid. | Correct types can still contain unacceptable values. |
Python example: reject empty, truncated, and invalid data
This complete example uses the OpenAI Python SDK configured for DeepSeek. It disables Thinking Mode because the task is a small, formatting-sensitive classification. It does not assume that a successful HTTP response contains usable data.
import json
import os
from typing import Any
from openai import OpenAI
ALLOWED_CATEGORIES = {"billing", "technical", "account", "other"}
ALLOWED_URGENCIES = {"low", "medium", "high"}
EXPECTED_KEYS = {
"category",
"urgency",
"summary",
"needs_human_review",
}
def extract_completed_json_text(response) -> str:
if not response.choices:
raise ValueError("DeepSeek returned no response choices.")
choice = response.choices[0]
if choice.finish_reason != "stop":
raise ValueError(
"DeepSeek did not return a complete final answer: "
f"finish_reason={choice.finish_reason!r}."
)
raw = choice.message.content
if not isinstance(raw, str) or not raw.strip():
raise ValueError("DeepSeek returned empty or non-text JSON content.")
return raw.strip()
def parse_and_validate_ticket(raw_json: str) -> dict[str, Any]:
try:
value = json.loads(raw_json)
except json.JSONDecodeError as exc:
raise ValueError("DeepSeek returned malformed or incomplete JSON.") from exc
if not isinstance(value, dict):
raise ValueError("The top-level JSON value must be an object.")
keys = set(value)
missing = EXPECTED_KEYS - keys
extra = keys - EXPECTED_KEYS
if missing or extra:
raise ValueError(
f"Unexpected JSON fields; missing={sorted(missing)}, "
f"extra={sorted(extra)}."
)
category = value["category"]
urgency = value["urgency"]
summary = value["summary"]
needs_human_review = value["needs_human_review"]
if type(category) is not str or category not in ALLOWED_CATEGORIES:
raise ValueError("category must be one of the allowed string values.")
if type(urgency) is not str or urgency not in ALLOWED_URGENCIES:
raise ValueError("urgency must be one of the allowed string values.")
if type(summary) is not str:
raise ValueError("summary must be a string.")
summary = summary.strip()
if not 1 <= len(summary) <= 240:
raise ValueError("summary must contain 1 to 240 characters.")
if type(needs_human_review) is not bool:
raise ValueError("needs_human_review must be a JSON boolean.")
return {
"category": category,
"urgency": urgency,
"summary": summary,
"needs_human_review": needs_human_review,
}
api_key = os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
raise RuntimeError("Set DEEPSEEK_API_KEY before running this example.")
client = OpenAI(
api_key=api_key,
base_url="https://api.deepseek.com",
)
system_prompt = """
You extract structured data from support messages.
Return only valid JSON.
Use exactly this JSON shape:
{
"category": "billing | technical | account | other",
"urgency": "low | medium | high",
"summary": "one short sentence",
"needs_human_review": true
}
Rules:
- Use only the listed category and urgency values.
- Return all four fields and no additional fields.
- needs_human_review must be a JSON boolean, not a string.
- Do not include markdown or text outside the JSON object.
"""
user_message = """
I was charged twice for my subscription yesterday.
I checked my bank account and both charges are still pending.
"""
response = client.chat.completions.create(
model="deepseek-flash",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
max_tokens=800,
extra_body={"thinking": {"type": "disabled"}},
)
raw_json = extract_completed_json_text(response)
data = parse_and_validate_ticket(raw_json)
print(data)The example refuses to parse a response that has no choice, ended for a reason other than stop, or contains no non-whitespace text. It then requires a top-level object with exactly four keys and checks every field’s runtime type and allowed value.
The check type(needs_human_review) is bool intentionally rejects strings such as "true", integers such as 1, and null. Do not silently coerce model output unless your application has a documented and tested normalization policy.
Node.js example: validate the completion and object
Use this server-side. Do not expose a DeepSeek API key in browser JavaScript or a public bundle.
import OpenAI from "openai";
const ALLOWED_SENTIMENTS = new Set(["positive", "neutral", "negative"]);
const ALLOWED_PRIORITIES = new Set(["low", "medium", "high"]);
const EXPECTED_KEYS = ["sentiment", "feature", "summary", "priority"];
function extractCompletedJsonText(completion) {
const choice = completion.choices?.[0];
if (!choice) {
throw new Error("DeepSeek returned no response choice.");
}
if (choice.finish_reason !== "stop") {
throw new Error(
`DeepSeek did not return a complete final answer: finish_reason=${choice.finish_reason}`
);
}
const raw = choice.message?.content;
if (typeof raw !== "string" || !raw.trim()) {
throw new Error("DeepSeek returned empty or non-text JSON content.");
}
return raw.trim();
}
function parseAndValidateFeedback(rawJson) {
let value;
try {
value = JSON.parse(rawJson);
} catch {
throw new Error("DeepSeek returned malformed or incomplete JSON.");
}
if (value === null || Array.isArray(value) || typeof value !== "object") {
throw new Error("The top-level JSON value must be an object.");
}
const actualKeys = Object.keys(value).sort();
const expectedKeys = [...EXPECTED_KEYS].sort();
if (JSON.stringify(actualKeys) !== JSON.stringify(expectedKeys)) {
throw new Error(
`Expected exactly these fields: ${expectedKeys.join(", ")}.`
);
}
if (typeof value.sentiment !== "string" || !ALLOWED_SENTIMENTS.has(value.sentiment)) {
throw new Error("sentiment has an invalid type or value.");
}
if (typeof value.priority !== "string" || !ALLOWED_PRIORITIES.has(value.priority)) {
throw new Error("priority has an invalid type or value.");
}
if (typeof value.feature !== "string" || !value.feature.trim()) {
throw new Error("feature must be a nonempty string.");
}
if (typeof value.summary !== "string") {
throw new Error("summary must be a string.");
}
const summary = value.summary.trim();
if (summary.length < 1 || summary.length > 240) {
throw new Error("summary must contain 1 to 240 characters.");
}
return {
sentiment: value.sentiment,
feature: value.feature.trim(),
summary,
priority: value.priority,
};
}
const apiKey = process.env.DEEPSEEK_API_KEY;
if (!apiKey) {
throw new Error("Set DEEPSEEK_API_KEY before running this example.");
}
const client = new OpenAI({
apiKey,
baseURL: "https://api.deepseek.com",
});
const completion = await client.chat.completions.create({
model: "deepseek-flash",
messages: [
{
role: "system",
content: `
Return only valid JSON.
Use exactly this JSON shape:
{
"sentiment": "positive | neutral | negative",
"feature": "short feature name",
"summary": "one short sentence",
"priority": "low | medium | high"
}
Return all fields and no additional fields.
Do not include markdown or text outside the JSON object.
`.trim(),
},
{
role: "user",
content: "The export button works, but large CSV files take too long to download.",
},
],
response_format: { type: "json_object" },
max_tokens: 800,
thinking: { type: "disabled" },
});
const rawJson = extractCompletedJsonText(completion);
const data = parseAndValidateFeedback(rawJson);
console.log(data);This is JavaScript. In strict TypeScript projects, represent DeepSeek-specific request fields in an explicit request type rather than weakening the entire payload to any.
Pydantic validation in Python
Pydantic can combine JSON decoding and strict application validation. The response-completion checks still need to happen before Pydantic receives the content.
from typing import Annotated, Literal
from pydantic import BaseModel, ConfigDict, StringConstraints, ValidationError
class TicketLabel(BaseModel):
model_config = ConfigDict(extra="forbid", strict=True)
category: Literal["billing", "technical", "account", "other"]
urgency: Literal["low", "medium", "high"]
summary: Annotated[
str,
StringConstraints(strip_whitespace=True, min_length=1, max_length=240),
]
needs_human_review: bool
raw_json = extract_completed_json_text(response)
try:
ticket = TicketLabel.model_validate_json(raw_json)
except ValidationError as exc:
raise ValueError(
"DeepSeek returned JSON that did not match the application schema."
) from exc
print(ticket.model_dump())extra="forbid" rejects unrecognized properties. Strict validation avoids silently turning a string, number, or other value into the expected type. Keep business rules that depend on external state—such as account ownership or permissions—outside the model schema.
Zod validation in TypeScript
import { z } from "zod";
const FeedbackSchema = z
.object({
sentiment: z.enum(["positive", "neutral", "negative"]),
feature: z.string().trim().min(1).max(120),
summary: z.string().trim().min(1).max(240),
priority: z.enum(["low", "medium", "high"]),
})
.strict();
const rawJson = extractCompletedJsonText(completion);
let parsed;
try {
parsed = JSON.parse(rawJson);
} catch {
throw new Error("DeepSeek returned malformed or incomplete JSON.");
}
const result = FeedbackSchema.safeParse(parsed);
if (!result.success) {
throw new Error(`JSON failed application validation: ${result.error.message}`);
}
console.log(result.data);Zod’s .strict() rejects extra properties. The schema also validates enum values and nonempty strings instead of checking only that a key exists.
Thinking Mode and JSON Output
DeepSeek’s current documentation says Thinking Mode is enabled by default with effort high. For simple extraction, normalization, or classification, disabling it can reduce latency and keep the request focused on the final JSON shape. For a reasoning-heavy task, test Thinking Mode and validate only the final content, not reasoning_content.
| Task | Suggested starting configuration | Why |
|---|---|---|
| Simple field extraction | Thinking disabled. | The task is mostly formatting and direct mapping. |
| Short classification with fixed enums | Thinking disabled, then benchmark against enabled mode if accuracy is insufficient. | Reduces unnecessary reasoning and latency. |
| Complex multi-step assessment | Thinking enabled with a tested effort level. | Reasoning may improve the final decision, but final JSON still requires validation. |
| Streaming JSON | Prefer non-thinking first. | It simplifies accumulation because reasoning and final content are separate streams. |
With the OpenAI Python SDK, DeepSeek documents passing the toggle inside extra_body:
extra_body={"thinking": {"type": "disabled"}}Do not depend on the default for a formatting-sensitive production workflow. Make the mode explicit so a future default or copied example does not change behavior silently.
How to choose max_tokens
Chat Completions uses max_tokens. DeepSeek currently allows values from 1 up to the model limit, and its default differs between thinking and non-thinking modes. A larger maximum is not a guarantee of completeness; you must still check the finish reason.
- Estimate the largest valid object, including nested arrays and long strings.
- Add a safety margin rather than setting the limit equal to the expected average.
- Use a smaller schema when the object is unnecessarily large.
- Monitor completion-token usage and truncation rate in production.
- Reject every
finish_reason="length"response, even ifjson.loadshappens to succeed. - Remember that input plus generated output must remain within the model’s context limit.
For the small examples above, 800 provides comfortable space without implying that every request needs 800 tokens. A nested extraction containing many items may need substantially more, while a two-label classifier may need less.
Can you stream DeepSeek JSON Output?
Yes, but a partial JSON stream is not a completed object. Accumulate only final-answer content, wait for the terminal chunk, verify the final finish reason, then parse and validate. If the connection ends before a final finish reason arrives, treat the result as incomplete.
For SSE parsing and interrupted-stream handling, use our DeepSeek API streaming guide.
import json
stream = client.chat.completions.create(
model="deepseek-flash",
messages=[
{
"role": "system",
"content": (
'Return only valid JSON with exactly these fields: '
'{"label":"bug | request | question","summary":"short string"}. '
"Do not include markdown or extra fields."
),
},
{"role": "user", "content": "The dashboard crashes after I upload a CSV."},
],
response_format={"type": "json_object"},
max_tokens=600,
stream=True,
extra_body={"thinking": {"type": "disabled"}},
)
parts: list[str] = []
final_finish_reason = None
for chunk in stream:
if not chunk.choices:
continue
choice = chunk.choices[0]
delta = choice.delta.content
if isinstance(delta, str):
parts.append(delta)
if choice.finish_reason is not None:
final_finish_reason = choice.finish_reason
if final_finish_reason != "stop":
raise ValueError(
"The JSON stream did not complete normally: "
f"finish_reason={final_finish_reason!r}."
)
raw_json = "".join(parts).strip()
if not raw_json:
raise ValueError("DeepSeek returned an empty JSON stream.")
try:
parsed = json.loads(raw_json)
except json.JSONDecodeError as exc:
raise ValueError("The completed stream was not valid JSON.") from exc
if not isinstance(parsed, dict):
raise ValueError("The top-level streamed JSON value must be an object.")
print(parsed)The stream example checks completeness and the root type, but a real application should apply the same exact field, type, enum, and value validation used in the non-streaming examples.
Finish reasons and what your application should do
finish_reason | Meaning in the current API reference | Recommended JSON handling |
|---|---|---|
stop | The model reached a natural or requested stop. | Continue to content, parsing, and schema validation. |
length | The output or context limit was reached. | Reject as incomplete. Increase the limit or request a smaller object. |
content_filter | Content was omitted by filtering. | Do not treat the output as a complete business object. |
tool_calls | The model requested one or more tools. | Unexpected in a final JSON-only workflow; handle through a Tool Calls loop instead. |
insufficient_system_resource | Generation was interrupted by insufficient inference resources. | Record a service failure and apply a bounded retry policy if appropriate. |
aborted | Generation was interrupted. | Reject the partial data; do not repair or store it as complete. |
JSON Output vs JSON Schema vs Tool Calls
| Feature | Where it applies | Use it when | Still validate |
|---|---|---|---|
Chat Completions json_object | Chat Completions. | You need valid JSON text for a relatively simple final answer. | Completion state, content, root type, fields, types, values, and business rules. |
Responses API json_object | Responses API. | You use the Responses format but only require JSON mode. | Response status, output text, parsed shape, and application rules. |
Responses API json_schema | Responses API. | You need output constrained to a named JSON Schema. | Response status plus business, authorization, and external-state rules. |
| Tool Calls strict mode | Tool Calls. | The model should request a controlled function with schema-constrained arguments. | Function allowlist, authorization, business rules, and side-effect safety. |
Use Tool Calls when the model should request an external lookup or action. Use JSON Output when the JSON object itself is the final model answer. Do not execute an action merely because a JSON result says it is allowed.
Responses API JSON Schema
DeepSeek’s Responses API documents text.format values of text, json_object, and json_schema. A JSON Schema format requires both a name and a schema.
{
"model": "deepseek-flash",
"input": "Classify this ticket: I cannot log in after resetting my password.",
"reasoning": {"effort": "none"},
"max_output_tokens": 800,
"text": {
"format": {
"type": "json_schema",
"name": "ticket_classification",
"schema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account", "other"]
},
"urgency": {
"type": "string",
"enum": ["low", "medium", "high"]
},
"summary": {
"type": "string",
"minLength": 1,
"maxLength": 240
}
},
"required": ["category", "urgency", "summary"],
"additionalProperties": false
}
}
}
}For Responses API, check that the top-level response status is completed. If it is incomplete, inspect incomplete_details.reason, which the current reference documents as max_output_tokens or content_filter. Also reject a failed response or a completed response without nonempty output text.
Prompt rules for reliable JSON
- Say “Return only valid JSON.”
- Use the word JSON in the system or user message.
- Show a small example object with the exact top-level shape.
- List all required fields and allowed enum values.
- Say whether extra fields are forbidden.
- Specify whether missing data should be
null, an empty array, or a rejected result. - Tell the model not to include Markdown fences or explanatory prose.
- Set realistic string-length or item-count expectations.
- Keep the schema small enough to complete within the output budget.
A prompt can improve consistency but cannot replace runtime validation. The model does not know every permission, database constraint, or current business rule in your application.
How to handle empty or invalid output
| Problem | Likely cause | Action |
|---|---|---|
| No response choice | Unexpected or incomplete API response. | Record the failure and do not index choices[0]. |
| Empty or whitespace-only content | Documented JSON Output edge case or a failed generation. | Reject it. Simplify the prompt and use a bounded retry only when appropriate. |
finish_reason="length" | Output/context limit reached. | Reject as truncated; increase the budget or reduce the requested object. |
| Parser error | Malformed, partial, or wrapped output. | Reject it; do not blindly remove text or append missing brackets. |
| Top-level array instead of object | Prompt shape was ambiguous or the model deviated. | Reject it or change the documented schema to an object such as {"items": [...]}. |
| Wrong type | The model produced a parseable but schema-invalid value. | Reject or route to review; avoid silent coercion. |
| Unexpected field | The model added data outside the contract. | Reject it when the contract is strict; do not silently trust it. |
| Invalid enum or range | JSON syntax succeeded but business validation failed. | Reject, review, or retry under a bounded policy. |
Current model names for JSON Output
DeepSeek’s current model table lists deepseek-flash and deepseek-v4-pro, and both list JSON Output support. Use the live model table before deployment because aliases and availability can change.
| Model name | Current guidance | JSON Output |
|---|---|---|
deepseek-flash | Current DeepSeek-V4.1-Flash API model ID. | Supported. |
deepseek-v4-pro | Separate Pro route in the current model table. | Supported. |
deepseek-v4-flash | Legacy compatibility name; avoid in new examples. | Use deepseek-flash instead. |
deepseek-v4-flash-vision-exp | Legacy compatibility name; avoid in new examples. | Use deepseek-flash instead. |
deepseek-chat | Appears in older tutorials; do not use as the default without current documentation. | Check the live model table. |
deepseek-reasoner | R1-era name; not the current default JSON Output model. | Check current documentation. |
Production checklist
- Use a current model ID and the correct API endpoint.
- Make Thinking Mode explicit.
- Choose an output-token budget based on the actual schema.
- Require at least one response choice.
- Require the expected completion status or finish reason.
- Reject empty, whitespace-only, or non-string Chat Completions content.
- Parse with a real JSON parser after completion.
- Validate the top-level JSON type.
- Reject missing and unexpected fields.
- Validate exact runtime types, enum values, lengths, ranges, and formats.
- Apply business and authorization rules after schema validation.
- Do not store or execute unvalidated model output.
- Use bounded retries and distinguish service failures from invalid model data.
- Log failure categories without exposing secrets or sensitive prompts.
- Monitor empty-output, truncation, parse-error, schema-error, and retry rates.
- Test normal, empty, truncated, wrong-root, missing-field, extra-field, wrong-type, and invalid-enum cases.
Frequently asked questions
What is DeepSeek JSON Output?
DeepSeek JSON Output is a Chat Completions feature that asks the model to generate valid JSON text when response_format is set to {"type":"json_object"} and the prompt also requests JSON.
How do I know the JSON response is complete?
For Chat Completions, require a response choice and finish_reason="stop". Reject length and every other unexpected reason before parsing. For Responses API, require status="completed".
Why did DeepSeek return empty JSON content?
DeepSeek’s JSON Output guide says empty content may occasionally occur. Treat it as a failure, simplify or clarify the prompt, and retry only under a bounded policy.
Can valid JSON still be wrong?
Yes. It can have the wrong root type, missing or extra fields, strings where booleans are required, invalid enum values, empty text, or incorrect business decisions.
Why is my JSON truncated?
The response may have reached max_tokens or the model context limit. A finish_reason of length must be treated as incomplete even when the partial text can be parsed.
What max_tokens value should I use?
There is no universal number. Estimate the largest valid object for your schema, add a safety margin, and monitor actual output usage. The examples use 600–800 tokens for small objects, not as a global default.
Should I disable Thinking Mode for JSON Output?
It is a strong starting point for simple extraction and classification because it reduces latency and separates the task from unnecessary reasoning. Test enabled Thinking Mode for genuinely complex decisions and parse only the final content.
Can I stream JSON Output?
Yes, but do not parse partial chunks. Accumulate final-answer content, wait for the terminal finish reason, require stop, then parse and validate.
Does DeepSeek support JSON Schema?
DeepSeek’s Responses API documents a json_schema output format with required name and schema fields. Chat Completions JSON Output uses json_object, not the same parameter shape.
Do Pydantic or Zod replace completion checks?
No. First verify that the API response completed and contains nonempty final content. Then use Pydantic, Zod, or another validator for the parsed data.
What model should I use?
Use a current model ID from DeepSeek’s live model table. At this review, deepseek-flash and deepseek-v4-pro both list JSON Output support.
Bottom line
To use DeepSeek JSON Output safely, do more than set response_format. Request JSON explicitly, choose the thinking mode and output budget intentionally, require a completed response, reject empty or truncated content, parse the final string, and validate the object’s fields, types, values, and business rules.
Use Chat Completions json_object for straightforward structured answers, Responses API json_schema when you need schema-constrained output, and Tool Calls when the model should request a controlled function. In every case, the application remains responsible for completion checks, authorization, and safe use of the data.
