Skip to content
, ,

DeepSeek API Python Tutorial (2026): Models, Streaming, JSON, Tools and Errors

9–14 minutes
Programmer writing Python code to call an AI API

The DeepSeek API changed a lot in 2026. The old deepseek-chat and deepseek-reasoner names have been replaced by deepseek-flash and deepseek-v4-pro, thinking mode is now on by default, and prices drop by half outside peak hours.

This tutorial walks you through everything in Python, from your first API key to streaming, JSON output, tool calls, caching and error fixes.

TLDR

  • Use the official OpenAI Python SDK with base_url="https://api.deepseek.com" and your DeepSeek key.
  • Current model names: deepseek-flash (served by DeepSeek-V4.1-Flash-1017 since October 17, 2026) and deepseek-v4-pro. The legacy deepseek-chat and deepseek-reasoner aliases were scheduled for retirement on July 24, 2026.
  • Thinking mode is on by default (effort high). Turn it on or off with extra_body={"thinking": {"type": "enabled"}} or "disabled". The chain of thought comes back in reasoning_content.
  • Context caching is automatic. Check prompt_cache_hit_tokens in the usage data to see savings.
  • Off-peak prices are half of peak. Peak hours are 01:00 to 04:00 and 06:00 to 10:00 UTC.
  • Common errors: 402 means top up your balance, 429 means slow down or reduce concurrency, 503 means the server is busy, so retry with backoff.

Step 1: Get a DeepSeek API key

The DeepSeek API is prepaid. You add credit to your account, and each call is deducted from that balance. Here is how to get set up:

  1. Go to the DeepSeek Platform at platform.deepseek.com and sign up or log in.
  2. Open the Top up page and add a small amount of credit. Without a balance, every call fails with error 402.
  3. Open API Keys and click Create new API key. Give it a name, such as “python-tutorial”.
  4. Copy the key right away and store it somewhere safe. Treat it like a password.
  5. Set it as an environment variable so it never sits in your code.

On macOS or Linux:

export DEEPSEEK_API_KEY="sk-your-key-here"

On Windows PowerShell:

$env:DEEPSEEK_API_KEY="sk-your-key-here"

Step 2: Install the SDK and set the base_url

DeepSeek’s API is compatible with the OpenAI format, so you use the official openai Python package. You only change two things: the API key and the base URL.

  1. Create a project folder and a virtual environment: python -m venv .venv, then activate it.
  2. Install the SDK: pip install openai.
  3. Create the client with DeepSeek’s base URL.
import os
from openai import OpenAI

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

DeepSeek also offers an Anthropic-compatible endpoint at https://api.deepseek.com/anthropic, and it supports the OpenAI Responses API format. This tutorial sticks with Chat Completions, which is the most common choice.

Current model names (and the old aliases)

This is where many older tutorials go wrong. Here is the current picture from DeepSeek’s own docs and changelog:

Model name What it is Status (November 2026)
deepseek-flash DeepSeek-V4.1-Flash-1017; 1M token context, up to 384K output, vision support Current, recommended default
deepseek-v4-pro DeepSeek-V4-Pro, the larger model Current; DeepSeek said it will keep serving it after September 14, 2026
deepseek-v4-flash, deepseek-v4-flash-vision-exp Older V4 Flash names Retired but still accepted; routed to V4.1 Flash at the Flash price
deepseek-chat, deepseek-reasoner Legacy aliases (non-thinking and thinking modes) Scheduled for retirement on July 24, 2026; no longer listed in the docs

The key changes, in date order:

  • April 24, 2026: DeepSeek-V4 launched (V4-Pro and V4-Flash). deepseek-chat and deepseek-reasoner were mapped to V4 Flash’s non-thinking and thinking modes until their retirement date of July 24, 2026.
  • August 13, 2026: V4-Pro reached general availability, thinking effort levels (low, high, max) arrived, and peak/off-peak pricing was announced.
  • September 10, 2026: V4.1 Flash launched as deepseek-flash, with native image understanding and lower prices.
  • October 17, 2026: deepseek-flash was upgraded to V4.1-Flash-1017, with no change to calling or pricing. DeepSeek also opened a Search and Fetch API.

Rule of thumb: use deepseek-flash for almost everything, and try deepseek-v4-pro when you need the strongest answers. Instead of the old deepseek-reasoner, keep thinking mode on. Instead of deepseek-chat, turn thinking off.

Your first request

Save this as hello.py and run python hello.py:

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 helpful assistant."},
        {"role": "user", "content": "Explain what an API is in two sentences."},
    ],
    extra_body={"thinking": {"type": "disabled"}},  # fast, chat-style answer
)

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

Tip: Turning thinking off makes simple answers faster and cheaper. Leave it on (the default) for maths, code and multi-step reasoning.

Streaming responses

Streaming shows text as it is generated, which feels much faster in chat apps. Set stream=True and loop over the chunks:

