Model Library
Browse and deploy state-of-the-art AI models through the DEVUP Gateway.
Browse and deploy state-of-the-art AI models through the DEVUP Gateway.
GPT-5.4 Pro is the deep-reasoning variant of GPT-5.4, and it asks two things of your integration before it asks anything of your prompt: it is reachable only through the responses endpoint, and its requests take minutes rather than seconds. Both are deliberate. It exists for problems that resist a quick answer — multi-step analysis, architectural judgment, questions where a confident wrong answer is more expensive than a slow right one. Three effort levels are available, starting at what other tiers treat as substantial deliberation, and OpenAI recommends submitting work in the background rather than holding a connection open for the duration.

The deep-reasoning variant of GPT-5.4. Two constraints define how you use it, and both are integration decisions rather than prompting ones.
It is served only through the responses endpoint. Not chat completions. An application written against the chat interface cannot reach this model by changing the model string — the request shape itself is different.
Requests take minutes. OpenAI states this plainly: some requests take several minutes to finish, and background mode is recommended to avoid timeouts. That is the documented normal case, not an edge case.
Everything else on this page follows from those two facts.
| Level | Status |
|---|---|
none | Unavailable |
low| Unavailable |
medium | Accepted — and the default |
high | Accepted |
xhigh | Accepted |
medium here is not a light setting that happens to be the minimum. On a model built for deep
analysis, it already represents substantially more deliberation than the same word means on a
general-purpose tier.
The default sits at the floor, which is worth noting because the equivalent model in the next generation defaults one level higher. If you are moving between them, a request that omits the effort level will behave differently — deeper and slower on the newer one.
Check the value before you send it. Strings that work on general-purpose models in this family —
none, low — are outside this model's accepted set.
A request that runs for four minutes has to survive four minutes of every layer between your process and the model: HTTP client defaults, reverse proxies, load balancer idle timeouts, a mobile network handing off between cells, a laptop going to sleep. Each of those is a place where the connection dies and the work is lost — after it has already been performed and charged.
Background submission removes the dependency. You submit, you get an identifier, and the run proceeds regardless of what happens to the connection that started it.
Two practical consequences.
Store the job identifier durably before you begin waiting. A crashed process holding the only reference to a running job has abandoned work that is still executing.
Shape the user experience around a wait rather than hiding it. A queued task with a notification when it lands is honest and reliable. A four-minute progress spinner is neither.
The useful question is not whether it is better. It is whether the problem in front of you is one where four minutes of correct reasoning beats ten seconds of plausible reasoning.
Frequently yes: architectural decisions with long consequences, research questions where a wrong premise costs a week of work, financial and legal analysis where mistakes surface late and expensively, and any question a faster model already answered in a way you did not believe.
Frequently no: anything a person is sitting and waiting for, anything running at volume, and anything where the task is transformation rather than judgment. A minutes-long response in an interactive path is a broken feature no matter how well-reasoned it is.
| Model ID | openai/gpt-5.4-pro |
| Context window | 1,050,000 tokens |
| Max output | 128,000 tokens |
| Knowledge cutoff | 31 August 2025 |
| Input | Text, images |
| Output | Text |
| Reasoning effort | medium (default) · high · xhigh |
| Interface | Responses endpoint, including batch submission |
Specifications as published by OpenAI. The model's construction is not disclosed — no parameter count, no architecture, no training method, no downloadable weights.
| Capability | Value |
|---|---|
input_types | text, image |
output_types | text |
audio_input | Not supported |
video_input | Not supported |
context_window | 1050000 |
max_output_tokens | 128000 |
reasoning | medium, high, xhigh — no none, no low, no max |
thinking_history | current_turn, all_turns |
streaming | Supported |
tool_calling | Supported |
structured_output | Supported — JSON schema |
background_execution | Supported |
requires_prompt | Yes — text prompt required, image optional |
The reasoning object carries a context setting alongside effort:
| Value | Behaviour |
|---|---|
current_turn (default) | This turn's reasoning only |
all_turns | Reasoning from every turn present in the input is available |
{ "reasoning": { "effort": "high", "context": "all_turns" } }OpenAI describes this model as existing partly to support multi-turn interactions before responding
— and that is exactly where persistence matters. At current_turn, a session rebuilds its analysis
from nothing on every exchange, arriving somewhere slightly different each time. Across a long
conversation those differences accumulate into an argument that contradicts itself.
The setting works only on reasoning that is actually present in the request. It does not reconstruct anything you discarded. Replay the response output in full, or reference a stored conversation — a rebuilt message containing only text has no reasoning to carry.
Base URL: https://api.devupai.com/v1 · Model ID: openai/gpt-5.4-pro
curl https://api.devupai.com/v1/responses \
-H "Authorization: Bearer $DEVUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4-pro",
"input": "Our checkout service loses roughly one order per thousand under load, with no errors logged. We have ruled out the payment provider and the database. Given that the service runs six replicas behind a load balancer with sticky sessions and writes to a shared Redis cart store, list the mechanisms that could produce silent loss, ordered by how consistent each is with the evidence.",
"reasoning": { "effort": "high" },
"max_output_tokens": 32768
}'Giving the model what you have already ruled out is worth the extra sentence. Without it, a deep reasoning model will spend a portion of its budget re-deriving conclusions you reached last week.
import os
import time
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEVUP_API_KEY"],
base_url="https://api.devupai.com/v1",
)
job = client.responses.create(
model="openai/gpt-5.4-pro",
input=analysis_prompt,
reasoning={"effort": "xhigh"},
max_output_tokens=65536,
background=True,
)
# Persist this before anything else. It is the only handle on work already running.
print(f"Submitted {job.id} — status {job.status}")
while True:
job = client.responses.retrieve(job.id)
if job.status in {"completed", "failed", "cancelled"}:
break
time.sleep(15)
print(job.output_text if job.status == "completed" else f"Ended as: {job.status}")The polling loop above is fine for a script. In a service, prefer storing the identifier in your own database and checking it from a scheduled worker — that way a deployment restart does not lose track of anything.
The pattern this model's multi-turn support exists for.
conversation = [
{"role": "user", "content": "Assess whether this data model will support the reporting requirements in the attached spec."}
]
first = client.responses.create(
model="openai/gpt-5.4-pro",
input=conversation,
reasoning={"effort": "high", "context": "all_turns"},
max_output_tokens=32768,
)
conversation += first.output # the full output list, reasoning included
conversation.append(
{"role": "user", "content": "Now assume the reporting volume is ten times what the spec says. Which of your conclusions change?"}
)
second = client.responses.create(
model="openai/gpt-5.4-pro",
input=conversation,
reasoning={"effort": "high", "context": "all_turns"},
max_output_tokens=32768,
)
print(second.output_text)Extending conversation with first.output rather than appending a hand-built message is the entire
mechanism. Reasoning items live in that list; drop them and all_turns has nothing to reference.
The second question is the shape worth reusing: change one premise and ask which conclusions move. That distinguishes conclusions that were load-bearing from conclusions that were incidental, and it is a question deep reasoning answers well.
with open("vendor_agreement.txt", encoding="utf-8") as handle:
document = handle.read()
response = client.responses.create(
model="openai/gpt-5.4-pro",
input=[
{
"role": "system",
"content": (
"Base every statement on the document provided and quote the clause it rests on. "
"Where the document does not address something you would expect it to, name the gap "
"rather than filling it."
),
},
{"role": "user", "content": f"{document}\n\nWhat happens to our data if the vendor is acquired?"},
],
reasoning={"effort": "xhigh"},
max_output_tokens=32768,
)Asking the model to name gaps rather than fill them is the most useful instruction available on a document question. The absence of a clause is often the finding.
requests = [
{
"custom_id": f"review-{index}",
"method": "POST",
"url": "/v1/responses",
"body": {
"model": "openai/gpt-5.4-pro",
"input": f"Identify every provision that conflicts with our standard position. Quote both.\n\n{text}",
"reasoning": {"effort": "high"},
"max_output_tokens": 32768,
},
}
for index, text in enumerate(agreements)
]Work that already takes minutes loses nothing by being queued. If nobody is waiting on an individual result, batch submission is strictly better than sequential calls.
import base64
with open("network_topology.png", "rb") as handle:
encoded = base64.b64encode(handle.read()).decode("utf-8")
response = client.responses.create(
model="openai/gpt-5.4-pro",
input=[
{
"role": "user",
"content": [
{"type": "input_image", "image_url": f"data:image/png;base64,{encoded}"},
{
"type": "input_text",
"text": "Identify every component in this topology whose failure would partition the network. Refer to components by their labels in the diagram, and describe only what is drawn.",
},
],
}
],
reasoning={"effort": "high"},
max_output_tokens=16384,
)npm install devupaiimport DevupAI from "devupai";
const client = new DevupAI({
apiKey: process.env.DEVUP_API_KEY,
});
const job = await client.responses.create({
model: "openai/gpt-5.4-pro",
input:
"Review this incident timeline and identify the earliest point at which the outage " +
"was preventable. Distinguish what was knowable at the time from what is only obvious " +
"in hindsight.\n\n" + timeline,
reasoning: { effort: "high" },
max_output_tokens: 32768,
background: true,
});
console.log(`Submitted ${job.id} — status ${job.status}`);Front-load your constraints. Team size, timeline, existing stack, budget, what is politically impossible. A deep analysis that ignores your real limits is a well-argued answer to someone else's question.
Say what you have already eliminated. Otherwise you pay for the model to reach the same dead ends you reached yourself.
Ask for the reasoning to appear in the answer. Not the internal trace — the argument. At this depth the argument is usually the more valuable half, and it is the half that lets you disagree intelligently.
Ask it to make the opposing case. "State the strongest argument for the option you rejected." Deep reasoning models do this unusually well and almost never do it without being asked.
Send one large question, not five small ones. Splitting a problem across requests throws away the cross-cutting reasoning that justifies using this tier at all.
Never iterate here. Draft and refine the prompt against a fast model, then run the real question once. A four-minute feedback loop is not a feedback loop.
| This model | GPT-5.4 | |
|---|---|---|
| Interface | Responses endpoint | Chat completions and responses |
| Minimum effort | medium | none |
| Default effort | medium | none |
| Typical latency | Minutes | Fast |
| Suits interactive use | No | Yes |
| Suits high volume | No | Yes |
The defaults tell the story. The general model answers immediately unless asked to think; this one thinks substantially unless asked to think harder. They are built for opposite ends of the same workload spectrum, and the interface difference makes moving between them a real migration rather than a configuration change.
medium is the floor; none and low are unavailable.