DeepSeek V4 Pro (0813): Thinking Passback & 3-API Matrix

AIHubMix15 min read
DeepSeek V4 Pro (0813): Thinking Passback & 3-API Matrix

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-Pro is 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 maps claude-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: exceeding max_tokens is rejected by validation rather than silently truncated — sending max_tokens=9999999 returns HTTP 400, and the error body names the field and gives the ceiling 393216.
# 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)" — an input_image part does not fail the request, it is swapped for placeholder text. On Chat Completions the user message content only takes a string, and on Messages type="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: with thinking.type="disabled", both message.reasoning_content and usage.completion_tokens_details.reasoning_tokens disappear 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, the reasoning output 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, the thinking block disappears entirely and only the text block remains.
On thinking levels: low and max both returned 200 on Chat Completions in testing (high is 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 the none level (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: splicing response.output back in as-is is all it takes. Filtering output items by type == "message" while assembling history drops the reasoning item 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 the thinking block from the content array returns 400 (with error.type set to invalid_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 400 Thinking 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-function tool_choice instead (as above, which works with thinking on), or turn thinking off first and then use required.

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 the tool_choice row, that disable_parallel_tool_use is ignored, and the Responses page likewise states parallel_tool_calls | Ignored (parallel tool calling is always enabled). Testing matches: asking about two cities at once with disable_parallel_tool_use: true still returns two tool_use blocks. 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 both content and reasoning_content. Code that reads only logprobs.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 under logprobs, 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: sending web_search_options or enable_search does not raise an error, but it does not retrieve anything either — the response carries no annotations (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.createdresponse.completed) stream (message_startmessage_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.