DeepSeek Python Integration with V4: A Practical Tutorial

Set up DeepSeek Python integration with the OpenAI SDK, V4 model IDs, streaming, JSON mode and cost math. Build your first call now.

Tutorials·April 25, 2026·By DS Guide Editorial
Title card with the eyebrow TUTORIAL · PYTHON and the title Python Integration, the tagline “Call DeepSeek with the official openai SDK.” and four stats: 3.10 Python minimum, 2 Current models, 1M Context window, and HIGH Default effort, beside a punch-cards motif.

You have a DeepSeek API key, an existing Python project, and a question that gets in the way of every first call: do you really need a new SDK, or does the OpenAI client already cover it? This tutorial answers that, then walks through a complete DeepSeek Python integration against the two current API models, deepseek-flash (served by DeepSeek-V4.1-Flash-1017) and deepseek-v4-pro (served by DeepSeek-V4-Pro-0813). By the end you will have a working client, a streaming example, a JSON-mode call that does not silently truncate, a thinking-mode pattern, and a cost calculation you can paste into a finance review. Every snippet is written for Python 3.10 or later with the current openai package and uses only the fields DeepSeek documents.

What you will build

Title card with the eyebrow PYTHON · FIRST CALL and the title Your First Call, the tagline “Set base_url and a DeepSeek key; keep the SDK.” and four stats: 2 Values to change, POST Chat endpoint, ENV Key in env var, and 384K Max output, beside a typewriter motif.

You will build a small Python module that talks to the DeepSeek API using the official OpenAI SDK — no DeepSeek-specific package needed. The module will support both current models, switch between thinking and non-thinking mode with a single argument, stream tokens, request structured JSON, and log token usage so you can keep an eye on spend.

If you are migrating older code: the legacy 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, and the legacy names deepseek-v4-flash and deepseek-v4-flash-vision-exp are still accepted but served by DeepSeek-V4.1-Flash-1017. New code should target deepseek-flash or deepseek-v4-pro from day one. If you have not picked up an API key yet, the get a DeepSeek API key walkthrough covers the platform.deepseek.com signup and the first top-up.

Prerequisites

  • Python 3.10+ — the minimum the current openai package supports.
  • OpenAI Python SDK, a current release: pip install -U openai. Version 3.37.0 was the latest on PyPI on 24 October 2026; the parameters used below (reasoning_effort, extra_body, response_format, tools, stream) are all part of its chat.completions.create() signature.
  • A DeepSeek API key with a topped-up or granted balance — calls return HTTP 402 “Insufficient Balance” if the account has run out.
  • An environment variable DEEPSEEK_API_KEY. Do not paste keys into source files.
  • Optional but recommended: python-dotenv for local development. The SDK brings its own HTTP client, so nothing else is required.

Why the OpenAI SDK works

DeepSeek’s docs put it this way: “The DeepSeek API uses an API format compatible with OpenAI/Anthropic. By modifying the configuration, you can use the OpenAI/Anthropic SDK or softwares compatible with the OpenAI/Anthropic API to access the DeepSeek API.” In practice that means swapping two values — base_url and api_key — and pointing the model field at a DeepSeek ID. Chat requests hit POST /chat/completions, the OpenAI-compatible endpoint, with the same JSON body shape you already know. The full reference lives in the DeepSeek OpenAI SDK compatibility guide.

If you prefer the Anthropic SDK, that works too, at a separate base URL: https://api.deepseek.com/anthropic. This article sticks with the OpenAI client because most existing Python codebases already depend on it.

Step 1 — Install the SDK and set environment variables

Create a virtual environment and install the OpenAI client. The shell commands below are bash-flavoured; PowerShell users should use $env:DEEPSEEK_API_KEY syntax.

python -m venv .venv
source .venv/bin/activate
pip install -U openai python-dotenv
export DEEPSEEK_API_KEY="sk-..."

For project-level config, put the key in a .env file at the repo root, add .env to .gitignore, and call load_dotenv() (from dotenv) before you create the client. The DeepSeek API authentication guide covers key rotation and revocation in more detail.

