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 Image 2.5 Sunburst is OpenAI's most capable image model, built for work that will be inspected closely and revised repeatedly. It ranks first on human-preference text-to-image evaluation and leads its own family on editing, which makes it the tier for production campaign creative, polished product imagery, and any visual that has to survive several rounds of art direction without drifting. It accepts up to sixteen reference images, supports masked inpainting, generates on transparent backgrounds, and renders at arbitrary dimensions up to 3840×2160. Five quality levels reach two steps beyond where the previous generation stopped, which is what makes fine print inside an image viable.

OpenAI's most capable image model. Built for premium workflows that need tight control across detailed and iterative edits.
In the September 2026 human-preference arena snapshot:
| Text to image | Image editing | |
|---|---|---|
| This model | 1421 (±13) — first | 1520 (±9) |
| The fast tier in the same family | 1399 (±13) | 1491 (±9) |
| Previous generation (medium) | 1381 (±4) | 1461 (±3) |
First on text-to-image, and ahead of its own family on editing. That combination is unusual — a precision tier normally trades generation quality for editing control.
Note the error bars on text-to-image. The twenty-two points separating this model from the fast tier sit inside overlapping confidence intervals; the thirty points on editing do not. The editing lead is the one you can count on.
The distinction from the fast tier is not general quality. It is control across a sequence of changes.
Art direction does not arrive as one instruction. A shot gets a colour change, then a prop, then a different wall, then a note about the light — and each round is judged before the next is asked for.
This model holds a subject and a composition steady across that sequence. Reference subjects keep their distinguishing features and lighting when moved into new settings. Localised changes stay localised. Complex layouts survive editing rather than resolving into something simpler.
Where that earns its cost: production campaign creative, polished product imagery, and anything that will be looked at closely rather than scrolled past.
Where it does not: one-shot generation at volume, concept exploration, and drafts. The fast tier in this family is designed for those, and the text-to-image gap between them is inside the noise.
Up to sixteen reference images on an edit — enough to hold a subject, a style, a palette, and a set of props simultaneously.
Mask support for inpainting, with one important constraint: a mask requires exactly one reference image. Masked editing and multi-reference editing are separate modes, not a combination.
White areas of the mask are replaced; everything else is preserved. That is how you change a sign above a door rather than regenerating a storefront that happens to have a different sign.
low · medium · high · xhigh · max
Two of these sit above where the previous generation stopped, and they exist for a specific problem: detail fine enough to be inspected closely. Small text on a label, fine print on packaging, dense texture in something destined for print.
Quality affects both cost and generation time.
Start at low while deciding what the image should be. Composition, framing, lighting direction,
and layout are all visible there. Step up once the image is right — not while you are choosing
between candidates.
Test max against high on one image before committing a batch to it. Generate the same prompt
at both, put them side by side, and decide whether you can see it. That settles a pipeline decision
in five minutes.
| Range | 480 to 3,840 pixels per edge |
| Step | 16 — both dimensions must be divisible by 16 |
| Aspect ratio | Between 1:3 and 3:1 |
| Total pixels | Up to 8,294,400 |
| Maximum resolution | 3840×2160 |
| Experimental above | 2560×1440 |
Four separate constraints, and a request failing any one of them is rejected. The divisibility rule
catches people most often: 1920x1080 fails because 1080 is not a multiple of 16. 1088 is.
Resolutions above 2560×1440 are marked experimental by OpenAI. They are supported in the sense that the request succeeds. Test your own case before depending on the output.
Standard presets are also available, including 1024x1024, 1536x1024, 1024x1536, and
3840x2160.
background takes auto, opaque, or transparent.
Transparent output requires a format carrying an alpha channel — PNG or WebP. JPEG has none, and requesting transparency alongside it produces an opaque image rather than an error.
| Model ID | openai/gpt-image-2.5-sunburst |
| Announced | September 2026 |
| Input | Text, images |
| Output | Images |
| Size range | 480–3,840 pixels per edge, in steps of 16 |
| Aspect ratio | 1:3 to 3:1 |
| Maximum resolution | 3840×2160 |
| Quality | low, medium, high, xhigh, max |
| Background | auto, opaque, transparent |
| Reference images | Up to 16 |
| Mask | Supported — requires exactly one reference image |
| Endpoint | /v1/images/generations · /v1/images/edits |
Nothing is published about the model's construction — no parameter count, no architecture, no training method, no weights.
| Capability | Value |
|---|---|
input_types | text, image |
output_types | image |
min_edge_pixels | 480 |
max_edge_pixels | 3840 |
size_divisor | 16 |
max_total_pixels | 8294400 |
aspect_ratio_range | 1:3 to 3:1 |
quality_levels | low, medium, high, xhigh, max |
background | auto, opaque, transparent |
max_reference_images | 16 |
mask_editing | Supported — one reference image only |
endpoint | /v1/images/generations, /v1/images/edits |
streaming | Not applicable |
deterministic | No |
requires_prompt | Yes |
Endpoints: POST https://api.devupai.com/v1/images/generations and
POST https://api.devupai.com/v1/images/edits
The response returns a signed URL in data[0].url alongside a _devup object carrying the request
cost and your remaining balance.
The URL is valid for five minutes. Fetch the image within that window.
Set a generous client timeout. This is the slower tier of its family by design, and larger dimensions take longer.
curl https://api.devupai.com/v1/images/generations \
-H "Authorization: Bearer $DEVUP_API_KEY" \
-H "Content-Type: application/json" \
--max-time 300 \
-d '{
"model": "openai/gpt-image-2.5-sunburst",
"prompt": "A wide editorial photograph of a ceramics workshop at dusk. Rows of unfired pots on wooden shelves, warm light from a single window on the left, dust visible in the beam. Nothing else in frame.",
"size": "1920x1088",
"quality": "high"
}'1920x1088 rather than 1920x1080 — both edges divisible by 16.
Four size constraints and five quality levels are enough to be worth checking locally.
import os
import requests
from pathlib import Path
DEVUP_API_KEY = os.environ["DEVUP_API_KEY"]
GENERATE = "https://api.devupai.com/v1/images/generations"
QUALITY_LEVELS = {"low", "medium", "high", "xhigh", "max"}
MAX_TOTAL_PIXELS = 8_294_400
def check_size(size: str) -> None:
"""Reject dimensions that violate any of the model's four size constraints."""
width, height = (int(value) for value in size.split("x"))
for edge in (width, height):
if not 480 <= edge <= 3840:
raise ValueError(f"each edge must be between 480 and 3840, got {size}")
if edge % 16:
raise ValueError(f"each edge must be divisible by 16, got {size}")
if width * height > MAX_TOTAL_PIXELS:
raise ValueError(f"total pixels exceed {MAX_TOTAL_PIXELS:,}, got {size}")
ratio = max(width / height, height / width)
if ratio > 3:
raise ValueError(f"aspect ratio must fall between 1:3 and 3:1, got {size}")
def generate(prompt: str, *, size: str = "1024x1024", quality: str = "low", **extra) -> bytes:
"""Generate one image and return its bytes."""
if quality not in QUALITY_LEVELS:
raise ValueError(f"quality must be one of {sorted(QUALITY_LEVELS)}, got {quality!r}")
check_size(size)
response = requests.post(
GENERATE,
headers={
"Authorization": f"Bearer {DEVUP_API_KEY}",
"Content-Type": "application/json",
},
json={"model": "openai/gpt-image-2.5-sunburst", "prompt": prompt, "size": size, "quality": quality, **extra},
timeout=300,
)
response.raise_for_status()
result = response.json()
spend = result.get("_devup", {})
print(f"{size} @ {quality} — {spend.get('cost_dzd')} DZD, balance {spend.get('balance_dzd')}")
# The URL is valid for five minutes.
image = requests.get(result["data"][0]["url"], timeout=120)
image.raise_for_status()
return image.contentEach check names the constraint it caught. A rejected request from your own code tells you which of four rules you broke; an error response does not always.
Changing one region and leaving everything else untouched.
EDIT = "https://api.devupai.com/v1/images/edits"
with open("storefront.png", "rb") as source, open("sign_mask.png", "rb") as mask:
response = requests.post(
EDIT,
headers={"Authorization": f"Bearer {DEVUP_API_KEY}"},
files={"image": source, "mask": mask},
data={
"model": "openai/gpt-image-2.5-sunburst",
"prompt": 'A hand-painted wooden board reading "ATLAS" in cream capitals.',
"size": "1920x1088",
"quality": "max",
},
timeout=300,
)
response.raise_for_status()
result = response.json()
# The URL is valid for five minutes.
Path("storefront_edited.png").write_bytes(
requests.get(result["data"][0]["url"], timeout=120).content
)Exactly one reference image when a mask is present. Masked editing and multi-reference editing are separate modes.
White regions of the mask are replaced; everything outside survives. max quality here because the
edit contains text small enough to be read.
Where sixteen references earn their place.
references = [
"subject_front.png",
"subject_profile.png",
"palette_swatch.png",
"prop_bag.png",
"set_reference.png",
]
files = [("images", open(path, "rb")) for path in references]
try:
response = requests.post(
EDIT,
headers={"Authorization": f"Bearer {DEVUP_API_KEY}"},
files=files,
data={
"model": "openai/gpt-image-2.5-sunburst",
"prompt": (
"Place the subject from the first two references in the setting shown in the last "
"reference, carrying the bag from the fourth. Match the colour palette of the third. "
"Keep the subject's features and the natural lighting consistent with the references."
),
"size": "1536x1024",
"quality": "high",
},
timeout=300,
)
response.raise_for_status()
result = response.json()
finally:
for _, handle in files:
handle.close()
Path("composed.png").write_bytes(
requests.get(result["data"][0]["url"], timeout=120).content
)Refer to references by position in the prompt. "The first two references", "the third" — the linkage between a reference and its role is expressed in language, not in the request structure.
No mask here. A mask would restrict this to a single reference.
The workflow this tier is built for.
def edit(image_path: Path, instruction: str, *, size: str = "1024x1024", quality: str = "high") -> bytes:
"""Apply one instruction to an existing image and return the result."""
check_size(size)
with image_path.open("rb") as source:
response = requests.post(
EDIT,
headers={"Authorization": f"Bearer {DEVUP_API_KEY}"},
files={"image": source},
data={
"model": "openai/gpt-image-2.5-sunburst",
"prompt": instruction,
"size": size,
"quality": quality,
},
timeout=300,
)
response.raise_for_status()
result = response.json()
# The URL is valid for five minutes.
return requests.get(result["data"][0]["url"], timeout=120).content
current = Path("campaign_00.png")
direction = [
"Change only the jacket colour to forest green. Keep everything else identical.",
"Replace the wall behind the subject with exposed brick. Keep the subject and lighting unchanged.",
"Warm the light slightly and soften the shadow on the wall. Change nothing else.",
]
for step, instruction in enumerate(direction, start=1):
output = Path(f"campaign_{step:02d}.png")
output.write_bytes(edit(current, instruction))
current = outputEvery instruction names what stays as well as what changes. "Keep everything else identical" is not filler — the instruction to preserve is as actionable as the instruction to alter, and on a chain of edits it is what keeps round three recognisable as the same image as round one.
Restate rather than chain when a change is structural. Chaining suits incremental art direction. A fundamentally different composition is a new generation.
Path("product_cutout.png").write_bytes(
generate(
"A single matte black water bottle standing upright, three-quarter view, even soft "
"lighting, no shadow on the ground, no reflection.",
size="1024x1024",
quality="max",
background="transparent",
output_format="png",
)
)Excluding the ground shadow is deliberate. A shadow baked into a transparent PNG travels with the object onto every background you composite it against.
npm install devupaiimport DevupAI from "devupai";
import { writeFile } from "node:fs/promises";
const client = new DevupAI({
apiKey: process.env.DEVUP_API_KEY,
});
const result = await client.images.generate({
model: "openai/gpt-image-2.5-sunburst",
prompt:
"An overhead shot of a wooden desk with a leather notebook open to blank pages, " +
"a fountain pen beside it, morning light from the upper right. Nothing else in frame.",
size: "1920x1088",
quality: "high",
});
// The URL is valid for five minutes.
const image = await fetch(result.data[0].url);
await writeFile("desk.png", Buffer.from(await image.arrayBuffer()));Describe a photograph, not a concept. A scene the model can construct beats a mood board it has to interpret.
Name the camera position. Overhead, eye level, three-quarter, close crop. Omit it and framing varies between runs for no reason you control.
Specify the light. Direction, softness, time of day. This model renders lighting and texture well enough that the choice shows in the output.
Use exclusions. "Nothing else in frame", "no text", "no people". Negative constraints work.
Quote text exactly and say it should appear verbatim. Place it explicitly. Proofread every character — text rendering is strong at the higher quality levels, not guaranteed.
On edits, name what to preserve. This is the single most effective instruction available on this model.
Refer to references by position when supplying several.
Change one clause at a time when iterating. Rewriting the whole brief tells you nothing about which change mattered.
Validate all four size rules before sending. Range, divisibility, total pixels, aspect ratio.
Fetch the image within five minutes of receiving the response.
Log _devup on every request. Cost moves with both quality and dimensions.
Chain edits from the output, not the original. Stability across a sequence is what this tier provides.
A mask means one reference image. Combining masked and multi-reference editing is not supported.
Transparent output needs PNG or WebP. JPEG carries no alpha channel.
Output is not reproducible. The same prompt run twice produces different images. Keep the file.
Use the fast tier for exploration. This model is for the version you intend to keep.
Rate-limit public paths in your own layer. Image generation is expensive per call and trivial to trigger repeatedly.
Moderate both directions. Filter prompts arriving from users, and review output before it reaches an audience.
This model for production creative, polished product imagery, work that will be inspected closely, and anything going through several rounds of art direction.
The fast tier for concept exploration, drafts, high-volume generation, and interactive experiences where latency is the product. On text-to-image the two sit inside overlapping confidence intervals; on editing this model's lead is clear.
A workable split: explore on the fast tier, direct and finish here.
xhigh and max are narrow improvements. They address fine detail; on images without it they
add cost and time for nothing visible.