DeepSeek Node.js Integration: A Production Quickstart for V4

You want to call DeepSeek from a Node.js service today, and you don’t want to babysit a custom HTTP client to do it. The good news: a working DeepSeek Node.js integration is roughly fifteen lines of code, because the API speaks the same wire format as OpenAI’s Chat Completions. Drop in the official openai package, swap the baseURL, change the model, and the rest of your code keeps working. The friction is in the details — the current model IDs, thinking mode as a parameter, JSON mode caveats, retry logic, and getting your cost math right. This guide walks through all of that with code you can paste into a fresh project, using only the fields DeepSeek documents, then hardens it for production. By the end you’ll have a client module, streaming, tool calls and a cost-aware logger.
What you’ll build

You’ll set up a Node.js 22+ project, install the OpenAI SDK, configure it for DeepSeek’s POST /chat/completions endpoint, and progressively add streaming, JSON mode, tool calling and a cost logger. The final client targets DeepSeek’s two current API models: deepseek-flash, served by DeepSeek-V4.1-Flash-1017, and deepseek-v4-pro, served by DeepSeek-V4-Pro-0813. Both have a 1M-token context with up to 384K tokens of output, and both are also published as MIT-licensed open weights.
If you’d rather work in Python first, the patterns map one-to-one — see our DeepSeek Python integration guide. The wire format is identical.
Prerequisites
- Node.js 22 or newer. The current
openaipackage (7.43.0 on npm on 24 October 2026) declaresnode >=22.0.0in its engines field. - A DeepSeek API key. If you don’t have one, follow our walkthrough to get a DeepSeek API key.
- The official OpenAI Node SDK, a current release (
npm install openai). No DeepSeek-specific package is needed. - A funded balance. DeepSeek may show a granted promotional balance in the billing console, but plan for paid usage from day one; calls return HTTP 402 “Insufficient Balance” when the balance runs out.
- Comfort with async/await and environment variables.
Step 1: Scaffold the project
Create a new directory and initialise it as an ES module project so you can use import syntax and top-level await cleanly.
mkdir deepseek-node && cd deepseek-node
npm init -y
npm pkg set type="module"
npm install openai dotenv
echo "DEEPSEEK_API_KEY=sk-..." > .env
echo ".env" >> .gitignore
Check which SDK version landed with npm ls openai. The examples below use only parameters that are part of the current SDK’s chat.completions.create() types (reasoning_effort, response_format, tools, stream) plus DeepSeek’s thinking field, which the SDK sends through in the request body unchanged.
Step 2: Configure the client
The whole integration hinges on two values: the base URL and the model ID. Create client.js:
// client.js
import "dotenv/config";
import OpenAI from "openai";
export const deepseek = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com",
maxRetries: 2, // the SDK default; raise it for batch jobs
// timeout defaults to 10 minutes in the SDK; keep it generous for thinking-mode calls
});
That’s the entire configuration. The OpenAI-format base URL is https://api.deepseek.com; the Anthropic-format base URL is https://api.deepseek.com/anthropic, so if your codebase already uses the Anthropic SDK you can point that at DeepSeek instead. We’re sticking with the OpenAI shape for this guide because it’s the broader ecosystem.
Choose the right model ID
DeepSeek’s pricing page lists two current model IDs. Use them in code, not the legacy names.
| Model ID | Served by | Best for | Context / output |
|---|---|---|---|
deepseek-flash |
DeepSeek-V4.1-Flash-1017 | Default choice: chat, classification, RAG, high-volume backends; the only current model with image input | 1M / 384K |
deepseek-v4-pro |
DeepSeek-V4-Pro-0813 | Call sites where you have measured a quality gain worth the higher price | 1M / 384K |
deepseek-v4-flash, deepseek-v4-flash-vision-exp (legacy) |
DeepSeek-V4.1-Flash-1017 | Still accepted and billed at the Flash price; switch to deepseek-flash |
1M / 384K |
deepseek-chat, deepseek-reasoner (legacy) |
— | Scheduled for retirement on 2026-07-24 at 15:59 UTC; no longer listed in DeepSeek’s docs | — |
Migrating from a legacy name is a one-line change to the model field; the baseURL does not change. DeepSeek’s 10 September 2026 Change Log put it simply: change the model name to deepseek-flash to call the latest V4.1 Flash model.
Step 3: Make your first call