Step 2 — Your first DeepSeek Python integration call

Title card with the eyebrow PYTHON · SETUP and the title Install the SDK, the tagline “pip install -U openai python-dotenv in a venv.” and four stats: 3.10 Python minimum, 2 Packages to add, venv Isolated env, and .env Kept out of git, beside a compact-desktop motif.

Save the following Python script as hello_deepseek.py. It targets deepseek-flash, the cost-efficient default for most workloads, with thinking mode switched on explicitly as in DeepSeek’s “Your First API Call” example.

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com",
)

response = client.chat.completions.create(
    model="deepseek-flash",
    messages=[
        {"role": "system", "content": "You are a precise technical assistant."},
        {"role": "user", "content": "In one sentence, what is a Mixture-of-Experts model?"},
    ],
    reasoning_effort="high",
    extra_body={"thinking": {"type": "enabled"}},
)

print(response.choices[0].message.content)
print("usage:", response.usage)

Run it with python hello_deepseek.py. You should see a one-sentence answer plus a usage block listing prompt, completion and total tokens.

Picking deepseek-flash vs deepseek-v4-pro

The two model IDs share the same API surface but differ in price and features. Both support a 1M-token context, thinking and non-thinking mode, JSON output and tool calls; only deepseek-flash accepts images.

Field deepseek-flash deepseek-v4-pro
Served by DeepSeek-V4.1-Flash-1017 DeepSeek-V4-Pro-0813
Context 1M tokens 1M tokens
Max output 384K tokens 384K tokens
Vision (image input) Yes No
Concurrency limit (per account) 2,500 500
Input, cache hit, per 1M (peak / off-peak) $0.006 / $0.003 $0.044 / $0.022
Input, cache miss, per 1M (peak / off-peak) $0.30 / $0.15 $1.32 / $0.66
Output, per 1M (peak / off-peak) $1.20 / $0.60 $3.96 / $1.98
Best for Default choice: chat, classification, RAG, anything with images Workloads where you have measured a quality gain worth the higher price

Pricing as of October 2026, per the official DeepSeek pricing page. Off-peak rates are half the peak rates; peak hours are 01:00–04:00 and 06:00–10:00 UTC, Monday to Friday, excluding Chinese public holidays, and all other hours are off-peak. deepseek-v4-pro costs 3.3× deepseek-flash on output and 4.4× on cache-miss input, so default to Flash and only escalate when you have a measured quality lift.

Step 3 — Multi-turn conversations (the API is stateless)

This trips up many developers moving from the chat app to the API. The web chat at chat.deepseek.com keeps your history for you. The API does not. You must resend the full messages array on every request. This loop runs in non-thinking mode so that the temperature setting takes effect:

def chat_loop():
    history = [{"role": "system", "content": "You are a precise technical assistant."}]
    while True:
        user_input = input("you: ").strip()
        if not user_input:
            break
        history.append({"role": "user", "content": user_input})
        resp = client.chat.completions.create(
            model="deepseek-flash",
            messages=history,
            temperature=1.3,  # general conversation
            max_tokens=800,
            extra_body={"thinking": {"type": "disabled"}},
        )
        reply = resp.choices[0].message.content
        history.append({"role": "assistant", "content": reply})
        print(f"deepseek: {reply}\n")

Two practical notes. First, history grows on every turn — once it threatens to exceed your budget, summarise older turns into a single system message. Second, the temperature recommendations DeepSeek publishes are: 0.0 for code and maths, 1.0 for data analysis, 1.3 for general chat and translation, 1.5 for creative writing. They apply to non-thinking mode only: thinking mode (the default) accepts temperature without error but ignores it. In thinking mode without tools, you do not need to send reasoning_content back in the history; DeepSeek’s docs say it is ignored if you do.

Step 4 — Streaming responses

For anything user-facing, stream. It cuts perceived latency from “the model is broken” to “the model is fast” without any actual speedup. In thinking mode the reasoning arrives first, on delta.reasoning_content, followed by the answer on delta.content:

stream = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "Explain context caching in three short paragraphs."}],
    stream=True,
    reasoning_effort="high",
    extra_body={"thinking": {"type": "enabled"}},
)

