DeepSeek shipped V4 Flash 0731 on July 31, and the headline everyone quoted was the price — roughly $0.14 / $0.28 per million tokens for a model that out-benchmarks flagships on agent tasks (we ran the numbers in the cheap model that beats the flagship). This piece is the other half: how you actually call it. It's OpenAI-compatible, so the migration is two lines — but thinking mode, the split reasoning field, and a 384K output ceiling each have one gotcha worth knowing before you wire it into an agent.
1. Point your client at it — two lines#
V4 Flash implements the OpenAI Chat Completions API. Reuse the official openai SDK and change only the base URL and the model name. Set the key once — never hard-code it:
export DEEPSEEK_API_KEY="sk-..."
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com/v1", # ← change 1
)
resp = client.chat.completions.create(
model="deepseek-v4-flash", # ← change 2
messages=[{"role": "user", "content": "Summarize this changelog in 3 bullets."}],
)
print(resp.choices[0].message.content)
That call runs in non-thinking mode — fast and cheap, the right default for extraction, classification, routing, and other high-volume grunt work. Everything else in your existing agent code (tool schemas, streaming, message history) stays exactly as it is.
2. Turn thinking on — a flag, not a new model#
You don't switch models to get reasoning. Thinking is a per-request setting: pass a thinking block via extra_body and set reasoning_effort.
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Plan the migration, then write the script."}],
extra_body={"thinking": {"type": "enabled"}},
reasoning_effort="high", # none disables · low/medium/high/xhigh/max
)
msg = resp.choices[0].message
print(msg.reasoning_content) # the chain-of-thought — for your logs
print(msg.content) # the actual answer — for your app
The effort dial is the cost dial. reasoning_effort="none" disables thinking entirely; low through max enable it at rising depth (medium and high both map to high effort). Turn it up for planning, debugging, and code generation where a wrong answer is expensive; turn it down — or off — for the calls where latency and cost beat depth.
The gotcha: the reasoning comes back as a separate field, reasoning_content, distinct from content. Log it, inspect it, throw it away — but never concatenate it into the assistant's turn or feed it back into the next request as message content. It's not part of the answer, and re-injecting it corrupts the conversation and inflates your input bill.
3. curl and Node — the same two changes#
curl https://api.deepseek.com/v1/chat/completions \
-H "Authorization: Bearer $DEEPSEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "ping"}],
"thinking": {"type": "enabled"},
"reasoning_effort": "low"
}'
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com/v1",
});
const resp = await client.chat.completions.create({
model: "deepseek-v4-flash",
messages: [{ role: "user", content: "Refactor this function." }],
reasoning_effort: "high",
// @ts-expect-error DeepSeek extension
thinking: { type: "enabled" },
});
console.log(resp.choices[0].message.content);
4. The Responses API — reasoning arrives as its own item#
V4 Flash also supports the Responses API, the stateful, item-based interface. The shape is different from Chat Completions in one way that matters: in thinking mode the chain-of-thought is emitted as a reasoning item that appears before the message item in the output array. Tool calls come back as function_call items and server-side web searches as web_search_call items.
So don't grab output[0] and assume it's the answer — walk the array and pull the message item:
resp = client.responses.create(
model="deepseek-v4-flash",
input="Draft the release note.",
extra_body={"thinking": {"type": "enabled"}},
reasoning_effort="medium",
)
answer = next(
part.text
for item in resp.output if item.type == "message"
for part in item.content if part.type == "output_text"
)
print(answer)
If you're weighing which interface to build on, we compared them for agents in Responses vs Assistants vs Chat Completions.
5. The one limit that will bite you: 384K output#
V4 Flash has a 1M-token context window, but its maximum output is 384K tokens — and in thinking mode the reasoning trace is spent from that same output budget. A long chain-of-thought plus a long answer can hit the ceiling and truncate the response mid-sentence. On any generation that can run long — a big refactor, a full document, a batch of tool calls — set max_tokens deliberately rather than trusting the default, and treat a finish_reason of length as a real error path, not a rare edge case. Our API errors, retries, and fallbacks how-to has the retry pattern.
The whole thing, in one line#
Change base_url and model, flip thinking on when the task needs it, read the answer from content (never reasoning_content), and cap max_tokens so the 384K ceiling can't swallow your output. That's a frontier-grade agent backend running against a budget-tier bill — which, as this week's Wire argued, is the default worth re-pricing your stack around this month.



