This article covers the usage notes and gotchas for deepseek-v4-pro-0813. On AIHubMix, the model is available through the Chat Completions, Responses, and Claude-compatible Messages APIs. See also: DeepSeek official API docs.
The "Verified" conclusions and sample responses in each section come from actual calls made on 2026-08-13 through the AIHubMix APIs (Chat Completions / Responses / Messages); spec items not marked "Verified" come from DeepSeek's official documentation.
1. Model Positioning and Specs at a Glance
V4 Pro is the high-end tier of DeepSeek's V4 generation (the lightweight deepseek-v4-flash is its sibling). The release line traces back to DeepSeek-V4 Preview on 2026-04-24, and 0813 is the MODEL VERSION label DeepSeek assigned to the current build. Beyond the raw specs, four things set it apart:
- A sparse frontier model: 1.6T total parameters / 49B activated (a MoE, or mixture-of-experts, architecture — each inference pass lights up only a subset of expert networks: total parameters determine knowledge capacity, activated parameters determine per-call compute cost). The model card lists CSA+HCA hybrid attention, mHC, and the Muon optimizer.
- Open weights under MIT:
deepseek-ai/DeepSeek-V4-Prois published on HuggingFace under the MIT license (one of the most permissive open-source licenses — commercial use and closed-source redistribution are both allowed) and can be self-hosted. MIT is uncommon for a model of this size. The model card's self-hosting notes also suggest a context window of ≥384K tokens when running in Think Max (the highest thinking level) — that is deployment guidance for self-hosting, not a spec of the hosted API. - Multi-protocol support is first-party, not third-party translation: DeepSeek itself offers an OpenAI Chat API, an Anthropic-compatible endpoint (
/anthropic, which mapsclaude-opus*onto this model), and the Responses API (DeepSeek describes native support for the format, with adaptations for Codex). It also offers FIM (fill-in-the-middle) completion as a Beta feature on a separate endpoint, which is not part of the three AIHubMix APIs. - A ~120× gap between cache-hit and cache-miss pricing: DeepSeek's published pricing mechanism is cache-hit $0.003625/M vs cache-miss $0.435/M (output $0.87/M), and caching is automatic with no parameter to set. For workloads that reuse long prefixes (system prompts, long documents), that gap dominates the bill. Actual retail pricing is whatever the model page shows.
| Item | Value |
|---|---|
| Model name on AIHubMix | deepseek-v4-pro-0813 |
| Context window | 1M tokens (1,000,000) |
| Max output | Official wording is MAX OUTPUT MAXIMUM: 384K (the exact token count and the default are not published) |
| Input modalities | Text only. The Responses compatibility page explicitly states that image and file inputs are unsupported; the Messages page explicitly marks type="image" blocks Not Supported; on Chat Completions the user message content accepts a string only, with no multimodal content parts |
| Thinking mode | Hybrid (thinking / non-thinking), thinking on by default |
| Thinking levels | reasoning_effort accepts low / high / max, default high; medium and xhigh are mapped to high for compatibility |
| Available APIs | Chat Completions, Responses, Messages (Claude-compatible) |
Verified: exceedingmax_tokensis rejected by validation rather than silently truncated — sendingmax_tokens=9999999returns HTTP 400, and the error body names the field and gives the ceiling393216.
# max_tokens=9999999 -> HTTP 400
"...max_tokens... 393216"
❗ Images do not raise an error, but they are dropped: the official wording for the Responses API is "Image and file inputs are not supported (input_image parts do not cause an error, but are replaced with a placeholder text)" — aninput_imagepart does not fail the request, it is swapped for placeholder text. On Chat Completions the user messagecontentonly takes a string, and on Messagestype="image"blocks are marked Not Supported. When building multimodal routing, never treat "no error" as evidence that the model actually saw the image.
2. How Do You Turn Thinking Off? Three APIs, Three Field Shapes
V4 Pro thinks by default: send no parameters at all and the response comes back with thinking content. Turning it off uses a different field shape on each of the three APIs.
Chat Completions
Use the top-level thinking object.
from openai import OpenAI
client = OpenAI(
base_url="https://aihubmix.com/v1",
api_key="<AIHUBMIX_API_KEY>",
)
completion = client.chat.completions.create(
model="deepseek-v4-pro-0813",
messages=[{"role": "user", "content": "What is 2 + 2?"}],
extra_body={"thinking": {"type": "disabled"}},
)
# Thinking on (default): message.reasoning_content present, reasoning_tokens = 43
# Thinking off (disabled): reasoning_content absent, reasoning_tokens absent
Verified: withthinking.type="disabled", bothmessage.reasoning_contentandusage.completion_tokens_details.reasoning_tokensdisappear together, which confirms the switch took effect.
Responses
There is no separate switch on Responses; turning thinking off means setting the level to none.
response = client.responses.create(
model="deepseek-v4-pro-0813",
input="What is 2 + 2?",
reasoning={"effort": "none"},
)
# effort="none": usage.output_tokens_details.reasoning_tokens = 0
# output[0] is the message item directly (no reasoning item)
# effort unset : output always starts with a reasoning item
Verified:reasoning.effort="none"differs observably from the default level (thinking tokens drop to zero, thereasoningoutput item disappears), which confirms it took effect.
Messages
Same name and same shape as Chat Completions: the top-level thinking object.
from anthropic import Anthropic
client = Anthropic(
api_key="<AIHUBMIX_API_KEY>",
base_url="https://aihubmix.com",
)
response = client.messages.create(
model="deepseek-v4-pro-0813",
max_tokens=1024,
messages=[{"role": "user", "content": "What is 2 + 2?"}],
extra_body={"thinking": {"type": "disabled"}},
)
# Thinking on (default): content = [thinking block, text block]
# Thinking off (disabled): content = [text block]
Verified: once disabled, thethinkingblock disappears entirely and only thetextblock remains.
On thinking levels:lowandmaxboth returned 200 on Chat Completions in testing (highis the default and applies when the field is omitted), but thinking-token counts show no monotonic difference between levels for the same question (easy question: low=43 / max=27; hard question: low=114 / max=92), and nothing is echoed back in the response — the levels are accepted, but no distinguishing signal is observable from the response. On Responses, only thenonelevel (thinking off) can be confirmed from the response side.
3. Why Does a Multi-Turn Conversation Suddenly Return 400? Thinking History Must Be Passed Back Verbatim
This is the single most common tripwire with this model: in thinking mode, a multi-turn conversation must pass the previous turn's thinking content back verbatim, or the request is rejected. Not degraded, not lower quality — a hard HTTP 400.
The three APIs carry the same thinking content under different field names:
| API | Passback shape | Error body when missing |
|---|---|---|
| Chat Completions | The reasoning_content field on the assistant message |
The `reasoning_content` in the thinking mode must be passed back to the API. |
| Responses | The output item with type="reasoning" in the input array |
The `reasoning_text` in the thinking mode must be passed back to the API. |
| Messages | The thinking block inside the assistant content blocks |
The `content[].thinking` in the thinking mode must be passed back to the API. |
Verified (trigger conditions): this validation fires consistently on multi-turn requests that carry tools (the model issues a tool call, then the tool result is sent back). On plain multi-turn requests without tools, where the model answers directly, the validation did not fire in this round of testing and the request returned 200. In other words, tool orchestration (agent / function-calling workloads) is where you are most likely to hit it, so treat thinking content as part of the conversation state you persist and replay.Chat Completions
# Multi-turn: pass the previous assistant message back verbatim, including reasoning_content
messages = [
{"role": "user", "content": "What is 1 + 1? Remember the result."},
{
"role": "assistant",
"content": "2",
"reasoning_content": "<reasoning_content from the previous response>",
},
{"role": "user", "content": "Add 1 to the result."},
]
# Dropping reasoning_content -> HTTP 400 invalid_request_error
Verified: a historical assistant message missing reasoning_content returns 400; adding it back makes the identical request return 200 and continue correctly.Responses
# Multi-turn: input = previous input + response.output (reasoning item included) + new message
input = previous_input + response.output + [
{"role": "user", "content": "Add 1 to the result."}
]
# Filtering out the type="reasoning" item -> HTTP 400
Verified: splicingresponse.outputback in as-is is all it takes. Filtering output items bytype == "message"while assembling history drops thereasoningitem and triggers the 400 — this is the most common way to get bitten.
Messages
# Multi-turn: pass response.content back verbatim as the assistant message
messages = [
{"role": "user", "content": "What's the weather in Paris?"},
{"role": "assistant", "content": response.content}, # thinking + tool_use blocks
{"role": "user", "content": [tool_result_block]},
]
# Stripping the thinking block -> HTTP 400
Verified: removing thethinkingblock from the content array returns 400 (witherror.typeset toinvalid_request_error).
4. Tool Calling
Each API declares tools in its own protocol shape; the shapes are not interchangeable.
Chat Completions
Nested shape (a function object wrapping name / parameters). A named-function tool_choice forces the call.
completion = client.chat.completions.create(
model="deepseek-v4-pro-0813",
messages=[{"role": "user", "content": "What's the weather in Paris?"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
},
}],
tool_choice={"type": "function", "function": {"name": "get_weather"}},
)
# Observed: finish_reason "tool_calls", tool_calls[0].function.arguments = {"city": "Paris"}
❗ Verified:tool_choice: "required"cannot be used while thinking is on — it returns 400Thinking mode does not support this tool_choice; disabling thinking (thinking.type="disabled") makes the identical request return 200. When you need "must call a tool" semantics, use a named-functiontool_choiceinstead (as above, which works with thinking on), or turn thinking off first and then userequired.
Responses
Flat shape (type / name / parameters at the same level).
response = client.responses.create(
model="deepseek-v4-pro-0813",
input="What's the weather in Paris?",
tools=[{
"type": "function",
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
}],
)
# Observed output items: ["reasoning", "function_call"]; arguments = {"city": "Paris"}
Verified: copying the Chat Completions nested shape (function: {...}) into Responses returns 400 — use the flat shape.tool_choice: "required"is subject to the same thinking-mode restriction as on Chat.
Messages
Anthropic-native shape (input_schema), with tool_choice: {"type": "any"} to force a call.
response = client.messages.create(
model="deepseek-v4-pro-0813",
max_tokens=1024,
tools=[{
"name": "get_weather",
"description": "Get weather for a city",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
}],
tool_choice={"type": "any"},
messages=[{"role": "user", "content": "What's the weather in Paris?"}],
)
# Observed: content contains a tool_use block, name = get_weather, input = {"city": "Paris"}
❗ Parallel tool calling cannot be turned off, by DeepSeek's own design — the official Anthropic-compatibility page states, on thetool_choicerow, thatdisable_parallel_tool_use is ignored, and the Responses page likewise statesparallel_tool_calls | Ignored (parallel tool calling is always enabled). Testing matches: asking about two cities at once withdisable_parallel_tool_use: truestill returns twotool_useblocks. If you need serial execution, take the first call or queue them yourself on the client side.
Tool count and context cost: sending 200 function definitions in a single request still returned 200 with a normal answer and did not trip any count validation (observed on this path; higher counts were not tested). But prompt_tokens for that request reached 6,105 — tool definitions go into the context in full and are billed. When you have many tools, trim the tool set per scenario rather than declaring everything unconditionally.5. Structured Output
Chat Completions
response_format supports JSON mode.
completion = client.chat.completions.create(
model="deepseek-v4-pro-0813",
messages=[{"role": "user", "content": "Return {\"a\": 1} as JSON."}],
response_format={"type": "json_object"},
)
# Observed response content: {"a":1}
Verified: the output is valid JSON.
Responses
Declare a JSON Schema through text.format, with strict mode supported.
response = client.responses.create(
model="deepseek-v4-pro-0813",
input="Return the number 1 under key a.",
text={
"format": {
"type": "json_schema",
"name": "extract",
"strict": True,
"schema": {"type": "object", "properties": {"a": {"type": "integer"}}, "required": ["a"]},
}
},
)
# Observed output text: {"a":1}
Verified: the output conforms strictly to the given schema.
Messages
The Messages (Anthropic) protocol has no response_format / text.format equivalent. The usual workaround is to carry the schema in a tool — declare a tool whose input_schema is your target schema, set tool_choice: {"type": "any"}, and read the structured result from the input of the tool_use block. This round of testing did not specifically verify that pattern; when you need hard schema guarantees, prefer Chat Completions or Responses.
6. How Do You Enable Context Caching? You Don't, It's Automatic
Context caching (identical prefixes are reused, and the cached portion is billed at a lower rate) is on by default and needs no parameters. A second request with the same long prefix reports the hit in usage, under a field name that varies by API. For caching details and current pricing, see the model page; for cross-model caching strategy and hit-rate techniques, see prompt caching practices.
Chat Completions
# usage of the second call with an identical long prefix
"prompt_tokens_details": {"cached_tokens": 640} # first call: 0
Verified: two back-to-back calls with the same long prefix on the same channel moved cached_tokens from 0 to 640.Responses
# usage of the second call with identical long instructions
"input_tokens_details": {"cached_tokens": 896} # first call: 0
Messages
# usage of a call whose long system prefix was already warmed
"cache_read_input_tokens": 896
Verified: the prefix above was warmed by a Responses request with identical content, and the first Messages call hit 896 straight away — consistent with caching being keyed on content prefix and shared across protocol surfaces.
7. logprobs: Chat Returns Two Channels
logprobs (log probabilities — the model's per-candidate-token confidence detail) comes back in different shapes on the two APIs, and parsing code has to handle them separately.
Chat Completions
completion = client.chat.completions.create(
model="deepseek-v4-pro-0813",
messages=[{"role": "user", "content": "Say hi."}],
logprobs=True,
top_logprobs=2,
)
# Observed: choices[0].logprobs contains TWO arrays
# logprobs.content[] -> tokens of the final answer
# logprobs.reasoning_content[] -> tokens of the thinking text
❗ Verified: Chat returns log probabilities for bothcontentandreasoning_content. Code that reads onlylogprobs.content, per the standard OpenAI response shape, will not error out but will silently miss the thinking channel; if your code assumes a single array underlogprobs, add a shape check first.
Responses
response = client.responses.create(
model="deepseek-v4-pro-0813",
input="Say hi.",
top_logprobs=3,
)
# Observed: logprobs only on the final message item
# output[-1].content[0].logprobs[] with logprob + top_logprobs details
Verified: Responses attaches logprobs only to the final text item — none of the dual-channel shape seen on Chat.
Messages
The Messages (Anthropic) protocol has no equivalent field. For token-level probability detail, use Chat Completions or Responses.
8. Which APIs Can Search the Web?
Web search here is a server-side tool (the retrieval runs on the server; the client never issues the request itself), and it genuinely executes on both the Responses and Messages APIs in testing.
Responses
response = client.responses.create(
model="deepseek-v4-pro-0813",
input="What is the latest stable version of Python?",
tools=[{"type": "web_search"}],
)
# Observed output item sequence:
# ["reasoning", "web_search_call", "reasoning", "message"]
Verified: a web_search_call item appears in the output sequence, which means the server really did run a retrieval.Messages
response = client.messages.create(
model="deepseek-v4-pro-0813",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
messages=[{"role": "user", "content": "What is the latest stable version of Python?"}],
)
# Observed content block sequence:
# ["thinking", "server_tool_use", "web_search_tool_result", "thinking", "text"]
# usage.server_tool_use.web_search_requests = 1
Verified: usage.server_tool_use.web_search_requests counts 1 — the retrieval request really happened and was metered.Chat Completions
Web search cannot be triggered on Chat. DeepSeek's official Chat API reference contains no search-tool field anywhere in the request schema (that is an absence established by going through the field list one by one; DeepSeek has made no explicit statement denying support). The API with an explicit official support statement for server-side search is Responses (web_search), and the official Messages compatibility page also lists the search-related content blocks.
# Three control groups, same question requiring live information, all HTTP 200:
# A no search field -> "cannot retrieve", annotations = null
# B web_search_options -> "cannot retrieve", annotations = null, usage identical to A
# C enable_search -> "cannot retrieve", annotations = null, usage identical to A
Verified: sendingweb_search_optionsorenable_searchdoes not raise an error, but it does not retrieve anything either — the response carries noannotations(the citation list attached to a response when web search runs), and usage matches the control group field for field. For web access, use the Responses or Messages API instead.
9. Usage Notes: DeepSeek's Design vs Deviations on Our Path
Everything below returns HTTP 200 while behaving counterintuitively. The causes differ, and so does what you should do about them, so they are listed separately: the first group is how DeepSeek designed the model, and changing providers will not change it; the second group is current behavior on the AIHubMix path, which we are working on.
9.1 By DeepSeek's Design
| Behavior | Official wording | What to do |
|---|---|---|
| Responses does not retain session state or metadata | The official Responses compatibility page states, row by row, store | Not supported. The response always carries store: false, metadata | Not supported, and safety_identifier | Not supported (of those four fields, only user is Supported). Testing matches: the request returns 200, but metadata is null, safety_identifier is absent, and store is always false |
Keep request-correlation data on the client; do not rely on server-side retention |
| Sampling parameters have no effect in thinking mode | DeepSeek states explicitly that temperature and top_p are silently inert in thinking mode. In testing both return 200 with nothing echoed back and no change in response shape |
Do not rely on sampling parameters for output stability in thinking mode; use structured output when you need determinism |
| Prefix continuation / FIM is only on the official beta endpoint | The official description of prefix is "(Beta) … You must set base_url="https://api.deepseek.com/beta" to use this feature", and FIM completion is likewise a Beta feature. Verified on AIHubMix production: sending prefix: true against the standard endpoint returns 200 but the prefix is silently discarded, consistent in direction with the official wording |
For controlled output format, use structured output (section 5) or stop truncation |
| Parallel tool calling cannot be disabled | See section 4: DeepSeek states on both the Responses and Anthropic pages that the switch is ignored and parallel calling is always on | Queue calls on the client when you need serial execution |
9.2 Current Behavior on the AIHubMix Path
| Behavior | What testing shows | What to do |
|---|---|---|
Non-standard type on Responses error objects |
The error.type on 4xx responses is Aihubmix_api_error, while the same class of error on Messages returns the canonical invalid_request_error |
Branch on the HTTP status code, not on the error.type string |
| Thinking tokens counted as 0 on Messages | The response does carry a thinking block, yet usage.output_tokens_details.thinking_tokens is always 0, which contradicts the thinking content actually produced; under the Anthropic contract we integrate against, that field is required and should be ≤ output_tokens |
For thinking-cost accounting, use completion_tokens_details.reasoning_tokens on Chat or output_tokens_details.reasoning_tokens on Responses |
Messages echoes model as deepseek-v4-pro |
The request sends deepseek-v4-pro-0813 and the response echoes deepseek-v4-pro. The cause is naming: DeepSeek's only official API model name is deepseek-v4-pro, and 0813 is its version label |
Do not make the response model field the sole basis for model routing checks or usage attribution |
9.3 Undefined by DeepSeek, So No Verdict Either Way
Sending a value outside the enum for reasoning_effort (e.g. bogus_xyz) returns 200 with a normal answer, no error, and no observable effect. The fact is clear enough — this path currently does not validate the reasoning_effort enum. What is unclear is whether it should: DeepSeek publishes the legal enum but never states whether an illegal level ought to be rejected, so there is no baseline to judge against, which means this counts neither as official behavior nor as a defect on our path. The safe client-side approach: validate the level yourself and do not count on the API to catch it.
10. Capability × API Support Matrix
The cells below give the parameter / field spelling for each API. Except where marked as DeepSeek's explicit wording, every conclusion comes from actual calls made on 2026-08-13 against the AIHubMix production APIs.
| Capability | Chat Completions | Responses | Messages |
|---|---|---|---|
| Basic chat / system instructions | ✅ messages |
✅ input + instructions |
✅ messages + top-level system |
| Streaming | ✅ stream + stream_options |
✅ stream (response.created … response.completed) |
✅ stream (message_start … message_stop) |
| Output ceiling | ✅ max_tokens (400 when exceeded, ceiling 393216) |
✅ max_output_tokens |
✅ max_tokens |
| Disabling thinking | ✅ thinking: {"type": "disabled"} |
✅ reasoning: {"effort": "none"} |
✅ thinking: {"type": "disabled"} |
| Thinking level | 🟡 reasoning_effort accepted, no distinguishing signal |
✅ reasoning.effort (only none confirmable) |
🟡 output_config.effort accepted, nothing echoed back |
| Thinking content returned | ✅ reasoning_content field |
✅ reasoning output item |
✅ thinking content block |
| Mandatory thinking-history passback | ✅ missing reasoning_content → 400 |
✅ missing reasoning item → 400 |
✅ missing thinking block → 400 |
| Tool calling | ✅ nested tools + named tool_choice |
✅ flat tools |
✅ input_schema + tool_choice: {"type":"any"} |
Forcing a call with required |
❗ 400 while thinking is on; disable thinking first | ❗ same as left | ✅ {"type": "any"} |
| Parallel tool calling (not disableable) | ➖ no such field on the official Chat API | ❗ DeepSeek states parallel_tool_calls is ignored and parallel calling is always on |
❗ DeepSeek states disable_parallel_tool_use is ignored; testing still returns two tool_use blocks |
| Structured output | ✅ response_format (json_object) |
✅ text.format (json_schema + strict) |
➖ no protocol field; carry the schema in a tool |
| Automatic cache-hit metering | ✅ usage.prompt_tokens_details.cached_tokens |
✅ usage.input_tokens_details.cached_tokens |
✅ usage.cache_read_input_tokens |
| logprobs | ❗ dual channel: content + reasoning_content |
✅ top_logprobs on the final text item only |
➖ |
| Web search | ➖ no search field on the official Chat API; sending one does not retrieve either | ✅ tools: [{"type": "web_search"}] |
✅ web_search_20250305 |
| Stop sequences | ✅ stop |
➖ no stop-sequence field in the protocol (only max_output_tokens limits length) |
✅ stop_sequences (stop_reason: "stop_sequence") |
Legend: ✅ verified working · 🟡 accepted but cannot be confirmed effective · ❗ needs attention (see the notes above) · ➖ no such concept on this API
FAQ
Which APIs does deepseek-v4-pro-0813 support on AIHubMix?
Chat Completions (/v1/chat/completions), Responses (/v1/responses), and the Claude-compatible Messages API (/v1/messages).
Why does a multi-turn conversation suddenly return 400?
The most common cause is thinking history that was not passed back. In thinking mode, the previous turn's thinking content must be replayed verbatim: reasoning_content on the assistant message for Chat, the type="reasoning" output item for Responses, and the thinking content block for Messages. Multi-turn with tools is where this bites hardest — many frameworks filter output items by type == "message" while assembling history, which drops the reasoning item.
Can thinking be turned off?
Yes. Send thinking: {"type": "disabled"} on Chat or Messages, and reasoning: {"effort": "none"} on Responses. Once off, both the thinking content and the thinking tokens disappear.
Do the three reasoning_effort levels differ?low / high / max are all accepted (default high; medium and xhigh are mapped to high for compatibility). In testing, thinking-token counts for the same question show no monotonic difference between levels and nothing is echoed back, so the difference cannot be confirmed from the caller's side. Only the none level on Responses (thinking off) produces a clear observable difference.
Why does tool_choice: "required" return 400?
That value is not accepted while thinking is on (the error body reads Thinking mode does not support this tool_choice). Use a named-function tool_choice ({"type": "function", "function": {"name": "..."}}) to force a specific call with thinking on, or disable thinking first and then use required.
How do you enable context caching?
You don't — it is automatic. Put the stable, unchanging content (system prompts, knowledge snippets, tool definitions) at the front of the request, and the hit count is reported in usage: prompt_tokens_details.cached_tokens on Chat, input_tokens_details.cached_tokens on Responses, and cache_read_input_tokens on Messages.
For pricing and real-time status, see the deepseek-v4-pro-0813 model page; for more models, visit the model gallery.
Related hands-on guides: Kimi K3 hands-on guide (new parameters and a three-API support matrix) and GPT-5.6 prompt caching and billing changes.




