DeepSeek's Two Model Names: deepseek-chat and deepseek-reasoner
8 min read · updated August 11, 2026
They are not two endpoints. They are two values of the model field on the same path, and the differences between them are entirely in what the server accepts and returns — which is why switching between them silently breaks clients that were written against one.
Two names, one base URL
Both names are sent to https://api.deepseek.com/chat/completions with the same auth header and the same body schema. Changing model is changing a string. That is a real convenience and it is also the source of the problem this page exists for: nothing about the request shape signals that the rules just changed.
The names have been stable across model generations while what sits behind them has not. deepseek-chat has pointed at successive V-series checkpoints and deepseek-reasoner at successive reasoning checkpoints, with later releases serving both names from a single underlying model operating in two modes. Treat the name as selecting a behaviour — non-thinking or thinking — rather than as pinning a specific model.
model string returned in each response, which names what actually served the request, and alert when it changes.Parameters that behave differently
This is the practical core. On deepseek-reasoner, DeepSeek documents a set of sampling parameters that are accepted and have no effect, and a smaller set that raise an error:
deepseek-chat deepseek-reasoner temperature honoured accepted, no effect top_p honoured accepted, no effect presence_penalty honoured accepted, no effect frequency_penalty honoured accepted, no effect logprobs honoured error top_logprobs honoured error max_tokens honoured honoured (covers the trace too) stop honoured honoured (risky during the trace)
The split is deliberate and the reasoning behind it is compatibility: silently ignoring the four sampling parameters means an existing client that sets a temperature keeps working when you point it at the reasoning model, whereas erroring on the two log-probability parameters is honest about the fact that no meaningful value could be returned. The consequence for you is that a request can be accepted with settings that did nothing — the fix page for that symptom goes through it in full.
One more difference sits in messages rather than in parameters. A previous turn’s reasoning_content must not be sent back; doing so is a 400 on the reasoning path, and there is no equivalent restriction on the chat path because the field does not exist there. Any shared conversation-history helper has to strip it.
The response gains a field
deepseek-reasoner adds reasoning_content to the assistant message — alongside content, never inside it — and adds completion_tokens_details.reasoning_tokens to usage. In a stream, the trace arrives on its own delta key before content deltas begin.
# a handler that works for both names msg = resp.choices[0].message trace = getattr(msg, "reasoning_content", None) # None on deepseek-chat answer = msg.content details = getattr(resp.usage, "completion_tokens_details", None) reasoning_tokens = getattr(details, "reasoning_tokens", 0) if details else 0
Write it that way once and both models flow through the same path. The alternative — branching on the model name — puts a string comparison in the middle of your response handling that has to be updated every time a model is added. The streaming equivalent is the same idea applied per delta.
Feature support and where to check it
Output ceilings differ substantially — the reasoning name carries a much larger documented default and maximum for max_tokens, because the trace is spent from the same budget. Beyond that, several features have historically been available on the chat name and not the reasoning one, including function calling, JSON output mode, and the fill-in-the-middle completion available through the beta base URL.
That matrix has moved with each model release, and it is the part of this page most likely to be out of date by the time you read it. The reasoning-model guide carries the current list of unsupported features and is the authority. Build for it changing: put per-model capability in configuration rather than in conditionals, and you can follow the docs without a deploy.
There is also a separate base URL, https://api.deepseek.com/beta, which enables features that are not part of the standard OpenAI-compatible surface — prefix completion, where you supply the beginning of the assistant’s reply and the model continues it, and fill-in-the-middle. Same key, same schema, different path.
Choosing between them per request
The decision is not which model is better. It is which requests are worth a trace, because the trace is where nearly all the cost and nearly all the latency variance live.
- Use the chat name by default. Classification, extraction, summarisation, rewriting, routing, formatting — anything where the work is recognition rather than deduction gets no benefit from thinking and pays fully for it.
- Use the reasoning name for multi-step deduction. Mathematics, algorithmic problems, debugging from a symptom, anything where a wrong intermediate step produces a confidently wrong answer.
- Escalate rather than defaulting. Try the chat model, validate the answer, and retry on the reasoning model when validation fails. On a workload where most requests are easy this costs a fraction of routing everything to the reasoning model.
- Do not use the reasoning name for structured output. Where JSON mode or tool calling is unavailable on that path, you are back to parsing prose, and the reasoning model is not better at producing well-formed JSON than the chat model is.
- Keep timeouts and budgets separate per name. One set of limits across both is either too tight for the reasoning path or far too loose for the chat path.
Moving a client from one to the other
Because the change is one string, it tends to be made without review and then debugged for a day. This is the checklist that turns it back into a five-minute change.
- Raise
max_tokens. The trace spends the same budget as the answer, so a chat-sized ceiling produces a full trace with an emptycontentandfinish_reason: "length". This is the first thing that breaks and the least obvious. - Stop expecting sampling parameters to work. Remove any logic that depends on
temperature: 0for stability, and removelogprobsandtop_logprobsentirely — those raise an error rather than being ignored. - Strip
reasoning_contentbefore the next turn. Any code that appends the whole assistant message object to history will 400 on the second turn of every conversation. - Read both delta keys when streaming. A client reading only
delta.contentshows nothing for the entire thinking phase, which presents as a hang. - Move instructions out of the system message. Format and style constraints that worked on the chat path get diluted by the trace — the fix is placement.
- Re-check the features you relied on. JSON output mode and function calling have not always been available on the reasoning path. If your client depends on either, verify before switching rather than after.
- Widen timeouts and re-baseline cost alerts. Latency and spend per request both rise substantially, and monitoring calibrated on the chat path will fire continuously.