Create hello.js. Thinking mode is on by default; this example sets it explicitly, as in DeepSeek’s own Node.js sample:
// hello.js
import { deepseek } from "./client.js";
const resp = await deepseek.chat.completions.create({
model: "deepseek-flash",
messages: [
{ role: "system", content: "You are a precise assistant." },
{ role: "user", content: "Summarise REST in one sentence." },
],
thinking: { type: "enabled" },
reasoning_effort: "high",
});
console.log(resp.choices[0].message.content);
console.log("Tokens:", resp.usage);
Run it: node hello.js. You should see a one-sentence summary plus a usage block. Chat requests hit POST /chat/completions, the OpenAI-compatible endpoint at the configured base URL.
Picking temperature
DeepSeek publishes opinionated defaults. They apply to non-thinking mode only — thinking mode accepts temperature without error but ignores it — so pair them with thinking: { type: "disabled" }:
- 0.0 — code generation and mathematics
- 1.0 — data analysis and data cleaning
- 1.3 — general conversation and translation
- 1.5 — creative writing and poetry
Step 4: Multi-turn conversation (state lives on the client)
Here’s the trap newcomers fall into. The DeepSeek web chat at chat.deepseek.com remembers your previous messages; the API does not. The API is stateless — you must resend the full message history with every request to sustain a multi-turn conversation. Build a small array and append to it:
// chat.js
import { deepseek } from "./client.js";
const history = [
{ role: "system", content: "You are a concise senior engineer." },
];
async function ask(userText) {
history.push({ role: "user", content: userText });
const resp = await deepseek.chat.completions.create({
model: "deepseek-flash",
messages: history,
});
const reply = resp.choices[0].message;
history.push({ role: "assistant", content: reply.content });
return reply.content;
}
console.log(await ask("What is a circuit breaker?"));
console.log(await ask("Sketch one in TypeScript."));
Without tools in the request, you don’t need to send reasoning_content back; DeepSeek’s Thinking Mode guide says it is ignored if you do. For background on storing this server-side, see the DeepSeek API best practices reference.
Step 5: Streaming responses
For chat UIs you want tokens as they arrive. Set stream: true and iterate the async iterable. In thinking mode the reasoning streams first on delta.reasoning_content, then the answer on delta.content:
// stream.js
import { deepseek } from "./client.js";
const stream = await deepseek.chat.completions.create({
model: "deepseek-flash",
messages: [{ role: "user", content: "Write a haiku about caches." }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.reasoning_content) continue; // reasoning tokens; show them separately if you need them
process.stdout.write(delta?.content ?? "");
}
process.stdout.write("\n");
Each chunk is a server-sent-event delta. For deeper detail on backpressure and Express/Fastify wiring, see DeepSeek API streaming.
Step 6: Thinking mode
Thinking is a request parameter, not a separate model ID. Both deepseek-flash and deepseek-v4-pro support it, it is on by default with effort high, and the response returns reasoning_content alongside the final content.
// thinking.js
import { deepseek } from "./client.js";
const resp = await deepseek.chat.completions.create({
model: "deepseek-v4-pro",
messages: [
{ role: "user", content: "Plan a zero-downtime Postgres major-version upgrade." },
],
reasoning_effort: "max",
// DeepSeek-specific field: the Node SDK sends it in the request body unchanged.
thinking: { type: "enabled" },
});
const msg = resp.choices[0].message;
console.log("--- reasoning ---");
console.log(msg.reasoning_content);
console.log("--- answer ---");
console.log(msg.content);
reasoning_effort accepts "low", "high" or "max"; DeepSeek’s 13 August 2026 guidance is low for simple tasks, high for daily agent tasks and max for more complex scenarios. To switch thinking off — for latency-sensitive calls or FIM completion — send thinking: { type: "disabled" }. Avoid a small max_tokens in thinking mode; if a response stops early, DeepSeek’s API reference says finish_reason is "length". What to do with reasoning_content in later turns depends on tools: without tools it is ignored, and with tools the reasoning of all previous turns must be passed back (see Step 8).
Step 7: JSON mode

JSON mode is designed to return valid JSON, not guaranteed. DeepSeek’s JSON Output guide says you must (1) set response_format to { type: "json_object" }, (2) include the word “json” and an example of the desired format in the prompt, and (3) set max_tokens reasonably so the response cannot be truncated mid-object. The API may also occasionally return empty content — handle that.
// json.js
import { deepseek } from "./client.js";
const resp = await deepseek.chat.completions.create({
model: "deepseek-flash",
response_format: { type: "json_object" },
messages: [
{
role: "system",
content:
'Reply as json. Schema example: {"sentiment":"pos|neg|neu","score":0.0}',
},
{ role: "user", content: "The deploy went fine but the dashboards are down." },
],
max_tokens: 500,
temperature: 0,
thinking: { type: "disabled" }, // so that temperature applies
});
const raw = resp.choices[0].message.content ?? "";
let parsed;
try {
parsed = JSON.parse(raw);
} catch {
parsed = { error: "invalid_or_empty", raw };
}
console.log(parsed);
For schema validation, layer Zod or Ajv on top of JSON.parse. The DeepSeek API JSON mode reference covers retry strategies in detail.
Step 8: Tool calling
Tool calling uses the OpenAI-compatible tools array. Declare functions, run the model, then execute the tool and feed the result back until the model answers without a tool call. In thinking mode (the default), every request that carries tools must include the earlier assistant messages with their reasoning_content, or the API returns a 400 error — pushing the returned message object, as below, does that.
// tools.js
import { deepseek } from "./client.js";
const tools = [
{
type: "function",
function: {
name: "get_weather",
description: "Get current weather for a city",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
},
},
];
function getWeather({ city }) {
return { city, tempC: 11, conditions: "drizzle" }; // replace with a real lookup
}
const messages = [{ role: "user", content: "Weather in Dublin?" }];
while (true) {
const resp = await deepseek.chat.completions.create({
model: "deepseek-flash",
messages,
tools,
thinking: { type: "enabled" },
reasoning_effort: "high",
});
const msg = resp.choices[0].message;
messages.push(msg); // keeps content, reasoning_content and tool_calls
if (!msg.tool_calls?.length) {
console.log(msg.content);
break;
}
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(getWeather(args)),
});
}
}
Tool calling works in both thinking and non-thinking modes. For agent-style loops, see DeepSeek API function calling.
Step 9: Cost logging
Wrap the client so every call writes a cost line. Costs depend on three token buckets — cache hit, cache miss, and output — on the model, and on whether the call ran at peak or off-peak time. DeepSeek’s API reference returns the buckets in usage as prompt_cache_hit_tokens, prompt_cache_miss_tokens and completion_tokens. As of October 2026, the official pricing page lists, in USD per 1M tokens (peak / off-peak): deepseek-flash $0.006 / $0.003 cache hit, $0.30 / $0.15 cache miss, $1.20 / $0.60 output; deepseek-v4-pro $0.044 / $0.022 cache hit, $1.32 / $0.66 cache miss, $3.96 / $1.98 output. Peak hours are 01:00–04:00 and 06:00–10:00 UTC, Monday to Friday, excluding Chinese public holidays; all other hours are off-peak. Confirm current numbers on the DeepSeek API pricing page before locking in a budget.
// cost.js — USD per 1M tokens, as listed on DeepSeek's pricing page (October 2026)
const RATES = {
"deepseek-flash": { peak: { hit: 0.006, miss: 0.30, out: 1.20 }, offPeak: { hit: 0.003, miss: 0.15, out: 0.60 } },
"deepseek-v4-pro": { peak: { hit: 0.044, miss: 1.32, out: 3.96 }, offPeak: { hit: 0.022, miss: 0.66, out: 1.98 } },
};
// Peak: 01:00–04:00 and 06:00–10:00 UTC, Monday to Friday.
// Chinese public holidays are off-peak all day; this helper does not know them.
export function isPeak(date = new Date()) {
const day = date.getUTCDay(); // 0 = Sunday, 6 = Saturday
const h = date.getUTCHours();
return day >= 1 && day <= 5 && ((h >= 1 && h < 4) || (h >= 6 && h < 10));
}
export function priceUSD(usage, model, date = new Date()) {
const table = RATES[model] ?? RATES["deepseek-flash"]; // legacy Flash names bill at the Flash price
const r = isPeak(date) ? table.peak : table.offPeak;
const hit = usage.prompt_cache_hit_tokens ?? 0;
const miss = usage.prompt_cache_miss_tokens ?? usage.prompt_tokens - hit;
const out = usage.completion_tokens;
return (hit * r.hit + miss * r.miss + out * r.out) / 1_000_000;
}
Worked example: deepseek-flash at scale
1,000,000 calls with a 2,000-token cached system prompt, a 200-token user message (uncached against that prefix), and a 300-token reply:
- Cached input: 2,000,000,000 × $0.006/M = $12.00 peak; × $0.003/M = $6.00 off-peak
- Uncached input: 200,000,000 × $0.30/M = $60.00 peak; × $0.15/M = $30.00 off-peak
- Output: 300,000,000 × $1.20/M = $360.00 peak; × $0.60/M = $180.00 off-peak
- Total: $432.00 if run entirely in peak hours; $216.00 if run entirely off-peak
The same workload on deepseek-v4-pro costs $88.00 + $264.00 + $1,188.00 = $1,540.00 at peak, or $44.00 + $132.00 + $594.00 = $770.00 off-peak — about 3.6 times the Flash spend. A real workload usually straddles both periods, so weight the two totals by your expected traffic split. Default to Flash; promote individual call sites to Pro only when measurements justify it.
Step 10: Verify it worked
Three quick checks before you call this done:
- Run
node hello.jsand confirm a 200-OK response with a non-emptycontentfield. - Call
await deepseek.models.list()(DeepSeek’sGET /modelsendpoint) and confirmdeepseek-flashanddeepseek-v4-proappear in the list. - Make a deliberate 401 (bad key) call and a deliberately invalid request — confirm your error handler logs the status code and error message of both clearly.
Common errors and fixes

