DeepSeek API Python Example: Chat, Streaming, Tools
A deepseek api python setup needs only the official openai package, a base URL, a key and a model ID. The examples below cover a plain request, streaming, JSON mode, tool calling and retries, all against an OpenAI-compatible endpoint.

Key points
- Install openai, then set base_url to https://api.deepseekapi.top/v1 and model to uncensored.
- Streaming, JSON mode and tool calling work through the normal SDK parameters.
- Handle HTTP 429 by reading Retry-After and trying again.
- Price is $0.15 per 1M input tokens and $0.50 per 1M output tokens.
Install and send a first request
This service is not the official DeepSeek API. It is a hosted, OpenAI-compatible endpoint that runs one open-weight model, ID uncensored, operated by an independent provider. Because it follows the OpenAI format, the standard Python SDK works without changes beyond the URL and key.
pip install openai
Get a key from the API key page. After sign-up you can claim $0.50 of trial credit with one click, with no card, valid for 7 days. Store the key in an environment variable rather than in the source file:
export API_KEY="YOUR_KEY"
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.deepseekapi.top/v1",
api_key=os.environ["API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Explain list comprehensions in two sentences."},
],
temperature=0.7,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)
The two usage numbers are what you are billed on. Multiply them by the prices to see the cost of any call: input tokens times $0.15 and output tokens times $0.50, each divided by 1,000,000.
Using the async client
For web servers and anything that handles many users, use the async client. It has the same parameters as the sync one and is a drop-in change in most code.
import asyncio
import os
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.deepseekapi.top/v1",
api_key=os.environ["API_KEY"],
)
sem = asyncio.Semaphore(8)
async def ask(prompt):
async with sem:
r = await client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
async def main():
prompts = ["Define idempotent.", "Define monotonic.", "Define pure function."]
answers = await asyncio.gather(*(ask(p) for p in prompts))
for a in answers:
print(a)
asyncio.run(main())
The semaphore is set to 8 because that is the concurrency of a full key. On a trial key use 2. Without a cap, a burst of tasks will produce concurrency errors even though each request is valid.
Streaming responses
Set stream=True and the endpoint sends server-sent events. Each chunk carries a small piece of text in delta.content. Asking for usage means the last chunk reports token counts.
# client = OpenAI(base_url="https://api.deepseekapi.top/v1", ...) as above
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a haiku about debugging."}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n", chunk.usage.prompt_tokens, chunk.usage.completion_tokens)
Two details trip people up. The final usage chunk can have an empty choices list, which is why the loop checks chunk.choices first. And a streamed request keeps its concurrency slot until the stream ends, so break out of loops cleanly and close streams you abandon. A full key allows 8 concurrent requests; a trial key allows 2.
JSON mode
For output a program will parse, use response_format with json_object. Also say the word JSON in the prompt and describe the shape you want. The model needs that instruction; the flag alone does not supply a schema.
# client = OpenAI(base_url="https://api.deepseekapi.top/v1", ...) as above
import json
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Reply with JSON only: {\"title\": str, \"tags\": [str]}"},
{"role": "user", "content": "Summarize: a guide to caching HTTP responses."},
],
response_format={"type": "json_object"},
temperature=0,
)
data = json.loads(resp.choices[0].message.content)
print(data["title"], data["tags"])
Keep a try/except around json.loads. If a reply is cut off by a token limit it will not parse, so avoid a tiny max_tokens with JSON. Setting temperature to 0 and a seed makes reruns more repeatable, since both are passed through.
Tool calling
The API accepts tools and tool_choice, and returns tool_calls on the assistant message. You run the function yourself and send the result back as a message with role: "tool".
# client = OpenAI(base_url="https://api.deepseekapi.top/v1", ...) as above
import json
tools = [{
"type": "function",
"function": {
"name": "add",
"description": "Add two numbers.",
"parameters": {
"type": "object",
"properties": {"a": {"type": "number"}, "b": {"type": "number"}},
"required": ["a", "b"],
},
},
}]
messages = [{"role": "user", "content": "What is 17.5 plus 24.25?"}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = args["a"] + args["b"]
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result),
})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools
)
print(final.choices[0].message.content)
else:
print(msg.content)
Always parse arguments defensively. It is a JSON string produced by the model, and a malformed string is possible. Validate it before you act on it, especially if the function touches files or a database.
Errors, limits and retries
A request over a limit returns HTTP 429. The error code tells you which limit: rate_limited for requests per minute (300 on a full key, 60 on a trial key) and concurrency for too many at once. On temporary overload you also get 429 with Retry-After: 5. The SDK raises openai.RateLimitError.
# client = OpenAI(base_url="https://api.deepseekapi.top/v1", ...) as above
import time
import openai
def ask(prompt, tries=4):
for attempt in range(tries):
try:
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
except openai.RateLimitError as e:
wait = int(e.response.headers.get("Retry-After", 5))
time.sleep(wait * (attempt + 1))
raise RuntimeError("still rate limited")
print(ask("Name three Python web frameworks."))
If you send many requests, cap your own parallelism with a semaphore or a thread pool of 8 workers, or 2 on a trial key. That avoids concurrency errors instead of retrying through them.
Reading headers
Each response carries X-Request-Id, X-Balance-USD, X-RateLimit-Limit-Requests and X-RateLimit-Limit-Concurrency. The SDK exposes them through the raw response:
# client = OpenAI(base_url="https://api.deepseekapi.top/v1", ...) as above
raw = client.chat.completions.with_raw_response.create(
model="uncensored",
messages=[{"role": "user", "content": "ping"}],
)
print(raw.headers.get("X-Balance-USD"), raw.headers.get("X-Request-Id"))
completion = raw.parse()
Logging the request ID with every failure makes support questions much easier to answer. Failed or refused requests are not charged.
Keeping a conversation going
The API is stateless. It does not remember earlier requests, so a multi-turn chat means you send the whole history every time. Keep a list and append to it:
# client = OpenAI(base_url="https://api.deepseekapi.top/v1", ...) as above
history = [{"role": "system", "content": "You are a helpful tutor."}]
def chat(user_text):
history.append({"role": "user", "content": user_text})
r = client.chat.completions.create(model="uncensored", messages=history)
reply = r.choices[0].message.content
history.append({"role": "assistant", "content": reply})
return reply
print(chat("What is a closure?"))
print(chat("Show me one in Python."))
Each turn resends everything, so input tokens grow turn by turn. Trim old messages once the list gets long, or ask the model to summarize the early part and replace it. With a 100,000-token window you have plenty of room, but the bill grows with the history, so a trimmed list is cheaper.
Choosing sampling values
Use temperature 0 to 0.3 for code and extraction, around 0.7 for general chat, and higher for fiction. top_p, stop, seed and the frequency and presence penalties are passed through, so they behave as the OpenAI documentation describes. Change one at a time and compare.
Planning cost and context
The context window is 100,000 tokens, prompt plus completion, and the body limit is 8 MB. Completion can use whatever is left of the window, and max_tokens is optional. Setting it is still good practice for batch jobs.
A worked example: a batch of 1,000 summaries, each with 1,500 input tokens and 200 output tokens.
- Input: 1,000 × 1,500 = 1,500,000 tokens, which is 1.5 × $0.15 = $0.225
- Output: 1,000 × 200 = 200,000 tokens, which is 0.2 × $0.50 = $0.10
- Total: $0.325
Pricing is pay as you go from prepaid credit, with no subscription, and credit never expires. Top-ups run from $20 to $500 in whole amounts, with +5% from $50, +10% from $100 and +15% from $200, payable by card or crypto. See the pricing page, and the quickstart docs for curl and Node examples. If you only need to locate your key, the API key overview covers it.
Prompts are not used for training. The service is text only: no images, audio, embeddings or fine-tuning, so do not plan around those endpoints.
More guides
Questions and answers
Do I need a special DeepSeek library?
No. Install the openai package, set base_url to https://api.deepseekapi.top/v1 and use the model ID uncensored. This is not the official DeepSeek API.
Does the Python SDK support streaming here?
Yes. Pass stream=True, and add stream_options with include_usage to get token counts in the last chunk.
How do I handle a 429 error?
Catch openai.RateLimitError, read the Retry-After header and wait before retrying. The error code tells you whether it was rate_limited or concurrency.
Can I test without paying?
Yes. After sign-up, one click on the API key page adds $0.50 of trial credit, valid for 7 days, with no card required.
Your key is one form away
Create an account, copy the key, change the base URL. That is the whole setup.