GPT-6 Sol & Claude Opus 5.5 are liveGPT-6 Sol at half the price of 5.6 Sol

Seedream 5.0 Pro API: Generate Images With Python

One POST with model Dola-Seedream-5.0-pro creates the image task. Here is the request code, the size and label behaviour we saw, and the two images we made on October 29, 2026.

Kiki, OmniaKey COO7 min read
On this page

Send one POST to https://api.omniakey.com/v1/media/tasks with model set to Dola-Seedream-5.0-pro and operation set to generate, then poll the task id until the status reads succeeded and download assets[0].url. By default the image carries an "AI generated" label in the corner. On October 29, 2026 we ran that request and one edit at 1024x1024, and both files came back as 1024×1024 PNG.

Below is the id and the two operations, the request code we ran unchanged, a list of the sizes we asked for next to the pixel sizes we read from each file, the label, the setting that turns it off (which has not been run), and the two errors that stop a create call. Both images are the ones we made that day.

Which model id and operations to use

Use Dola-Seedream-5.0-pro, the id on the Seedream 5.0 Pro model page (checked October 29, 2026). The page lists two operations, text to image and image edit. It also says generating and editing share one rate, and that neither the size you pick nor reference images you attach add to it. Current rates are on the pricing page.

These are the fields that shape a request, before you get to the prompt:

  • model: Dola-Seedream-5.0-pro
  • operation: generate or edit
  • parameters.size: 1K, 1.5K, 2K, or widthxheight with an x
  • output_format: png or jpeg, and jpeg is the default

The size and format fields sit inside parameters. Output stops at 2K: the model page (checked October 29, 2026) lists no 4K size. Seedream takes a lowercase x between the sides, while Qwen and Wan take an asterisk, so keep the two forms apart in a client that calls both. Our runs used the widthxheight form at 1024x1024 with PNG, and the three tiers stayed out of our runs.

ByteDance sells the same model on BytePlus ModelArk under a different id, dola-seedream-5-0-pro-260628. That one belongs to BytePlus, so a request to OmniaKey wants the id above.

The request code

The script is the one we ran on October 29, 2026, unchanged: two helpers, one generate call, one edit call, Python standard library only. It reads your key from OMNIAKEY_API_KEY and writes each PNG into the folder you run it from. The image API docs describe the same three steps.

python
import base64
import json
import os
import time
import urllib.error
import urllib.request
import uuid

API = "https://api.omniakey.com/v1/media/tasks"
KEY = os.environ["OMNIAKEY_API_KEY"]
MODEL = "Dola-Seedream-5.0-pro"

def request(method, url, body=None, extra_headers=None):
    headers = {"Authorization": f"Bearer {KEY}", "User-Agent": "omniakey-example/1"}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode()
    headers.update(extra_headers or {})
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    with urllib.request.urlopen(req, timeout=120) as resp:
        return json.load(resp)

def poll(url, attempts=5):
    for attempt in range(attempts):
        try:
            return request("GET", url)
        except urllib.error.HTTPError as err:
            if err.code < 500 or attempt == attempts - 1:
                raise
        except urllib.error.URLError:
            if attempt == attempts - 1:
                raise
        time.sleep(5 * (attempt + 1))

def run_task(body, out_path):
    task = request("POST", API, body, {"Idempotency-Key": str(uuid.uuid4())})["id"]
    print("task", task, flush=True)
    for _ in range(150):
        time.sleep(4)
        state = poll(f"{API}/{task}")
        if state["status"] not in ("queued", "processing"):
            break
    if state["status"] != "succeeded":
        raise RuntimeError(state.get("error") or state["status"])
    asset_url = state["assets"][0]["url"]  # signed link: short-lived, so download now
    req = urllib.request.Request(asset_url, headers={"User-Agent": "omniakey-example/1"})
    with urllib.request.urlopen(req, timeout=120) as resp, open(out_path, "wb") as f:
        f.write(resp.read())
    return task

def generate_image(prompt, out_path, size="1024x1024"):
    body = {
        "model": MODEL,
        "type": "image",
        "operation": "generate",
        "input": {"prompt": prompt},
        "parameters": {"size": size, "output_format": "png"},
    }
    return run_task(body, out_path)

def edit_image(source_path, prompt, out_path, size="1024x1024"):
    with open(source_path, "rb") as f:
        image = base64.b64encode(f.read()).decode()
    body = {
        "model": MODEL,
        "type": "image",
        "operation": "edit",
        "input": {"prompt": prompt, "image": {"data": image, "mime_type": "image/jpeg"}},
        "parameters": {"size": size, "output_format": "png"},
    }
    return run_task(body, out_path)

if __name__ == "__main__":
    task = generate_image(
        "A glass bottle of cold-pressed orange juice on a pale stone counter, morning light from the left, "
        "a single orange slice beside it, shallow depth of field, no text, no logos.",
        "juice-bottle.png",
    )
    print("generate task", task)
    task = edit_image(
        "cafe-base.jpg",
        "Change the red bicycle to matte black. Keep everything else exactly the same: "
        "the table, the cup, the window frame and the daylight. Photorealistic, no text, no logos.",
        "cafe-edited.png",
    )
    print("edit task", task)

The three steps sit inside run_task: create the task with a fresh idempotency key, poll the task URL until the status leaves queued and processing, then download the first asset URL. The create call answers HTTP 202 with an id that starts with task_, and the status starts as queued (docs, checked October 29, 2026). A task that lands on failed raises with the error from the response, so the reason shows up in your terminal.

The sizes and formats we ran, and what came back

Both runs asked for the same size string and the same format, and both files came back as 1024×1024 PNG.

  • Generate — Size asked for: 1024x1024; Pixels in the file: 1024×1024; Format: PNG; Date: 2026-10-29
  • Edit — Size asked for: 1024x1024; Pixels in the file: 1024×1024; Format: PNG; Date: 2026-10-29

Read the pixel size off the file you downloaded. The size string is what you asked for; the file is what you got. We opened each PNG, read its width and height, and looked at it next to the prompt that made it.

The edit is the more interesting run. We sent a 1024×683 JPEG of a café window and one instruction, and a square image came back. The bicycle is now dark, the table, the cup and the plant are still in the picture, and because the output is square, the window and the table edge are framed differently from the source.

prompt
Change the red bicycle to matte black. Keep everything else exactly the same: the table, the cup, the window frame and the daylight. Photorealistic, no text, no logos.
Left: a café window with a red bicycle outside. Right: the same window after the edit, with the bicycle now dark, in a square frame.
Left: the source photo we sent, 1024×683. Right: the output of one edit on October 29, 2026, size 1024x1024. The bicycle is now dark. The output is a square, so the window and the table edge are framed differently from the source.

The label, and how to turn it off

Every output carries an "AI generated" label in the corner unless you set watermark to false in parameters, per the model page (checked October 29, 2026). The page puts the label in the bottom-right corner, and that is where it sits in the default output below: faint and small. Leave watermark out of the request and you get the label.

prompt
A glass bottle of cold-pressed orange juice on a pale stone counter, morning light from the left, a single orange slice beside it, shallow depth of field, no text, no logos.

Here is that output, made with the default settings:

A glass bottle of orange juice with an orange slice on a pale stone counter, with a faint AI-generated label in the corner.
Generated with Seedream 5.0 Pro via the OmniaKey API on October 29, 2026, size 1024x1024, default settings. The faint 'AI generated' label sits in the bottom-right corner.

The watermark-false setting was not run: the blog tool's per-run spend cap stopped that call before it was sent.

Errors and limits

Two headers decide whether a create call goes through: the bearer token and an Idempotency-Key.

What you sendWhat comes back
Create call with no idempotency key400 missing_idempotency_key
Same key and same body, again200 with the existing task, nothing new charged
Same key, changed body409 idempotency_key_reused

Same key with the same body is the retry you want: the existing task comes back and nothing new is charged. Same key with an edited prompt or size returns 409, so make a new key for every new request. The key can be up to 255 characters (docs, checked October 29, 2026).

Keep the request body under 1 MiB, Base64 image included, and resize a large source photo before you send it. When a task succeeds, the download URL in the response is a signed link that lasts about an hour, per the docs, so fetch the file at once or poll the task again for a fresh link.

One header surprised us on October 29, 2026. A POST sent with Python's default urllib User-Agent returned HTTP 403 with the body error code: 1010 from Cloudflare. Our own User-Agent header reached the API, and nothing was charged.

Send a User-Agent header of your own, such as omniakey-example/1. Python's default one got the 403 above on October 29, 2026.

Seedream or GPT Image 2.5 for API work?

The job decides. In our edit, Seedream 5.0 Pro changed the one object we named, the bicycle, and kept most of the scene, though the frame changed. Product photos have not been tested here. The call is one body with operation set to generate or edit, and the source can be a public URL or inline Base64 (docs, checked October 29, 2026).

For a same-prompt result next to GPT Image 2, the Seedream 5.0 Pro vs GPT Image 2 comparison shows both outputs from one prompt. For the same generate-and-edit shape on another model, the GPT Image 2.5 API tutorial walks through its request and sizes. Run your hardest prompt on the candidate before you move a pipeline over, then check the rates.

FAQ

Which model id do I pass to the API?

Dola-Seedream-5.0-pro, the id in the model page's sample request (checked October 29, 2026). ByteDance's own dola-seedream-5-0-pro-260628 belongs to BytePlus ModelArk, and OmniaKey takes the id above.

Why does my image have an "AI generated" label?

Every Seedream 5.0 Pro output carries the label in the bottom-right corner unless you set watermark to false, per the model page (checked October 29, 2026). Our default-setting run on October 29, 2026 carried it.

Which sizes does the API accept?

The tiers 1K, 1.5K and 2K, or the widthxheight form with a lowercase x between the sides (model page, checked October 29, 2026). Output stops at 2K, and both of our runs asked for 1024x1024.

Can I get PNG instead of JPEG?

Yes. output_format takes png or jpeg, and JPEG is the default (docs, checked October 29, 2026). We ran PNG in both runs and got PNG back.

Can I edit an existing image with Seedream?

Yes: send operation set to edit with the prompt and the source image, either as inline Base64 with its mime type or as a public URL. Our café window edit is in the sizes section above.

Models in this article

Seedream 5.0 Proimage APIPythontext-to-imageAPI

Related articles

Recent posts