| Symptom | Cause | Fix |
|---|---|---|
401 Authentication Fails |
Wrong or missing key | Check .env loads before the client module runs; print process.env.DEEPSEEK_API_KEY?.slice(0,5) |
402 Insufficient Balance |
Balance used up | Top up on the platform; the key is fine |
422 Invalid Parameters |
The request contains invalid parameters | Fix the field named in the error message; check the model ID is deepseek-flash or deepseek-v4-pro |
429 Rate Limit Reached |
Requests too fast, or over the per-account concurrency limit (2,500 for deepseek-flash, 500 for deepseek-v4-pro) |
Exponential backoff and a concurrency cap; the SDK’s maxRetries covers transient cases |
Empty content in JSON mode |
Truncation or model edge case | Raise max_tokens; include “json” + example in the prompt; retry once |
| HTTP 400 inside a tool-calling loop | Thinking mode with tools, but earlier reasoning_content not passed back |
Push the full returned message object onto messages, not just its content |
| Long thinking calls time out | A custom client timeout set too low |
The SDK default is 10 minutes; raise any custom value |
For a fuller mapping, see DeepSeek API error codes.
Next steps
- Add prefix caching deliberately — see DeepSeek context caching.
- Wire it into a chat UI — the DeepSeek Streamlit app tutorial covers the streaming UX patterns even if you port them to React.
- Compare the SDK ergonomics across languages in our DeepSeek OpenAI SDK compatibility notes.
- Browse more DeepSeek tutorials for adjacent integrations like Discord bots and RAG pipelines.
How do you integrate DeepSeek with Node.js?
Install the official OpenAI SDK with npm install openai, instantiate it with baseURL: "https://api.deepseek.com" and your DeepSeek API key, then call chat.completions.create with model: "deepseek-flash" or "deepseek-v4-pro". The wire format is the same as OpenAI’s, so existing code paths transfer with a small config change; DeepSeek-specific fields such as thinking go straight into the request object. Full walkthrough in our DeepSeek API getting started guide.
Which model ID should you use in 2026?
Use deepseek-flash (DeepSeek-V4.1-Flash-1017) for most workloads, and deepseek-v4-pro (DeepSeek-V4-Pro-0813) where you have measured a quality gain worth its higher price. The older deepseek-chat and deepseek-reasoner IDs were scheduled for retirement on 2026-07-24 at 15:59 UTC and are no longer listed in DeepSeek’s docs. Background on the lineage in our DeepSeek V4 overview.
Does DeepSeek’s API remember previous messages?
No. The API is stateless — you must resend the full conversation history on every request. The web chat at chat.deepseek.com keeps session history for users, but the developer API does not keep your conversation between calls. Maintain a messages array in your Node service and append each user and assistant turn before the next request. See DeepSeek API documentation.
Can you stream responses to a Node.js client?
Yes. Pass stream: true to chat.completions.create and iterate the returned async iterable. Each chunk is a server-sent-event delta with the next text fragment in chunk.choices[0].delta.content. When thinking mode is enabled, the reasoning streams first on delta.reasoning_content. See DeepSeek API streaming for SSE plumbing across Express and Fastify.
How much does a typical Node.js app cost on DeepSeek?
It depends on the model, caching and time of day. As a worked example on deepseek-flash, one million calls with a 2,000-token cached system prompt, 200-token uncached user input and 300-token reply cost $432.00 if run entirely at peak ($12.00 cached input + $60.00 uncached input + $360.00 output) or $216.00 entirely off-peak ($6.00 + $30.00 + $180.00). The same workload on deepseek-v4-pro is $1,540.00 peak or $770.00 off-peak. Confirm current rates on the DeepSeek API pricing page.
Sources & Methodology
Primary sources
- OfficialDeepSeek API Docs — Your First API CallBase URLs, model names, Node.js sample passing thinking and reasoning_effort at the top level; accessed 24 Oct 2026Last checked: October 24, 2026
- PricingDeepSeek API Docs — Models & PricingPeak and off-peak rates for deepseek-flash and deepseek-v4-pro, peak hours, 1M context, 384K output, vision on deepseek-flash only; accessed 24 Oct 2026Last checked: October 24, 2026
- OfficialDeepSeek API Docs — Thinking ModeThinking on by default (effort high), effort values and mapping, temperature ignored, reasoning_content handling (ignored without tools; must be passed back with tools, else 400); accessed 24 Oct 2026Last checked: October 24, 2026
Technical references
- OfficialDeepSeek API Docs — Create Chat Completionusage fields prompt_cache_hit_tokens / prompt_cache_miss_tokens, finish_reason 'length', response_format text|json_object; accessed 24 Oct 2026Last checked: October 24, 2026
- OfficialDeepSeek API Docs — JSON Outputjson_object, 'json' plus example in prompt, max_tokens, occasional empty content; accessed 24 Oct 2026Last checked: October 24, 2026
- OfficialDeepSeek API Docs — List ModelsGET /models; accessed 24 Oct 2026Last checked: October 24, 2026
- OfficialDeepSeek API Docs — Error Codes and Rate Limit & Isolation401, 402, 422, 429; concurrency 2,500 / 500 per account; accessed 24 Oct 2026Last checked: October 24, 2026
- SDKopenai on npm (openai-node 7.43.0)Current version, engines node >=22.0.0, create() parameter types, default timeout 10 minutes and maxRetries 2; accessed 24 Oct 2026Last checked: October 24, 2026
Context sources
- OfficialDeepSeek API Docs — Change LogModel versions, legacy names, 10 Sep 2026 'change the model name to deepseek-flash', 13 Aug 2026 effort guidance; accessed 24 Oct 2026Last checked: October 24, 2026
Methodology
Information in this article was checked against the official DeepSeek documentation, the article's cited primary sources, and the deepseekai.guide pricing dataset on the date shown above. Where third-party numbers are quoted, the originating source is linked inline so the reader can verify and re-check.
Data confidence
High for facts cited inline with primary-source links; medium for context narrative.
Editorial note
AI tooling moves quickly. If you spot an outdated number or a broken link, the contact link in the footer is the fastest way to flag it for the editorial team.
