Quick answer :
DeepSeek Tool Calls let the model request external functions during a chat workflow. The model can return a structured tool call with a function name and JSON-style arguments, but it does not run the function by itself.
Your application validates the arguments, runs the real function, appends atoolresult message with the matchingtool_call_id, and sends the updated conversation back to DeepSeek so the model can produce the final answer. For current DeepSeek API examples, usedeepseek-v4-flashordeepseek-v4-pro, not legacy aliases as primary model IDs.
Independent disclosure: Chat-Deep.ai is an independent DeepSeek-focused guide and browser access site. Chat-Deep.ai is not affiliated with DeepSeek, DeepSeek.com, Hangzhou DeepSeek Artificial Intelligence Co., Ltd., the official DeepSeek app, the official DeepSeek API platform, OpenAI, or the OpenAI Python SDK.
This guide is written for developers who want to understand DeepSeek Tool Calls in practical application code. Always verify production behavior against the official DeepSeek documentation before deploying tool workflows that read private data, call internal services, or perform actions.
DeepSeek API snapshot — last verified April 28, 2026
Last reviewed: April 28, 2026. This guide was checked against the official DeepSeek Tool Calls, Thinking Mode, Create Chat Completion, and Models & Pricing documentation.
- Current API model IDs:
deepseek-v4-flashanddeepseek-v4-pro - Base URL:
https://api.deepseek.com - Beta base URL for strict mode:
https://api.deepseek.com/beta - API format: OpenAI-compatible Chat Completions
- Context length: 1M tokens
- Max output: 384K tokens
- Tool Calls: supported
- JSON Output: supported
- Thinking mode: supported
- Non-thinking mode: supported
- Legacy aliases:
deepseek-chatanddeepseek-reasonerare legacy compatibility aliases scheduled for retirement after July 24, 2026, 15:59 UTC
Table of contents
- Who this guide is for
- What are DeepSeek Tool Calls?
- Tool Calls vs Function Calling
- Tool Calls vs JSON Output vs Agents
- How the DeepSeek Tool Calls flow works
- Which DeepSeek models support Tool Calls?
- Install and set up the Python client
- Basic non-thinking Tool Calls example
- How to define tools correctly
- tool_choice explained
- Handling multiple tool calls
- Returning tool results with tool_call_id
- Strict Tool Calls mode
- Supported strict-mode JSON Schema subset
- Tool Calls in Thinking Mode
- Streaming and Tool Calls note
- Token usage, context caching, and cost control without prices
- Error handling and debugging
- Security and validation checklist
- Common mistakes
- Production checklist
- When this guide is not the right page
- FAQ
Who this guide is for
This guide is for developers building DeepSeek API workflows that need external data, application actions, internal lookups, or controlled function execution. It is useful for support bots, order lookup assistants, inventory tools, internal knowledge apps, lightweight agents, data extraction systems, and workflow automation prototypes.
The examples use Python because Python is a clear way to show the full loop: define tools, call the model, validate tool arguments, run the application function, return the tool result, and ask the model for the final answer.
If you only need basic Python setup, read the DeepSeek Python SDK guide. If you want a wider overview of API access, model IDs, and endpoints, read the DeepSeek API guide.
What are DeepSeek Tool Calls?
DeepSeek Tool Calls are a Chat Completions feature that lets the model request a function call instead of answering only with natural language. The model chooses a tool, generates arguments, and returns a structured tool request in the assistant message.
The important rule is simple: the model asks for the function call, but your application executes it. The function can be a database lookup, an internal API request, a search operation, an inventory check, or another safe backend operation that you control.
Tool Calls are useful when the model needs information it does not already have, or when the answer depends on live application state. For example, a model can ask your backend to check an order status, then turn the returned status into a helpful response for the user.
Tool Calls vs Function Calling
Many developers use the phrase DeepSeek Function Calling because older API ecosystems often described this pattern as function calling. In DeepSeek’s current API wording, the feature is documented as Tool Calls.
For practical development, both phrases point to the same core workflow: you describe functions with JSON Schema, the model selects a function when appropriate, and your application executes the function safely.
Use “DeepSeek Tool Calls” in documentation and page titles because that matches the official feature name. Use “DeepSeek Function Calling” naturally as a secondary search phrase when explaining the concept.
Tool Calls vs JSON Output vs Agents
Tool Calls, JSON Output, and agents are related, but they are not the same thing.
- JSON Output is for structured model responses. Use it when the model only needs to return structured text that your application can parse.
- Tool Calls are for external reads or actions that your application executes. Use them when the model needs data from your backend or needs to request an operation.
- Agents are higher-level workflows that may combine planning, memory, tools, user confirmation, retries, and multiple model calls.
If your task only needs a structured answer, use DeepSeek JSON Output. If the model needs to ask your application to look something up or perform a controlled action, use Tool Calls.
How the DeepSeek Tool Calls flow works
A DeepSeek Tool Calls workflow usually follows this sequence:
- The user asks a question or requests an action.
- Your application sends the conversation and available tools to DeepSeek.
- The model either answers normally or returns one or more
tool_calls. - Your application parses the tool arguments.
- Your application validates the arguments and checks permissions.
- Your application executes the real function if the call is safe and allowed.
- Your application appends a
role="tool"message with the matchingtool_call_id. - Your application sends the updated conversation back to the model.
- The model uses the tool result to produce a final user-facing answer.
This loop is the core of DeepSeek API tools. The safest mental model is: the model proposes; your application validates and executes.
Which DeepSeek models support Tool Calls?
For new integrations, use the current DeepSeek V4 API model IDs:
deepseek-v4-flashfor fast everyday tool workflows, support assistants, lookups, structured extraction, and high-volume applications.deepseek-v4-profor harder reasoning, complex tool planning, coding workflows, long-context analysis, and higher-value production tasks.
Both current V4 API models support Tool Calls, JSON Output, thinking mode, and non-thinking mode.
Do not use deepseek-chat or deepseek-reasoner as the primary model IDs in new examples. They are legacy compatibility aliases scheduled for retirement after July 24, 2026, 15:59 UTC. For compatibility, deepseek-chat currently corresponds to DeepSeek V4 Flash non-thinking mode, and deepseek-reasoner currently corresponds to DeepSeek V4 Flash thinking mode.
Install and set up the Python client
The OpenAI-compatible DeepSeek API can be called from Python with the OpenAI Python SDK. Install the package first:
pip install openaiSet your DeepSeek API key as an environment variable. Do not hard-code API keys in source code.
macOS or Linux
export DEEPSEEK_API_KEY="your_api_key_here"Windows PowerShell
[Environment]::SetEnvironmentVariable("DEEPSEEK_API_KEY", "your_api_key_here", "User")Create the Python client with the DeepSeek base URL:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
)Basic non-thinking Tool Calls example
This example uses a safe read-only function: lookup_order_status(order_id). The model can request the function, but the Python application validates and executes it.
import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
)
def lookup_order_status(order_id: str) -> dict:
"""Demo read-only order lookup. Replace with your real backend call."""
if not isinstance(order_id, str):
raise ValueError("order_id must be a string.")
if not order_id.startswith("ORD-"):
raise ValueError("order_id must start with ORD-.")
demo_orders = {
"ORD-12345": {
"order_id": "ORD-12345",
"status": "processing",
"estimated_ship_date": "tomorrow",
}
}
return demo_orders.get(
order_id,
{
"order_id": order_id,
"status": "not_found",
"estimated_ship_date": None,
},
)
tools = [
{
"type": "function",
"function": {
"name": "lookup_order_status",
"description": "Look up the current status of a customer order by order ID.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID, for example ORD-12345.",
}
},
"required": ["order_id"],
},
},
}
]
messages = [
{
"role": "user",
"content": "Can you check the status of order ORD-12345?",
}
]
first_response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
tool_choice="auto",
extra_body={"thinking": {"type": "disabled"}},
)
assistant_message = first_response.choices[0].message
if not assistant_message.tool_calls:
print(assistant_message.content)
else:
messages.append(assistant_message.model_dump(exclude_none=True))
for call in assistant_message.tool_calls:
if call.function.name != "lookup_order_status":
raise ValueError(f"Unsupported tool requested: {call.function.name}")
try:
arguments = json.loads(call.function.arguments)
except json.JSONDecodeError as exc:
raise ValueError("Tool arguments were not valid JSON.") from exc
order_id = arguments.get("order_id")
result = lookup_order_status(order_id)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
}
)
final_response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
tool_choice="auto",
extra_body={"thinking": {"type": "disabled"}},
)
print(final_response.choices[0].message.content)In this example, DeepSeek does not execute lookup_order_status. The model requests the tool call, and the Python application decides whether to run the function.
How to define tools correctly
Tool definitions are part of the control surface. The model uses the function name, description, and parameter schema to decide when and how to call the tool.
A good tool definition should be specific, narrow, and easy to validate. Avoid vague tools like do_anything, run_task, or execute_command. Prefer read-only tools for first integrations.
{
"type": "function",
"function": {
"name": "lookup_order_status",
"description": "Look up the current status of a customer order by order ID. Use this only when the user asks about an order status.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID, for example ORD-12345."
}
},
"required": ["order_id"]
}
}
}Currently, Tool Calls use function tools. A request can include multiple functions, but each one should have a clear purpose and a schema that your backend can validate.
Tool definition checklist
- Use a clear function name.
- Describe exactly when the function should be used.
- Keep the parameter schema narrow.
- Use enums where the set of valid values is known.
- Reject arguments that are not in the schema.
- Keep sensitive or destructive actions behind explicit authorization and confirmation.
tool_choice explained
tool_choice controls whether the model can call tools, must call tools, or must call a specific function.
Let the model decide
"tool_choice": "auto"Use auto for most workflows. The model may answer normally or call one or more tools.
Disable tool use
"tool_choice": "none"Use none when the model should answer without calling a tool, even if tools are available.
Require at least one tool call
"tool_choice": "required"Use required when the assistant must call one or more tools before answering. This is useful for workflows where a live lookup is required.
Force a specific function
{
"type": "function",
"function": {
"name": "lookup_order_status"
}
}Use named tool forcing only when your application logic already knows which function must be called. Do not force a tool just to make the assistant appear more agentic.
If no tools are present, the default behavior is effectively no tool use. If tools are present, the default behavior is usually to let the model decide.
Handling multiple tool calls
DeepSeek Tool Calls can return one or more requested function calls. Your application should loop over each call, verify that the function is allowlisted, parse and validate the arguments, execute the function, and append one tool result for each tool_call_id.
import json
def run_tool_call(call) -> dict:
allowed_tools = {
"lookup_order_status": lookup_order_status,
}
tool_name = call.function.name
if tool_name not in allowed_tools:
raise ValueError(f"Unknown or disallowed tool: {tool_name}")
try:
arguments = json.loads(call.function.arguments)
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON arguments for {tool_name}.") from exc
if tool_name == "lookup_order_status":
order_id = arguments.get("order_id")
if not isinstance(order_id, str):
raise ValueError("order_id must be a string.")
return allowed_tools[tool_name](order_id)
raise ValueError(f"No handler implemented for {tool_name}.")
for call in assistant_message.tool_calls or []:
result = run_tool_call(call)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
}
)Never execute a tool just because the model requested it. Unknown tool names, malformed arguments, unsafe values, and unauthorized actions should be rejected.
Returning tool results with tool_call_id
Every tool result must match the exact tool_call_id returned by the assistant message. This is how the model knows which result belongs to which requested function call.
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(
{
"order_id": "ORD-12345",
"status": "processing",
"estimated_ship_date": "tomorrow",
}
),
}
)For most workflows, serialize the function result as JSON text. Keep the result compact and include only the information needed for the model to answer the user.
Strict Tool Calls mode
Strict Tool Calls mode is a Beta feature. It is designed to make tool-call arguments follow your function JSON Schema more closely. Strict mode is useful when malformed tool arguments are hurting reliability, but it is not a replacement for backend validation, permissions, or business rules.
To use strict mode, set the client base URL to https://api.deepseek.com/beta and set "strict": True inside each function definition.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/beta",
)
strict_tools = [
{
"type": "function",
"function": {
"name": "lookup_order_status",
"strict": True,
"description": "Look up the current status of a customer order by order ID.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID, for example ORD-12345.",
}
},
"required": ["order_id"],
"additionalProperties": False,
},
},
}
]In strict mode, the server validates the JSON Schema and can reject unsupported or invalid schemas. Your application should still validate arguments before executing any function.
Supported strict-mode JSON Schema subset
Strict mode supports a limited JSON Schema subset. Keep schemas simple and explicit.
- Supported types and features include:
object,string,number,integer,boolean,array,enum,anyOf, and reusable definitions with$ref/$def. - For object schemas: list every property in
requiredand setadditionalPropertiestofalse. - Documented unsupported constraints include:
minLength,maxLength,minItems, andmaxItems.
{
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID, for example ORD-12345."
},
"include_history": {
"type": "boolean",
"description": "Whether to include recent order history."
}
},
"required": ["order_id", "include_history"],
"additionalProperties": false
}Strict mode improves schema adherence, but it does not decide whether the user is authorized to access an order. Authorization remains your application’s job.
Tool Calls in Thinking Mode
DeepSeek Tool Calls can be used in Thinking Mode. Thinking Mode is useful when tool use requires planning across multiple steps, but it also changes how you handle the assistant message.
For thinking-mode tool-call loops, preserve and pass back the full assistant message because it may include reasoning_content and tool_calls. Do not print reasoning_content to end users by default. Use the final content as the user-facing answer.
import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
)
def lookup_order_status(order_id: str) -> dict:
if not isinstance(order_id, str) or not order_id.startswith("ORD-"):
raise ValueError("Invalid order_id.")
return {
"order_id": order_id,
"status": "processing",
"estimated_ship_date": "tomorrow",
}
tools = [
{
"type": "function",
"function": {
"name": "lookup_order_status",
"description": "Look up the current status of a customer order by order ID.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID, for example ORD-12345.",
}
},
"required": ["order_id"],
},
},
}
]
messages = [
{
"role": "user",
"content": "Check order ORD-12345 and explain what the customer should expect next.",
}
]
for _ in range(4):
response = client.chat.completions.create(
model="deepseek-v4-pro",
messages=messages,
tools=tools,
tool_choice="auto",
reasoning_effort="high",
extra_body={"thinking": {"type": "enabled"}},
)
assistant_message = response.choices[0].message
# Preserve the full assistant message internally.
# In thinking-mode tool loops, this may include reasoning_content and tool_calls.
messages.append(assistant_message.model_dump(exclude_none=True))
if not assistant_message.tool_calls:
print(assistant_message.content)
break
for call in assistant_message.tool_calls:
if call.function.name != "lookup_order_status":
raise ValueError(f"Unsupported tool requested: {call.function.name}")
try:
arguments = json.loads(call.function.arguments)
except json.JSONDecodeError as exc:
raise ValueError("Tool arguments were not valid JSON.") from exc
order_id = arguments.get("order_id")
result = lookup_order_status(order_id)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
}
)
else:
raise RuntimeError("Tool loop reached the maximum number of rounds.")In Thinking Mode, parameters such as temperature, top_p, presence_penalty, and frequency_penalty do not affect output even if passed. Use reasoning_effort="high" or reasoning_effort="max" where supported and appropriate.
Streaming and Tool Calls note
Streaming can be useful for user-facing chat, but Tool Calls require extra care. A streamed response may deliver partial deltas before the final tool-call structure is complete. Your application should not execute a tool until it has received and assembled the complete tool call.
For first production integrations, start with non-streaming Tool Calls. Add streaming only after your application can reliably parse assistant messages, detect tool calls, and avoid executing incomplete arguments.
Token usage, context caching, and cost control without prices
Tool workflows usually add messages to the conversation: the original user message, the assistant tool-call message, one or more tool result messages, and the final assistant answer. That means token usage can grow faster than in a simple one-turn chat.
usage = final_response.usage
if usage:
print("Prompt tokens:", getattr(usage, "prompt_tokens", None))
print("Completion tokens:", getattr(usage, "completion_tokens", None))
print("Total tokens:", getattr(usage, "total_tokens", None))
print("Prompt cache hit tokens:", getattr(usage, "prompt_cache_hit_tokens", None))
print("Prompt cache miss tokens:", getattr(usage, "prompt_cache_miss_tokens", None))Context Caching is enabled by default and does not require a code change. It can help repeated-prefix workloads, especially when your application reuses stable system prompts, tool definitions, or repeated document context.
Because DeepSeek API pricing can change, this guide does not copy token prices. Check the official DeepSeek pricing page and Chat-Deep.ai’s pricing guide before making billing decisions.
Cost-control habits without copying prices
- Keep tool results compact.
- Return only the fields the model needs.
- Use non-thinking mode for simple lookup workflows.
- Use thinking mode only when planning quality improves the outcome.
- Trim or summarize old conversation turns.
- Log token usage by route, model, and tool name.
- Avoid sending entire documents when a small excerpt is enough.
Error handling and debugging
Tool workflows can fail because of normal API errors, invalid request formatting, malformed tool arguments, unsupported schemas, unsafe user-controlled values, or missing tool_call_id fields.
Retry rate-limit and temporary server issues carefully. Do not blindly retry invalid requests, bad API keys, insufficient account balance, or invalid parameters without fixing the underlying issue.
import time
from openai import APIStatusError, APITimeoutError
RETRY_STATUS_CODES = {429, 500, 503}
def create_completion_with_retry(payload: dict, max_attempts: int = 3):
delay_seconds = 2
for attempt in range(1, max_attempts + 1):
try:
return client.chat.completions.create(**payload)
except APITimeoutError:
if attempt == max_attempts:
raise
except APIStatusError as exc:
if exc.status_code not in RETRY_STATUS_CODES:
raise
if attempt == max_attempts:
raise
time.sleep(delay_seconds)
delay_seconds *= 2
raise RuntimeError("Request failed after retries.")Tool Calls debugging checklist
- Invalid JSON arguments: parse with
json.loadsand handleJSONDecodeError. - Missing tool_call_id: every tool result must include the exact ID from the assistant tool call.
- Unsupported schema: simplify the JSON Schema, especially in strict mode.
- Wrong base URL for strict mode: use
https://api.deepseek.com/betaonly when strict mode is required. - Stripped reasoning_content: preserve the full assistant message in thinking-mode tool-call loops.
- Unknown tool name: reject any tool not in your allowlist.
- Unsafe user-controlled arguments: validate and authorize every argument before execution.
Security and validation checklist
Tool Calls can connect language models to real application capabilities. Treat tool execution as backend logic, not as a casual model response.
- Use an allowlist of supported tool names.
- Validate JSON arguments before calling a function.
- Reject unknown fields where possible.
- Check user permissions before returning private data.
- Keep tool results minimal and relevant.
- Never let the model construct raw SQL, shell commands, or privileged admin actions without strict server-side controls.
- Require explicit user confirmation for write actions.
- Use audit logging for sensitive tool execution.
- Separate read-only tools from tools that can change state.
- Do not expose API keys, internal tokens, or secrets in tool results.
If a workflow performs write actions such as updating records, sending messages, changing account settings, or creating transactions, add explicit user confirmation, authorization checks, audit logging, and server-side validation.
Production checklist
- Use
deepseek-v4-flashordeepseek-v4-profor new Tool Calls integrations. - Store
DEEPSEEK_API_KEYsecurely outside source code. - Use
base_url="https://api.deepseek.com"for normal Tool Calls. - Use
base_url="https://api.deepseek.com/beta"only for documented beta features such as strict mode. - Keep tool definitions narrow and specific.
- Validate all tool arguments before execution.
- Reject unknown tool names.
- Return one tool result per
tool_call_id. - Preserve full assistant messages in thinking-mode tool loops.
- Keep
reasoning_contentseparate from normal user-facing output. - Log token usage and tool execution outcomes.
- Add retries only for appropriate transient errors.
- Keep sensitive actions behind confirmation, permissions, and audit logging.
- Do not include static price values in this page.
When this guide is not the right page
This page focuses on DeepSeek Tool Calls. Use a more specific page if your goal is different:
- For general Python setup, read the DeepSeek Python SDK guide.
- For API keys, base URLs, and model overview, read the DeepSeek API guide.
- For current V4 model details, read the DeepSeek V4 guide.
- For structured model responses, read the DeepSeek JSON Output guide.
- For reasoning behavior, read the DeepSeek Thinking Mode guide.
- For cache behavior, read the DeepSeek Context Caching guide.
- For token accounting, read the DeepSeek Token Usage guide.
- For troubleshooting, read the DeepSeek Error Codes guide.
- For migration from OpenAI-style code, read the OpenAI SDK to DeepSeek guide.
- For JavaScript and TypeScript, read the DeepSeek Node.js TypeScript guide.
FAQ
What are DeepSeek Tool Calls?
DeepSeek Tool Calls let the model request external functions during a chat workflow. The model returns a structured request, and your application validates the arguments, runs the function, and sends the result back to the model.
Is DeepSeek Function Calling the same as Tool Calls?
In practice, yes. “Function Calling” is a common developer phrase, while DeepSeek’s current documentation labels the feature as Tool Calls.
Which DeepSeek models support Tool Calls?
The current DeepSeek V4 API models deepseek-v4-flash and deepseek-v4-pro support Tool Calls.
Does deepseek-v4-flash support Tool Calls?
Yes. deepseek-v4-flash supports Tool Calls and is a practical choice for fast read-only lookups, routine assistants, and high-volume workflows.
Does deepseek-v4-pro support Tool Calls?
Yes. deepseek-v4-pro supports Tool Calls and is a stronger fit for complex reasoning, multi-step planning, and more demanding tool workflows.
Does the model execute the function by itself?
No. The model requests a tool call. Your application validates the arguments, executes the real function, and sends the result back as a tool message.
What does tool_choice do?
tool_choice controls whether the model can call a tool, must call a tool, or must call a specific function. Common values include auto, none, and required.
What is DeepSeek strict mode?
Strict mode is a Beta feature that makes tool-call arguments follow your function JSON Schema more closely. It requires the beta base URL and strict: true in the function definition.
Can DeepSeek Tool Calls return multiple function calls?
Yes. Your application should loop over each returned call, validate it, execute only allowed functions, and append one matching tool result per tool_call_id.
How are Tool Calls different from JSON Output?
JSON Output is for structured model responses. Tool Calls are for external reads or actions that your application executes and returns to the model.
Can I use Tool Calls in Thinking Mode?
Yes. Tool Calls can be used in Thinking Mode, but you must preserve the full assistant message during tool-call loops.
Do I need to pass reasoning_content back in thinking-mode tool calls?
Yes. For thinking-mode turns that involve tool calls, preserve and pass back the full assistant message internally, including reasoning_content where present. Do not show reasoning content to end users by default.
Should I still use deepseek-chat or deepseek-reasoner?
For new code, use deepseek-v4-flash or deepseek-v4-pro. deepseek-chat and deepseek-reasoner are legacy compatibility aliases scheduled for retirement after July 24, 2026, 15:59 UTC.
Where can I check DeepSeek API pricing?
Because DeepSeek API pricing can change, this guide does not copy token prices. Check the official DeepSeek pricing page and Chat-Deep.ai’s pricing guide before making billing decisions.
