DeepSeek API Function Calling: tools vs JSON Output
📑 Table of Contents
Introduction
Search DeepSeek API Function Calling, and many people mash two features together: hang tools for real actions, then also set response_format: json_object for “extra safety.” Officially they are separate — use Tool Calls to call out; use JSON Output when you only need a valid JSON string.
Short pick: need DB/API/state changes → tools; need structured fields for your parser → response_format. Use shipping model names deepseek-flash or deepseek-v4-pro (pricing). This site is an independent community; fields and Beta limits follow official Tool Calls / JSON Output. Echo rules with thinking: DeepSeek thinking mode.

I. Split Them First: Function Calling ≠ JSON Output
| Need | Use | Key params |
|---|---|---|
| Model proposes a function you implement; you return results into the chat | Tool Calls / Function Calling | tools; inspect tool_calls on the response |
Model emits a valid JSON text string for local json.loads | JSON Output | response_format={"type":"json_object"} |
| Strict fields and a real call-out | Prefer tools + schema | Shape comes from the function parameter schema; do not stack JSON Output |
Official Tool Calls docs: the model does not execute functions — it only proposes calls; execution and safety checks are yours. For a support-bot skeleton that embeds tools, see Build a chatbot with the DeepSeek API — that post is the scenario frame; this one covers protocol and selection.
II. Minimal Function Calling Loop (deepseek-flash)
base_url stays https://api.deepseek.com. Four steps: user message → model returns tool_calls → you run the function → reply with role: tool → request again for the natural-language answer.
Minimal example:
from openai import OpenAI
client = OpenAI(api_key="YOUR_KEY", base_url="https://api.deepseek.com")
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather of a location.",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City, e.g. Hangzhou"},
},
"required": ["location"],
},
},
}]
messages = [{"role": "user", "content": "How's the weather in Hangzhou?"}]
msg = client.chat.completions.create(
model="deepseek-flash", messages=messages, tools=tools,
).choices[0].message
messages.append(msg)
if msg.tool_calls:
call = msg.tool_calls[0]
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": "24C, cloudy",
})
final = client.chat.completions.create(
model="deepseek-flash", messages=messages, tools=tools,
)
print(final.choices[0].message.content)
Must do:
try/exceptparsetool.function.arguments, then map to a allow-listed function.- With thinking on and
toolspresent, historicalreasoning_contentmust be echoed or you get HTTP 400 — appending the wholemessageis safest. - Chat Completions cannot insert tool calls that the model did not generate mid-thread; use the Anthropic-compatible endpoint or Responses API when you must inject history.
III. strict Mode (Beta): Turn On When Schema Must Hold
When the server must enforce argument shape, set strict: true:
base_url="https://api.deepseek.com/beta"- Every
functionin the list sets"strict": true - Every
object: all properties inrequired, andadditionalProperties: false
Supported types include object / string / number / integer / boolean / array / enum / anyOf; string may use pattern and format (email, uuid, …) but not minLength / maxLength; array not minItems / maxItems. Invalid schemas error before the model runs — useful at deploy time.
Default production traffic still uses https://api.deepseek.com; do not blindly route everything to /beta.
IV. JSON Output: The Prompt Must Contain json
When you only need structured text and no call-out:
response_format={"type": "json_object"}- system or user prompt must include the word
json, plus an example of the expected fields - Set
max_tokenssanely sofinish_reason=lengthdoes not truncate mid-JSON - Official docs note occasional empty
content— retry with a tweaked prompt; checkfinish_reasonbefore parsing
Minimal example:
response = client.chat.completions.create(
model="deepseek-flash",
messages=[
{"role": "system", "content": "Extract fields as JSON. EXAMPLE JSON OUTPUT: {\"name\":\"A\",\"age\":1}"},
{"role": "user", "content": "Alice is 30 years old."},
],
response_format={"type": "json_object"},
max_tokens=256,
)
print(response.choices[0].message.content)

V. Selection Checklist and Common Traps
| Scene | Tip |
|---|---|
| Order lookup / tickets / internal APIs | Function Calling |
| Extract fields, report rows, fixed key/value returns | JSON Output |
| Long agent chains + thinking | tools + thinking echo rules |
| Server-enforced schema | Beta strict |
| 429 / concurrency | DeepSeek API 429 |
| Bill control | Peak hours |
Don’t:
- Enable
toolsand JSON Output together as “double constraints” — structure belongs in the tool schema. evalor SQL-concatenate uncheckedarguments.- Keep expired model names; new code writes
deepseek-flash/deepseek-v4-pro. Overview: DeepSeek API.
FAQ
How many functions can Function Calling take?
The upgrade note mentioned up to 128 per call with parallel calls; re-check the live Tool Calls docs and regression-test your tool list before shipping.
Is JSON Output the same as JSON inside tool arguments?
No. The former constrains the assistant’s final content; the latter is the tool_calls[].function.arguments string — still validate locally.
Does the web app expose Function Calling?
This article covers the DeepSeek API. Web chat: DeepSeek web; protocol capabilities follow the API docs.
Summary
Shortest path for DeepSeek API Function Calling: call-outs → tools loop with validated args; JSON only → response_format + prompt contains json; hard schema → Beta strict. Open official Tool Calls / JSON Output pages first; do not skip thinking + tools echo rules.
Further reading
DeepSeek V4 Pro Team
DeepSeek V4 Pro technical team
Ready to experience DeepSeek V4?
Start chatting now and feel the power of 1M-token context.
🚀 Start ChattingFree · No sign-up required
🧭 In this series
Explore related guides and hub pages in this topic cluster.
Category hub
Tutorial →