Streaming, tools and structured output
The three things the chat endpoint does beyond returning a block of text — with the request and response shapes for each
The chat completion reference lists stream, tools and response_format as supported fields. This page shows what each one actually looks like on the wire.
Everything below is OpenAI-compatible, so an existing SDK works unchanged once you point it at the base URL.
Streaming
Set stream: true and the response arrives as server-sent events instead of one JSON body. Each data: line carries a JSON delta; the stream ends with a literal data: [DONE].
import os, requests, json
response = requests.post(
"https://api.deutschlandgpt.de/v2/chat/completions",
headers={"Authorization": f"Bearer {os.environ['DGPT_API_KEY']}"},
json={
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Count to five."}],
"stream": True,
"stream_options": {"include_usage": True},
},
stream=True,
)
for line in response.iter_lines():
if not line or not line.startswith(b"data: "):
continue
payload = line[len(b"data: "):]
if payload == b"[DONE]":
break
delta = json.loads(payload)["choices"][0]["delta"]
print(delta.get("content", ""), end="", flush=True)
stream_options.include_usage adds one final chunk with the token counts before [DONE]. Without it a streamed response never reports its own usage, which is the usual reason a streaming integration cannot account for its spend.
Streaming and json_schema are mutually exclusive. A request asking for both is rejected — pick
structured output or incremental tokens.
Tools (function calling)
Describe the functions the model may call in tools. Each is a JSON Schema; the model never executes anything, it only asks you to.
{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "What is the weather in Berlin?" }],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city — used by the model to decide when to call it",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
]
}
A function name is at most 64 characters, from a-z, A-Z, 0-9, underscores and dashes.
When the model wants a call, the response carries tool_calls instead of content. You run the function yourself and send the result back as a tool message, then call the endpoint again with the extended history:
{
"model": "gpt-4o",
"messages": [
{ "role": "user", "content": "What is the weather in Berlin?" },
{
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Berlin\"}" }
}
]
},
{ "role": "tool", "tool_call_id": "call_abc123", "content": "18 °C, light rain" }
]
}
arguments is a JSON string, not an object — parse it before use. The tool_call_id on your reply must match the id of the call it answers.
By default the model may request several calls in one turn. Set parallel_tool_calls: false to force one at a time.
tool_choice — forcing a specific function — is accepted but ignored on this endpoint. If you
need it, use /v2/responses.
Structured output
To get JSON matching a schema rather than prose, pass a response_format of type json_schema:
{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "Extract the invoice total and currency." }],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "invoice",
"strict": true,
"schema": {
"type": "object",
"properties": {
"total": { "type": "number" },
"currency": { "type": "string" }
},
"required": ["total", "currency"],
"additionalProperties": false
}
}
}
}
The answer still arrives in choices[0].message.content, as a JSON string you parse yourself.
strict: true is what makes the model adhere to the schema rather than treat it as a suggestion. It requires additionalProperties: false and every property listed in required.
Reasoning models
reasoning_effort sets how much thinking a reasoning model does before answering: none (the default, extended thinking off), minimal, low, medium, high or xhigh. Lower is faster and cheaper. It has no effect on models that do not reason.
What bounds your throughput
There is no request-per-minute rate limit and no 429. What actually stops a request is money and permission:
| Limit | What happens |
|---|---|
| Workspace credits | 402 once the balance or the monthly spending limit is reached |
| Per-key spending limit | 402 for that key, while other keys keep working |
| Model restrictions on a key | 403 for a model the key may not use |