for chunk in stream:
    if not chunk.choices:
        continue
    delta = chunk.choices[0].delta
    if getattr(delta, "reasoning_content", None):
        pass  # reasoning tokens; log or display them separately if you need them
    elif delta.content:
        print(delta.content, end="", flush=True)
print()

Server-sent events arrive as ChatCompletionChunk objects. Catch openai.APIConnectionError around the iterator if you are running over a flaky network — broken streams should not crash your worker. The DeepSeek API streaming reference walks through SSE framing and reconnection patterns.

Step 5 — Thinking mode

Thinking mode is a request parameter on either model, not a separate model ID, and it is on by default with effort high. You control it with two fields: extra_body={"thinking": {"type": "enabled"}} or {"type": "disabled"}, and reasoning_effort set to "low", "high" or "max". The model returns reasoning_content alongside the final content when thinking is on:

response = client.chat.completions.create(
    model="deepseek-v4-pro",
    messages=[
        {"role": "user", "content": "A train leaves Chicago at 60mph; another leaves Denver at 75mph. They start 1,000 miles apart. When do they meet?"},
    ],
    reasoning_effort="max",
    extra_body={"thinking": {"type": "enabled"}},
)

msg = response.choices[0].message
print("THINKING:", getattr(msg, "reasoning_content", None))
print("ANSWER:", msg.content)

DeepSeek’s 13 August 2026 guidance is low for simple tasks, high for daily agent tasks and max for more complex scenarios; other requested values are mapped (minimal → low, medium and xhigh → high). To opt out — for latency-sensitive calls or FIM completion — pass extra_body={"thinking": {"type": "disabled"}}. Avoid a small max_tokens in thinking mode; if a response stops early, check for finish_reason == "length". Code that still uses the old deepseek-reasoner ID needs updating: that ID was scheduled for retirement on 2026-07-24 at 15:59 UTC, and the replacement is deepseek-flash or deepseek-v4-pro with thinking enabled.

Step 6 — JSON mode without footguns

JSON mode is designed to return valid JSON, not guaranteed to. The API can occasionally return empty content, or content that was truncated mid-object because max_tokens was too low. Three rules keep it boring:

  1. Set response_format={"type": "json_object"}.
  2. Include the word json in the system or user prompt, with a small example schema.
  3. Set max_tokens high enough that the response cannot truncate.
import json

schema_hint = """
Return JSON in this exact shape:
{"sentiment": "positive|negative|neutral", "confidence": 0.0-1.0, "key_phrases": ["..."]}
"""

resp = client.chat.completions.create(
    model="deepseek-flash",
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": "You are a sentiment classifier. Respond only with JSON. " + schema_hint},
        {"role": "user", "content": "The dashboard finally loads in under a second. Impressive."},
    ],
    temperature=0.0,
    max_tokens=400,
    extra_body={"thinking": {"type": "disabled"}},
)

raw = resp.choices[0].message.content
if not raw:
    raise RuntimeError("Empty JSON response — retry or fall back.")
result = json.loads(raw)
print(result)

This classifier runs in non-thinking mode so that temperature=0.0 applies. For deeper patterns — including retry-on-empty and Pydantic validation — see the DeepSeek API JSON mode reference.

Step 7 — Tool calling

Tool calling uses the OpenAI tools array verbatim, on both models and in both thinking and non-thinking mode. One DeepSeek-specific rule matters in thinking mode: when a request carries tools, the reasoning_content of all previous assistant turns must be passed back, or the API returns a 400 error. Appending the returned message object, as below, does that for you.

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

def get_weather(city):
    return f"Cloudy, 12°C in {city}"  # replace with a real lookup

messages = [{"role": "user", "content": "What's the weather in Dublin?"}]
while True:
    resp = client.chat.completions.create(
        model="deepseek-flash",
        messages=messages,
        tools=tools,
        reasoning_effort="high",
        extra_body={"thinking": {"type": "enabled"}},
    )
    msg = resp.choices[0].message
    messages.append(msg)  # keeps content, reasoning_content and tool_calls
    if not msg.tool_calls:
        print(msg.content)
        break
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": get_weather(**args),
        })

