How to Use DeepSeek with LangChain: A V4 Python Tutorial

You want a working LangChain pipeline that calls DeepSeek’s current models, returns clean structured output, and does not depend on the legacy deepseek-chat ID that has since been retired. That is a fair ask: as of October 2026, LangChain’s own ChatDeepSeek integration page still shows model="deepseek-chat" in its examples. This guide walks through using DeepSeek with LangChain end to end: installing the official langchain-deepseek package, wiring it to deepseek-flash and deepseek-v4-pro, controlling thinking mode, streaming tokens, calling tools, and dropping the model into a small RAG chain. By the end you will have a Python script that runs, plus the rates you need to decide which model to use.
What you’ll build

The goal is a single Python project that demonstrates the four patterns most teams actually need: a plain chat call, a streaming call, a tool-calling call, and a retrieval-augmented chain. All four use the same ChatDeepSeek class, just configured differently. You can swap the model= string between deepseek-flash and deepseek-v4-pro without touching anything else.
Before any of that works, two facts to anchor on. First, DeepSeek’s API lists two models today: deepseek-flash, served by DeepSeek-V4.1-Flash-1017 (released 17 October 2026), and deepseek-v4-pro, served by DeepSeek-V4-Pro-0813 (released 13 August 2026). Both publish their weights under the MIT licence. Second, LangChain ships an official integration package, langchain-deepseek (version 1.1.1 as of October 2026). It is published on PyPI under the MIT licence with langchain-core and langchain-openai as required dependencies — and its ChatDeepSeek class subclasses LangChain’s OpenAI chat-model base, BaseChatOpenAI.
Prerequisites
- Python 3.10 or later (langchain-deepseek 1.1.1 requires it).
- A DeepSeek API key from the developer console — see our walkthrough on how to get a DeepSeek API key if you have not done it yet.
- A virtual environment (venv, uv, or poetry) to keep dependencies isolated.
- A topped-up or granted DeepSeek balance. Calls are billed per token at the rates in Step 3; for scale, the Step 2 call is capped at
max_tokens=1024, so its output costs at most 1,024 × $1.20 / 1M ≈ $0.0012 at peak ($0.0006 off-peak), and the Step 4deepseek-v4-procall, capped at 16,384 tokens, at most 16,384 × $3.96 / 1M ≈ $0.065 at peak ($0.032 off-peak). - Familiarity with the basics of the DeepSeek API getting started flow helps but is not required.
Step 1: Install the LangChain DeepSeek package
The LangChain DeepSeek integration lives in the langchain-deepseek package, which pulls in langchain-core and langchain-openai. Install it with pip in your virtual environment:
pip install -U langchain-deepseek
If you plan to follow the RAG example in Step 5, also install an embedding provider. DeepSeek’s API documents no embeddings endpoint, so pair it with whatever embedder you already use; the example below runs a local sentence-transformers model through langchain-huggingface and keeps vectors in LangChain’s built-in in-memory store:
pip install -U "langchain-huggingface[full]"
Then export your key as the DEEPSEEK_API_KEY environment variable, which ChatDeepSeek reads automatically:
export DEEPSEEK_API_KEY="sk-..."
Step 2: A minimal ChatDeepSeek call
The wrapper class is ChatDeepSeek. Because it inherits from BaseChatOpenAI, the OpenAI-style arguments you already know — temperature, max_tokens, timeout, reasoning_effort, extra_body — work as expected. The class reads the API key from DEEPSEEK_API_KEY and points at DeepSeek’s OpenAI-compatible base URL by default (override it with api_base or the DEEPSEEK_API_BASE variable), so the only required argument is the model name.
Thinking mode is on by default for both DeepSeek models. For a plain chat call, switch it off through extra_body — DeepSeek’s documented field — which also makes temperature take effect. Save this as quickstart.py:
from langchain_deepseek import ChatDeepSeek
llm = ChatDeepSeek(
model="deepseek-flash",
temperature=1.3, # DeepSeek's suggestion for general conversation
max_tokens=1024,
timeout=60,
max_retries=2,
extra_body={"thinking": {"type": "disabled"}}, # plain chat, no reasoning step
)
messages = [
("system", "You are a concise technical writer."),
("human", "Explain MoE routing in two sentences."),
]
response = llm.invoke(messages)
print(response.content)
print(response.usage_metadata)
Two things to notice. The messages list uses LangChain’s tuple shorthand instead of explicit HumanMessage / SystemMessage objects — both work, the tuples are shorter. And response.usage_metadata returns a dict like {"input_tokens": …, "output_tokens": …, "total_tokens": …}; langchain-deepseek also copies DeepSeek’s cache-hit count into input_token_details["cache_read"], which is what you wire into your own cost tracking.
Behind the wrapper, every call hits POST /chat/completions on DeepSeek’s OpenAI-compatible endpoint. The API is stateless: DeepSeek does not remember prior turns server-side, so multi-turn conversations require resending the full messages array every request. The web chat and mobile app behave differently — they keep session history for you. The API does not.
Step 3: Pick the right model
The two models share the same API surface and the same 1M-token context window with up to 384K output tokens. The differences are price, concurrency and image input. Here are the current rates, per 1M tokens, from the official DeepSeek pricing page as of October 2026 (peak / off-peak):
| Model | Input cache hit | Input cache miss | Output | Notes |
|---|---|---|---|---|
deepseek-flash |
$0.006 / $0.003 | $0.30 / $0.15 | $1.20 / $0.60 | Default choice; the only current model with image input; 2,500 concurrent requests per account |
deepseek-v4-pro |
$0.044 / $0.022 | $1.32 / $0.66 | $3.96 / $1.98 | 3.3× Flash’s output price; no image input; 500 concurrent requests per account |
Peak hours are 01:00–04:00 and 06:00–10:00 UTC, Monday to Friday, excluding Chinese public holidays; every other hour bills at the off-peak rate. DeepSeek’s 10 September 2026 release note for V4.1-Flash says tests by multiple parties put it ahead of V4-Pro on performance, cost, speed and total runtime, so start with deepseek-flash and switch only where your own evaluation favours Pro. For worked cost examples, see our DeepSeek API pricing page or run the numbers in our DeepSeek pricing calculator.
Step 4: Enable thinking mode

