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
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
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:
| Model | Input / output | Context | Image input | Intelligence Index |
|---|---|---|---|---|
deepseek-v4.1-flash | 0.013 / 0.038 | 1M | no | 39 |
deepseek-v4-pro | 0.075 / 0.225 | 1M | yes | 36 |
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.
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.
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:
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_tokensso 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.
Related articles
- Your Own Neural Network Proxy on Node.js: Keep the API Key Off the FrontendA small Express server between your frontend and the model API: the key stays on the server, and models, request size and rate are capped.
- Gemini API in Python: requests via the OpenAI SDK and OpenAI formatA working Gemini API example in Python with no extra library: change base_url in the OpenAI SDK, add streaming and JSON, and compare with google-genai.
- Anthropic SDK for Python: base_url, key and a first request via a gatewayThe official anthropic package for Python just needs a different address. We cover base_url, the key, streaming, token counting, caching and errors.
- Claude Code security: a checklist before running the agent on a projectThe agent reads files and runs commands, so security settings belong before the first launch. A checklist: permissions, secrets, sandbox, a separate key with a limit.