The model returns a tool_calls entry; your code executes the function, appends a {"role": "tool", ...} message with the result, and calls the API again until the model answers without a tool call. The DeepSeek API function calling guide has a full agent loop example.

Step 8 — Verify it worked

Title card with the eyebrow PYTHON · VERIFY and the title Verify It Worked, the tagline “Five smoke checks before you deploy.” and four stats: 5 Smoke checks, 401 Bad API key, 402 No balance, and 429 Rate limited, beside an open-manual motif.

Run a smoke test that exercises every path you rely on before you deploy. A sensible minimum:

  • A non-streaming call returns a non-empty content and a usage block with non-zero token counts.
  • A streaming call yields at least one chunk with non-empty delta.content.
  • A JSON-mode call parses cleanly through json.loads.
  • A thinking-mode call exposes reasoning_content on the message object.
  • An invalid API key surfaces openai.AuthenticationError rather than crashing.

Common errors and fixes

Error Likely cause Fix
401 Authentication Fails Wrong or missing key Re-export DEEPSEEK_API_KEY; rotate if leaked.
402 Insufficient Balance Account balance used up Top up at platform.deepseek.com; the key is fine.
429 Rate Limit Reached Requests too fast, or over the concurrency limit (2,500 for deepseek-flash, 500 for deepseek-v4-pro, per account) Exponential backoff; cap concurrency.
400 in a tool-calling loop Thinking mode with tools, but earlier reasoning_content not passed back Append the full returned message to messages.
Empty content in JSON mode Prompt missing “json” / truncation Add schema example; raise max_tokens.
Truncated answer mid-sentence max_tokens too low Raise it; check finish_reason == "length".
Calls with an old model ID deepseek-chat / deepseek-reasoner were scheduled for retirement on 2026-07-24 and are no longer listed Switch to deepseek-flash or deepseek-v4-pro.
TypeError: unexpected keyword argument reasoning_effort Very old openai package Upgrade with pip install -U openai.

The DeepSeek API error codes reference lists every status code with retry guidance.

Costing your integration honestly

Every Python integration eventually faces the question “what does this cost at 1M calls a day?” Here is the worked example for deepseek-flash with a 2,000-token system prompt that gets cached, a 200-token user message that does not, and a 300-token reply, at the rates above:

  • Cached input: 2,000 × 1,000,000 = 2,000,000,000 tokens × $0.006/M = $12.00 peak; × $0.003/M = $6.00 off-peak
  • Uncached input: 200 × 1,000,000 = 200,000,000 tokens × $0.30/M = $60.00 peak; × $0.15/M = $30.00 off-peak
  • Output: 300 × 1,000,000 = 300,000,000 tokens × $1.20/M = $360.00 peak; × $0.60/M = $180.00 off-peak
  • Total per 1M calls: $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× the Flash total. A real workload usually straddles both periods, so weight the two totals by the share of traffic you expect in each. The trap is forgetting the uncached-input line: each new user message is a cache miss against the cached prefix, even when the system prompt is reused. Skip that line and you under-budget by about 14% on Flash ($60.00 of $432.00) and about 17% on Pro ($264.00 of $1,540.00).

Two mechanical levers reduce the bill: keep system prompts stable so the cache hit rate stays high (see DeepSeek context caching), and schedule batch work for off-peak hours, when every rate is half price. Setting max_tokens to the output you actually need also caps runaway responses; the 384K maximum is for niche long-form work.

Production hardening

Three habits separate prototypes from running services:

  1. Wrap every call in a retry helper that handles 429 and 5xx with exponential backoff. The OpenAI SDK already retries timeouts, 429 and 5xx responses twice by default (max_retries on the client); raise that or add your own backoff for batch jobs. Do not retry other 4xx errors — they are bugs in your code, not transient failures.
  2. Log token usage on every call. Ship the usage object to your observability stack: DeepSeek’s API reference documents prompt_tokens, completion_tokens, the cache split prompt_cache_hit_tokens / prompt_cache_miss_tokens, and completion_tokens_details.reasoning_tokens. An alert on a sudden spike in reasoning tokens catches drifted prompts before the bill arrives.
  3. Cap concurrency. DeepSeek counts concurrency per account — 2,500 requests for deepseek-flash and 500 for deepseek-v4-pro — and returns HTTP 429 above the limit. An asyncio.Semaphore around the AsyncOpenAI client keeps a runaway worker inside it.