Thinking mode is a request parameter, not a separate model ID, and both models support it. It is on by default with effort high; with LangChain you set the effort through reasoning_effort ("low", "high" or "max") and the toggle through extra_body, exactly as with the OpenAI SDK:
from langchain_deepseek import ChatDeepSeek
reasoner = ChatDeepSeek(
model="deepseek-v4-pro",
max_tokens=16384,
reasoning_effort="high",
extra_body={"thinking": {"type": "enabled"}},
)
result = reasoner.invoke(
"A train leaves Chicago at 9:00 averaging 60 mph. "
"A second train leaves the same station at 10:30 averaging 80 mph "
"on a parallel track. When does the second train catch up?"
)
print("Reasoning content:")
print(result.additional_kwargs.get("reasoning_content"))
print("\nAnswer:")
print(result.content)
Thinking-mode responses return reasoning_content alongside the final content. langchain-deepseek copies the reasoning text to response.additional_kwargs["reasoning_content"]; the user-facing answer stays on response.content. DeepSeek’s guidance is low for simple tasks, high for daily agent tasks and max for more complex scenarios.
Temperature guidance from DeepSeek’s own documentation, which is worth pinning to the wall — it applies to non-thinking mode only, because in thinking mode temperature is accepted but has no effect:
- 0.0 — code generation, math
- 1.0 — data analysis, cleaning
- 1.3 — general conversation, translation
- 1.5 — creative writing
Legacy IDs in older examples
Older codebases, and LangChain’s integration page as of October 2026, still show deepseek-chat or deepseek-reasoner, together with a note that deepseek-reasoner does not support tool calling or structured output. That guidance reflects the V3 era. Both legacy IDs were scheduled for retirement on 2026-07-24 at 15:59 UTC and are no longer listed in DeepSeek’s docs, so replace them with deepseek-flash or deepseek-v4-pro. The name deepseek-v4-flash is still accepted, but DeepSeek serves it with DeepSeek-V4.1-Flash-1017 at the Flash price. Today both models support tool calls in thinking and non-thinking mode. Migration is a one-line model= swap; the base URL does not change.
Step 5: Streaming, tool calling, and RAG
Streaming tokens
For chat UIs, streaming is non-negotiable. ChatDeepSeek implements LangChain’s standard .stream() method; with the non-thinking llm from Step 2:
for chunk in llm.stream("Write a haiku about MoE routing."):
print(chunk.content, end="", flush=True)
When thinking mode is enabled, reasoning chunks stream before the answer chunks; each one carries its text in chunk.additional_kwargs["reasoning_content"], so you can separate the two:
for chunk in reasoner.stream("Is 1,001 a prime number?"):
reasoning = chunk.additional_kwargs.get("reasoning_content")
if reasoning:
print(reasoning, end="", flush=True)
elif chunk.content:
print(chunk.content, end="", flush=True)
Tool calling
ChatDeepSeek exposes bind_tools() in the standard LangChain shape. One DeepSeek-specific rule matters here: in thinking mode, a request that carries tools must pass back the reasoning_content of all earlier assistant turns, or the API returns a 400 error. The langchain-deepseek 1.1.1 source does not add reasoning_content when it serialises earlier assistant messages, so for multi-step tool loops through ChatDeepSeek, disable thinking:
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_deepseek import ChatDeepSeek
@tool
def get_weather(city: str) -> str:
"""Return current weather for a city."""
return f"{city}: 14°C, light rain" # stub: call a real weather API here
tool_llm = ChatDeepSeek(
model="deepseek-flash",
extra_body={"thinking": {"type": "disabled"}},
)
agent = tool_llm.bind_tools([get_weather])
messages = [HumanMessage("What's the weather in Manchester?")]
ai_msg = agent.invoke(messages)
print(ai_msg.tool_calls)
messages.append(ai_msg)
for call in ai_msg.tool_calls:
messages.append(get_weather.invoke(call)) # returns a ToolMessage
final = agent.invoke(messages)
print(final.content)
A note on strict=True: DeepSeek’s strict mode for tool schemas is a Beta feature served from https://api.deepseek.com/beta, and ChatDeepSeek switches to that endpoint automatically when you pass strict=True to bind_tools() or with_structured_output() (provided api_base is left at its default). Per the langchain-deepseek docstring, strict mode requires every object property to be marked as required in the schema.
Structured JSON output
with_structured_output() uses DeepSeek’s tool calling by default (method="function_calling"; "json_schema" is mapped to it) or JSON mode with method="json_mode":
from pydantic import BaseModel, Field
class Ticket(BaseModel):
"""Fields extracted from a support message."""
product: str = Field(description="Product or page affected")
severity: int = Field(description="1 (low) to 5 (critical)")
extractor = tool_llm.with_structured_output(Ticket)
print(extractor.invoke("The billing page has crashed for every customer since 9am."))
For response_format={"type": "json_object"} (JSON mode), remember that it is designed to return valid JSON, not guaranteed. Always include the word “json” plus a small example schema in the prompt, and set max_tokens high enough that the response cannot truncate mid-object. DeepSeek’s docs also note that the API may occasionally return empty content — handle that path explicitly. Our DeepSeek API JSON mode guide covers the gotchas.
A small RAG chain
Drop ChatDeepSeek into a standard LangChain retriever + prompt + LLM pipeline. Because DeepSeek has no first-party embeddings API, pair it with a separate embedder (here, a local sentence-transformers model). For a deeper walkthrough see our DeepSeek RAG tutorial.
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_deepseek import ChatDeepSeek
from langchain_huggingface import HuggingFaceEmbeddings
docs = [
"The deepseek-flash API model is served by DeepSeek-V4.1-Flash-1017.",
"The deepseek-v4-pro API model is served by DeepSeek-V4-Pro-0813.",
"Both DeepSeek API models have a 1M-token context window.",
]
emb = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
store = InMemoryVectorStore.from_texts(docs, emb)
retriever = store.as_retriever(search_kwargs={"k": 2})
def format_docs(found):
return "\n".join(doc.page_content for doc in found)
prompt = ChatPromptTemplate.from_messages([
("system", "Answer using only the provided context. If unsure, say so."),
("human", "Context:\n{context}\n\nQuestion: {question}"),
])
llm = ChatDeepSeek(
model="deepseek-flash",
extra_body={"thinking": {"type": "disabled"}},
)
chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
print(chain.invoke("Which model serves deepseek-flash?"))
Step 6: Verify it worked
A working integration shows three signs:
response.contentcontains a sensible answer.response.usage_metadatais populated with non-zero token counts.response.response_metadata["model_name"]shows the model name the API reported back.
If you enabled thinking mode, also check that additional_kwargs["reasoning_content"] is non-empty. If it is missing, check that no extra_body={"thinking": {"type": "disabled"}} is set on that model instance.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
AuthenticationError: 401 |
Missing or wrong API key | Re-export DEEPSEEK_API_KEY; restart the shell |
| 400 or 422 error that mentions the model | Typo in the model name, or a retired legacy name such as deepseek-chat |
Use deepseek-flash or deepseek-v4-pro |
| 400 error on the second turn of a tool loop | Thinking mode with tools, and earlier reasoning_content not passed back |
Disable thinking for that model instance (see Step 5) |
Empty content in JSON mode |
Prompt missing the word “json”, or max_tokens too low |
Add a JSON schema example; raise max_tokens |
Answer cut off (finish_reason is "length") |
max_tokens too low for a long reasoning-plus-answer response |
Raise max_tokens (output can reach 384K tokens) or lower reasoning_effort |
| Tool not called | Tool description too vague | Rewrite the docstring to describe when to use the tool |
RateLimitError (HTTP 429) |
More concurrent requests than your account’s limit (2,500 for deepseek-flash, 500 for deepseek-v4-pro) |
Retry with exponential backoff; see DeepSeek API rate limits |
When LangChain is the wrong choice
Not every DeepSeek project benefits from a LangChain wrapper. For a single-shot API call from a script or a Lambda, the official OpenAI SDK pointed at https://api.deepseek.com is shorter and has fewer moving parts. LangChain earns its weight when you need orchestration: chains of prompts, retrievers, tool calls, agents, memory, or the LangSmith tracing surface. If your project is “send one prompt, get one answer”, call POST /chat/completions directly. If your project is “retrieve, plan, call tools, summarise”, LangChain saves real time.
Next steps