stream = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "Write a haiku about Python."}],
    extra_body={"thinking": {"type": "disabled"}},
    stream=True,
)

for chunk in stream:
    if not chunk.choices:
        continue
    delta = chunk.choices[0].delta
    if delta.content:
        print(delta.content, end="", flush=True)
print()

While a request waits in the queue, DeepSeek sends : keep-alive comments on streams (and empty lines on non-streaming calls). The SDK handles these for you. If you parse raw HTTP yourself, skip them.

Thinking mode and reasoning_content

Thinking mode makes the model reason before it answers. According to the official guide:

  • It is enabled by default with effort high.
  • Toggle it with extra_body={"thinking": {"type": "enabled"}} or "disabled".
  • Set the effort with the top-level reasoning_effort argument: low, high or max. Other values are mapped (for example medium becomes high).
  • The reasoning text comes back in reasoning_content, next to content.
  • temperature, presence_penalty and frequency_penalty have no effect in thinking mode, but they do not cause errors.
response = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "Which is larger, 9.11 or 9.8? Explain."}],
    reasoning_effort="high",
    extra_body={"thinking": {"type": "enabled"}},
)

msg = response.choices[0].message
print("REASONING:\n", getattr(msg, "reasoning_content", None))
print("\nANSWER:\n", msg.content)

Streaming with thinking works the same way, but each chunk can carry delta.reasoning_content or delta.content:

stream = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "How many r's are in strawberry?"}],
    extra_body={"thinking": {"type": "enabled"}},
    stream=True,
)

reasoning, answer = "", ""
for chunk in stream:
    if not chunk.choices:
        continue
    d = chunk.choices[0].delta
    if getattr(d, "reasoning_content", None):
        reasoning += d.reasoning_content
    elif d.content:
        answer += d.content
print(answer)

Multi-turn rule (important): in a normal chat with no tools, earlier reasoning_content is ignored, so you only need to send back content.

But if your request includes tools, you must pass back the reasoning_content of all previous assistant turns, or the API returns a 400 error. The easiest way is to append the whole response.choices[0].message object to your message list.

JSON output

JSON mode makes the model return valid JSON, which is perfect for extracting data. The official rules are:

  1. Set response_format={"type": "json_object"}.
  2. Include the word “json” in your system or user prompt.
  3. Give an example of the exact JSON shape you want.
  4. Set max_tokens high enough that the JSON is not cut off.
  5. Handle empty content: if the output is not valid JSON, the API may return blank content.
import json

system_prompt = """Extract the product and price from the text.
Reply in json like this example:
{"product": "keyboard", "price_usd": 49.99}"""

response = client.chat.completions.create(
    model="deepseek-flash",
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": "I bought a wireless mouse for 25 dollars."},
    ],
    response_format={"type": "json_object"},
    max_tokens=500,
    extra_body={"thinking": {"type": "disabled"}},
)

raw = response.choices[0].message.content
if raw and raw.strip():
    data = json.loads(raw)
    print(data["product"], data["price_usd"])
else:
    print("Empty JSON, adjust the prompt and retry.")

Function calling (tool calls)

Function calling lets the model ask your code to run a function, such as looking up the weather or a database row. The model never runs code itself. You run the function and send the result back.

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

def get_weather(location):
    return f"24 C and sunny in {location}"  # replace with a real API call

messages = [{"role": "user", "content": "What's the weather in Paris?"}]

first = client.chat.completions.create(
    model="deepseek-flash", messages=messages, tools=tools,
)
msg = first.choices[0].message
messages.append(msg)  # keeps reasoning_content too, which tools mode needs

if msg.tool_calls:
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_weather(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": result,
        })
    final = client.chat.completions.create(
        model="deepseek-flash", messages=messages, tools=tools,
    )
    print(final.choices[0].message.content)
else:
    print(msg.content)

This example keeps thinking mode on (the default), which is supported for tool calls. Because we append the full message object, the required reasoning_content is passed back automatically.

Strict mode (beta): to force the model’s arguments to match your schema exactly, use base_url="https://api.deepseek.com/beta", set "strict": true on every function, list every property in required, and set "additionalProperties": false on every object.

Context caching (automatic)

DeepSeek caches the start of your prompts automatically. It is enabled by default for all users, and you do not need to change any code. When a later request starts with the exact same prefix, those tokens are billed at the much cheaper cache-hit price.

How to get the most from it:

  • Put fixed content (system prompt, long documents, few-shot examples) at the start of your messages, and the changing user question at the end.
  • Keep the prefix identical, character for character. A partial match does not count.
  • Remember it is best-effort. There is no guaranteed hit rate, and unused caches are cleared after a few hours to a few days.

Check your savings in the usage data:

u = response.usage
print("cache hit tokens:", getattr(u, "prompt_cache_hit_tokens", 0))
print("cache miss tokens:", getattr(u, "prompt_cache_miss_tokens", 0))

For a side-by-side look at how caching works across providers, see my prompt caching guide.

Pricing and off-peak hours

Since August 2026, DeepSeek uses peak and off-peak pricing. Off-peak prices are half of peak prices. Peak hours are 01:00 to 04:00 and 06:00 to 10:00 UTC (09:00 to 12:00 and 14:00 to 18:00 Beijing time). Third-party price trackers report that these peak windows apply on weekdays only, with weekends billed off-peak.

Per 1M tokens (USD) deepseek-flash peak deepseek-flash off-peak deepseek-v4-pro peak deepseek-v4-pro off-peak
Input, cache hit $0.006 $0.003 $0.044 $0.022
Input, cache miss $0.30 $0.15 $1.32 $0.66
Output $1.20 $0.60 $3.96 $1.98

Note: These figures come from price trackers that checked DeepSeek’s pricing page in September 2026. DeepSeek said the October 17 upgrade did not change prices. Always confirm on the official Models and Pricing page before you budget.

For the full cost breakdown and examples, read my DeepSeek API pricing guide, and for a wider comparison see the AI API pricing comparison.

Cost tip: if you run batch jobs, schedule them outside the peak windows. The same job costs half as much.

Troubleshooting: error codes and fixes

Code Meaning Fix
400 Invalid format Fix the request body. In tools mode, make sure you pass back earlier reasoning_content.
401 Authentication fails Check the key and the environment variable. Create a new key if needed.
402 Insufficient balance Top up your balance on the platform. Calls work again right after.
422 Invalid parameters Read the hint in the error message and fix the parameter.
429 Rate limit reached Lower concurrency and retry with backoff. Limits are 2,500 concurrent requests for flash and 500 for v4-pro per account.
500 Server error Retry after a short wait. Contact support if it continues.
503 Server overloaded (busy) Retry after a short wait with exponential backoff.

The OpenAI SDK already retries 429 and 5xx errors twice by default. You can raise that and add your own handling for 402:

import time
import openai

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

def ask(prompt, attempts=3):
    for i in range(attempts):
        try:
            r = client.chat.completions.create(
                model="deepseek-flash",
                messages=[{"role": "user", "content": prompt}],
            )
            return r.choices[0].message.content
        except openai.APIStatusError as e:
            if e.status_code == 402:
                raise SystemExit("Balance is empty. Top up at platform.deepseek.com.")
            if e.status_code in (429, 500, 503):
                time.sleep(2 ** i)
                continue
            raise
    return None

Other common problems:

  • “Model not found” with deepseek-chat or deepseek-reasoner. These legacy names were scheduled for retirement on July 24, 2026. Switch to deepseek-flash and control thinking with the thinking parameter.
  • Empty JSON content. Add the word “json” and an example to the prompt, and raise max_tokens.
  • Temperature seems ignored. It has no effect in thinking mode. Disable thinking if you need it.
  • Connection closes after a long wait. If inference has not started after 10 minutes, the server closes the connection. Retry, ideally off-peak.
  • Old code uses openai.ChatCompletion.create. That is the pre-1.0 SDK style. Upgrade with pip install -U openai and use client.chat.completions.create.

Comparing DeepSeek with other options? My DeepSeek vs ChatGPT guide covers quality and price differences.

Frequently Asked Questions

What is the DeepSeek API base_url?

For the OpenAI-compatible API, use https://api.deepseek.com. For the Anthropic-compatible API, use https://api.deepseek.com/anthropic. Strict function calling uses https://api.deepseek.com/beta.

Can I still use deepseek-chat and deepseek-reasoner?

You should not. DeepSeek scheduled them for retirement on July 24, 2026, and the current docs list only deepseek-flash and deepseek-v4-pro. Use deepseek-flash with thinking on or off instead.

Is thinking mode on by default?

Yes. It is enabled by default with effort high. Pass extra_body={"thinking": {"type": "disabled"}} to turn it off.

Do I need to do anything to use context caching?

No. It is automatic for all users. Keep repeated content at the start of your prompt to get more cache hits, and check prompt_cache_hit_tokens in the usage data.

When are DeepSeek’s off-peak hours?

Peak hours are 01:00 to 04:00 and 06:00 to 10:00 UTC. All other times are off-peak and cost half as much.

How do I fix error 402 Insufficient Balance?

Your prepaid balance is empty. Log in to the DeepSeek Platform, open the top-up page and add credit. Your calls will work again immediately.

Does the DeepSeek API work with LangChain or other OpenAI tools?

Most tools that let you set a custom OpenAI base URL work with DeepSeek. Point them at https://api.deepseek.com, use your DeepSeek key, and set the model to deepseek-flash.

Sources

Free weekly email

Get new AI guides every week

One short weekly email with our newest local AI, ComfyUI and AI tool guides. No spam, no pop-ups, just the good stuff.

Free. Unsubscribe anytime in one click. We only use your email to send this newsletter. See our privacy policy.

Programmer writing Python code to call an AI API