For framework integrations — agent loops, document Q&A, vector retrieval — there are dedicated tutorials for DeepSeek with LangChain and DeepSeek with LlamaIndex.

Next steps

Diagram of a Python app structure calling DeepSeek V4 with config, retry and streaming response.

Once the basic integration is solid, the highest-use follow-ups are: deploying behind a Streamlit or FastAPI front-end, adding retrieval, or migrating an existing OpenAI codebase. Try the DeepSeek RAG tutorial for retrieval, the DeepSeek Streamlit app walkthrough for a quick UI, or browse the full set of DeepSeek tutorials for adjacent topics. If your codebase is JavaScript-first, the parallel DeepSeek Node.js integration guide mirrors this one.

Frequently asked questions

How do you install the DeepSeek Python SDK?

There is no separate DeepSeek Python SDK. DeepSeek’s API is OpenAI-compatible, so you install the OpenAI client with pip install -U openai and point it at https://api.deepseek.com with your DeepSeek API key. The Anthropic SDK also works if you point it at https://api.deepseek.com/anthropic. The DeepSeek SDKs reference covers every supported client.

What model ID should you use in Python?

Use deepseek-flash (DeepSeek-V4.1-Flash-1017) for chat, classification, image input and most workloads, or deepseek-v4-pro (DeepSeek-V4-Pro-0813) where you have measured a quality gain worth its higher price. The legacy deepseek-chat and deepseek-reasoner IDs were scheduled for retirement on 2026-07-24 at 15:59 UTC and are no longer listed in the docs. See the DeepSeek V4 model page for the full lineage.

Does DeepSeek work with the OpenAI Python client without code changes?

Almost — you change two values. Set base_url="https://api.deepseek.com" on the OpenAI() constructor and pass your DeepSeek API key. The wire format is the same for chat completions, streaming, JSON mode, and tool calling, and reasoning_effort is a regular argument of the SDK’s create() method. Only DeepSeek-specific fields such as the thinking toggle (and user_id) go in the extra_body argument. The DeepSeek OpenAI SDK compatibility guide details every edge case.

How do you enable thinking mode from Python?

Thinking mode is on by default with effort high. To set it explicitly, pass reasoning_effort="high" (or "low" / "max") together with extra_body={"thinking": {"type": "enabled"}} on either model; to turn it off, use {"type": "disabled"}. With thinking on, the response object exposes reasoning_content alongside the final content. The DeepSeek API best practices guide covers when each effort level is worth the latency cost.

Can you stream tokens with the DeepSeek Python integration?

Yes. Set stream=True on client.chat.completions.create() and iterate the returned object — each chunk is a ChatCompletionChunk with a delta. When thinking mode is enabled, reasoning streams on delta.reasoning_content before the answer arrives on delta.content. Wrap the iterator in try/except for APIConnectionError so flaky networks do not crash your worker. The DeepSeek API streaming reference walks through reconnection patterns and SSE framing.

Sources & Methodology

Last source check: October 24, 2026

Primary sources

  • 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 — Your First API CallOpenAI/Anthropic base URLs, model names, thinking fields in the Python example; accessed 24 Oct 2026Last checked: October 24, 2026
  • OfficialDeepSeek API Docs — Thinking ModeThinking on by default (effort high), extra_body thinking toggle, effort mapping, temperature ignored in thinking mode, reasoning_content handling in multi-turn and tool calls (400 if not passed back); accessed 24 Oct 2026Last checked: October 24, 2026

Technical references

Context sources

  • OfficialDeepSeek API Docs — Change LogModel versions behind deepseek-flash / deepseek-v4-pro, legacy names, 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 *