- Compare the LangChain integration with the equivalent flow for a different framework — see our DeepSeek with LlamaIndex walkthrough.
- Wire the same model into a Streamlit UI with our DeepSeek Streamlit app tutorial.
- Browse other patterns in our step-by-step DeepSeek guides.
- Read more about the model family in the DeepSeek V4 overview.
Frequently asked questions
How do I install the LangChain DeepSeek integration?
Run pip install -U langchain-deepseek in a Python 3.10+ virtual environment, then export your key as DEEPSEEK_API_KEY. The package pulls in langchain-core and langchain-openai as dependencies. Import ChatDeepSeek from langchain_deepseek. For a full walkthrough including how to obtain a key, see our guide on getting a DeepSeek API key.
What model name should I use with ChatDeepSeek today?
Use deepseek-flash (DeepSeek-V4.1-Flash-1017) as the default, or deepseek-v4-pro (DeepSeek-V4-Pro-0813), which costs 3.3× as much per output token. The legacy IDs deepseek-chat and deepseek-reasoner were scheduled for retirement on 2026-07-24 at 15:59 UTC and are no longer listed in DeepSeek’s docs, so update any code that still uses them. See the DeepSeek V4 overview for background.
Does ChatDeepSeek support tool calling and structured output?
Yes, on both current models. ChatDeepSeek provides bind_tools() and with_structured_output(), which use DeepSeek’s tool calling or JSON mode. Passing strict=True sends the request to DeepSeek’s Beta endpoint for strict schema adherence. For multi-step tool loops, disable thinking on that model instance, because thinking mode with tools requires earlier reasoning_content to be passed back. For deeper detail, browse our DeepSeek API function calling reference.
Can I run DeepSeek with LangChain locally instead of via the API?
Yes, with limits. The current open weights are MIT-licensed but large — about 510 GB for DeepSeek-V4.1-Flash-1017 and about 893 GB for DeepSeek-V4-Pro-0813 on Hugging Face — so they need a multi-GPU server running vLLM or SGLang; point ChatDeepSeek at that server’s OpenAI-compatible endpoint with api_base. On a laptop, the smaller DeepSeek R1 distills run in Ollama and plug into LangChain through ChatOllama; note that Ollama’s deepseek-v4.1-flash and deepseek-v4-pro tags are cloud-only. Our running DeepSeek on Ollama walkthrough covers the setup.
How much does a typical LangChain + DeepSeek workflow cost?
LangChain adds no fee of its own; you pay DeepSeek’s per-token rates. As of October 2026, deepseek-flash costs $0.006 (cache hit), $0.30 (cache miss) and $1.20 (output) per 1M tokens at peak, and $0.003, $0.15 and $0.60 off-peak; deepseek-v4-pro costs $0.044, $1.32 and $3.96 at peak, and $0.022, $0.66 and $1.98 off-peak. Multiply each token bucket from usage_metadata by its rate, or estimate your own numbers with our DeepSeek pricing calculator.
Sources & Methodology
Primary sources
- OfficialDeepSeek API documentationEndpoints, request/response shape, model IDsLast checked: October 25, 2026
- Model cardDeepSeek on Hugging FaceWeights, license, usage notes per modelLast checked: October 25, 2026
- Official documentationDeepSeek pricing pageModel names, peak/off-peak rates per 1M tokens, peak hours, 1M context / 384K output, image input on deepseek-flash onlyLast checked: October 25, 2026
- Official documentationDeepSeek API docs — Thinking ModeThinking on by default, reasoning_effort low/high/max, extra_body toggle, reasoning_content, temperature ignored, reasoning_content pass-back with toolsLast checked: October 25, 2026
- Official documentationDeepSeek API docs — Tool CallsTool calls in thinking and non-thinking mode; strict mode (Beta) via https://api.deepseek.com/betaLast checked: October 25, 2026
- Official documentationDeepSeek API docs — JSON Outputjson_object, word 'json' plus example, max_tokens, occasional empty contentLast checked: October 25, 2026
- Official documentationDeepSeek API docs — Rate Limit & IsolationConcurrency limits 2,500 / 500 and HTTP 429Last checked: October 25, 2026
- Official announcementDeepSeek news — V4.1-Flash release (10 Sep 2026)Attributed statement that tests by multiple parties put V4.1-Flash ahead of V4-ProLast checked: October 25, 2026
Technical references
- Technical reportDeepSeek-V3 technical report (arXiv:2412.19437)V3 architecture and training methodologyLast checked: October 25, 2026
- Technical reportDeepSeek-R1 paper (arXiv:2501.12948)R1 reasoning approach, distillation resultsLast checked: October 25, 2026
- Package registrylangchain-deepseek on PyPIVersion 1.1.1, MIT licence, Python 3.10+, dependencies langchain-core and langchain-openaiLast checked: October 25, 2026
- Source codelangchain-deepseek source (LangChain repository)ChatDeepSeek subclasses BaseChatOpenAI; DEEPSEEK_API_BASE; reasoning_content in additional_kwargs; cache_read usage; strict=True uses the beta endpoint; reasoning_content not re-sent on earlier assistant messagesLast checked: October 25, 2026
- Tool documentationLangChain docs — ChatDeepSeek integrationInstall command; page still shows deepseek-chat examples (checked 25 Oct 2026)Last checked: October 25, 2026
- Package registrylangchain-huggingface on PyPIHuggingFaceEmbeddings and the [full] extra for sentence-transformersLast checked: October 25, 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.
