DeepSeek Node.js Integration: A Production Quickstart for V4

Build a DeepSeek Node.js integration with the OpenAI SDK, V4 model IDs, streaming, JSON mode and tool calls. Ship a production client today.

Tutorials·April 25, 2026·By DS Guide Editorial
Title card with the eyebrow TUTORIAL · NODE.JS and the title Node.js Integration, the tagline “Call DeepSeek with the official openai npm package.” and four stats: 22 Node.js minimum, 2 Current models, 1M Context window, and HIGH Default effort, beside an open-manual motif.

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

Title card with the eyebrow NODE.JS · STREAMING and the title Stream Tokens, the tagline “Iterate stream: true chunks as they arrive.” and four stats: SSE Event stream, CoT Streams first, 1M Context window, and 384K Max output, beside a typewriter motif.

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 openai package (7.43.0 on npm on 24 October 2026) declares node >=22.0.0 in 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

Title card with the eyebrow NODE.JS · FIRST CALL and the title Your First Call, the tagline “Point baseURL at DeepSeek; keep the SDK.” and four stats: 2 Values to change, POST Chat endpoint, ENV Key in env var, and 384K Max output, beside a compact-desktop motif.

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

Title card with the eyebrow NODE.JS · JSON MODE and the title JSON Mode, the tagline “json_object output: say json and show an example.” and four stats: 3 Rules to follow, WORD json in prompt, 0.0 Temp, non-think, and 1 Retry if empty, beside a punch-cards motif.

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:

  1. Run node hello.js and confirm a 200-OK response with a non-empty content field.
  2. Call await deepseek.models.list() (DeepSeek’s GET /models endpoint) and confirm deepseek-flash and deepseek-v4-pro appear in the list.
  3. 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

Diagram of a Node.js server proxying a streamed DeepSeek V4 response back to a browser client.
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

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

Last source check: October 24, 2026

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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *