DeepSeek API in Python: Requests, Streaming, JSON and Error Handling

Ready code for DeepSeek V4.1 Flash and V4 Pro on the OpenAI SDK: a request, streaming, JSON parsing and careful error handling.

4 min · · development · API keys

If you need the DeepSeek API in Python, no separate library is required: DeepSeek models answer in the OpenAI-compatible format, so the regular openai package works. Below is working code for deepseek-v4.1-flash and deepseek-v4-pro: a basic request, streaming, a JSON answer, and error handling with retries. Access from Russia goes through the gateway, with payment by SBP, MasterCard or crypto.

Setup

bash
pip install openai
export ZUKKO_API_KEY="fetch_your_key"

Create the key in the Zukko dashboard. Keep it in an environment variable, not in code. The general SDK setup is described in the Python section of the docs.

First request

bash
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.zukko.pro/v1",
    api_key=os.environ["ZUKKO_API_KEY"],
)

resp = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[
        {"role": "system", "content": "Answer briefly."},
        {"role": "user", "content": "How is a list different from a tuple in Python?"},
    ],
    max_tokens=400,
)

print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)

The address must end in /v1: the OpenAI SDK appends only chat/completions. The usage field shows how many tokens were spent and is handy for computing cost.

Which DeepSeek model to choose

Prices as of October 18, 2026, $ per 1M tokens:

ModelInput / outputContextImage inputIntelligence Index
deepseek-v4.1-flash0.013 / 0.0381Mno39
deepseek-v4-pro0.075 / 0.2251Myes36

Example calculation for a request with 2000 input and 500 output tokens:

  • V4.1 Flash: 2000 × 0.013 / 1M + 500 × 0.038 / 1M = 0.000026 + 0.000019 = $0.000045.
  • V4 Pro: 2000 × 0.075 / 1M + 500 × 0.225 / 1M = 0.00015 + 0.0001125 = about $0.00026.

If the request needs images, only V4 Pro of the pair fits. For text tasks start with Flash and check the result on your own data; the models are close on the index in the table, but your material decides, not the index. The current price list: GET https://api.zukko.pro/v1/models.

Streaming

Streaming is for showing the answer to the user as it is generated. An important detail: at the end of the stream the server sends a chunk with usage statistics and an empty choices list, so accessing choices[0] without a check will fail.

bash
stream = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Outline an article about caching"}],
    stream=True,
    stream_options={"include_usage": True},
)

for chunk in stream:
    if chunk.choices:
        delta = chunk.choices[0].delta.content
        if delta:
            print(delta, end="", flush=True)
    if chunk.usage:
        print("\n", chunk.usage.prompt_tokens, chunk.usage.completion_tokens)

If the stream breaks mid-word, check max_tokens and the finish_reason in the last content chunk: length means the answer hit the limit.

A JSON answer

The most portable approach: ask for JSON in the prompt, parse the answer, and retry on failure. It does not depend on whether a given model supports a special JSON mode.

bash
import json

SYSTEM = (
    "Return only JSON with no commentary and no markdown. "
    'Schema: {"sentiment": "positive|neutral|negative", "topics": [string]}'
)

def classify(text: str, attempts: int = 3) -> dict:
    for _ in range(attempts):
        r = client.chat.completions.create(
            model="deepseek-v4.1-flash",
            messages=[
                {"role": "system", "content": SYSTEM},
                {"role": "user", "content": text},

You can add response_format={"type": "json_object"} to the call, but support depends on the model, so test it on yours and still keep the json.loads check. More techniques are in the article on structured output.

Errors and retries

The OpenAI client itself retries network failures and some server errors (twice by default); the number of retries is set by max_retries. Other errors must be handled by meaning:

bash
import os
import time
import openai

client = openai.OpenAI(
    base_url="https://api.zukko.pro/v1",
    api_key=os.environ["ZUKKO_API_KEY"],
    max_retries=2,
    timeout=60,
)

def ask(prompt: str, model: str = "deepseek-v4.1-flash") -> str:
    for attempt in range(4):
        try:

What the codes mean: 401 is a wrong key, 402 not enough balance, 403 model_not_allowed_for_key means the model is not allowed for this key, 404 model_not_found is a wrong model name, 413 a request that is too large, 429 an exhausted key limit or a limit at the model provider, 503 a provider outage or a disabled model. Retrying 401, 402, 403 and 404 is pointless: fix the cause. More on retry strategy and a fallback model in the article on fallback and retry.

Production notes

  • Set max_tokens so that one prompt mistake does not produce a long, expensive answer.
  • Create a separate key per project with a daily dollar limit and a list of allowed models.
  • For DeepSeek the cache-read price in the table equals the input price, so the calculation gets no separate cache discount.
  • Do not hardcode the model: put its name in settings so you can swap it when the catalog changes.

FAQ

Do I need a separate DeepSeek key?

No. A fetch_... key from the gateway is enough: it works with all catalog models, including Claude, GPT and Qwen, so you can switch by changing the model string.

How do I enable reasoning?

Parameter names for reasoning modes differ between models, and we do not list them here. Check the chat completions reference and watch how the number of output tokens changes through usage.

Can I send images?

Of the two models in this article, deepseek-v4-pro accepts image input. deepseek-v4.1-flash is marked "no" in the table.

What if I get a 404?

Most likely a typo in the model id. Compare the name with the list from GET /v1/models: for example deepseek-v4.1-flash, not deepseek-v4-1-flash.

To run the code, create a key in the dashboard, top up from $1 and put the key into the environment variable.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

DeepSeek API Python: request, streaming, JSON, errors · Zukko