Sociaro

bytedance/seedream-5-pro

Seedream 5.0 Pro — BytePlus's top image model.

Text-to-image and image-to-image, with two things the rest of the family does not have: it can take a finished picture APART into editable layers, and it can return a live alpha channel.

Sizes are a parameter here, not separate model ids.

Renders in roughly two minutes, so it is served asynchronously — submit, then poll.

Slugbytedance/seedream-5-pro
Kindimage
Vendorbytedance
EndpointPOST /v1/byteplus/images/async

Charged per job from what the generator reports it rendered — your Logs show the exact figure for each one; see balance and billing.

Calling it

curl https://api.sociaro.com/v1/byteplus/images/async \
  -H "Authorization: Bearer $SOCIARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "model": "bytedance/seedream-5-pro",
        "prompt": "a paper boat on a rain-soaked street"
      }'

Parameters

Seedream 5.0 Pro — text-to-image, image-to-image, and layer decomposition

FieldTypeRequiredDefaultValuesWhat it does
promptstringyes*—textWhat to generate. Optional in layer-decomposition mode, where the model finds the elements itself; required otherwise.
imagestring[]no—public https urlsPresent -> image-to-image. Exactly ONE url in layer-decomposition mode; more is an error.
sizestringno2K (auto when decomposing)1K · 1.5K · 2K, or WxH in pixels1.5K costs the same as 1K and looks better. Pixels are the only way to ask for a shape other than a square: area up to 4,624,220 px, aspect within 1:16..16:1. In decomposition mode only the presets and `auto` are accepted.
layer_decompositionbooleannofalsetrue · falseTakes one image apart into a base image plus up to 16 PNG layers with alpha. The poll then returns `layers[]` beside `media_url`. Charged per output image at half the generation rate — seventeen charges, not one.
backgroundstringnoopaquetransparent · opaque`transparent` keeps a live alpha channel. Image-to-image only, with exactly one input image that already has alpha; output is png, so `output_format: jpeg` beside it is an error.
output_formatstringnojpegjpeg · pngIn decomposition mode this governs the base image only — layers are always png.
response_formatstringnourlurlThe async route is url-only; `b64_json` is refused, because buffering full images would sink the relay.
watermarkbooleannofalse heretrue · falseBytePlus defaults this ON; we default it off. Set true if you want the mark.
seedintegernorandom—Accepted and forwarded. NOT in the vendor's published field list, so treat reproducibility as unverified.

Anything not listed here is refused rather than ignored, so a typo fails loudly instead of quietly producing something else.

Worked example

curl https://api.sociaro.com/v1/spicy/generations \
  -H "Authorization: Bearer $SOCIARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "model": "spicy/seedream-5-pro",
        "prompt": "a red paper boat on wet asphalt at night, neon reflections",
        "size": "1.5K"
      }'

# take a finished picture apart — base image plus up to 16 layers
curl https://api.sociaro.com/v1/spicy/generations \
  -H "Authorization: Bearer $SOCIARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "model": "spicy/seedream-5-pro",
        "image": ["https://your-cdn.example.com/scene.png"],
        "layer_decomposition": true
      }'

Taking a picture apart

With layer_decomposition: true the poll returns the base image in media_url and the layers beside it, bottom to top:

{ "job_id": "...", "status": "completed", "model": "spicy/seedream-5-pro",
  "media_url": "https://api.sociaro.com/v1/media/<base>",
  "layers": [
    { "url": "https://api.sociaro.com/v1/media/<id>", "z_index": 1,
      "name": "paper boat", "description": "a folded paper boat, wet, listing to port",
      "bounding_box": { "absolute": [225, 442, 796, 1414],
                        "normalized": [220, 432, 777, 1000] } }
  ] }

To rebuild the picture: place each layer at (left, top), scale it to right-left x bottom-top, and stack in ascending z_index. The base is z_index 0 and covers the whole canvas, which is why it carries no box; normalized is the same rectangle on a 0-1000 grid, for compositing onto a canvas of your own size.

Every layer url is a /v1/media link on the same terms as any other asset: opaque, its own expiry, no Authorization header. A layer we cannot serve is omitted rather than returned broken — the base image is unaffected.

Charged per OUTPUT image, at half the generation rate — so a decomposition returning a base plus sixteen layers is seventeen charges at that reduced rate, not one flat fee. Layers in the same response can land in different pixel tiers and are each charged on their own.

Two limits: exactly one input image, and no partial success — if a single layer fails, the whole request fails and nothing is charged.

Getting a transparent result

background: "transparent" returns a live alpha channel. It works only on image-to-image with exactly one input image that ALREADY has alpha, and the output is png:

{ "model": "spicy/seedream-5-pro", "prompt": "drop the background",
  "image": ["https://your-cdn.example.com/cutout.png"], "background": "transparent" }

Asking for output_format: "jpeg" beside it is an error, as is feeding it a jpeg. Both refusals come from the generator in its own words.

Shapes other than a square

This model has no aspect-ratio field — pixels are the only way to ask for one. 2816x1584 gives 16:9, 1584x2816 gives 9:16. Both the area (up to 4,624,220 px) and the ratio (within 1:16 to 16:1) must hold, and the generator names whichever one you missed.

How a job runs

These routes are a pass-through: you send BytePlus's own body and get their own response back, with our additions beside it. Generation takes longer than a request should wait, so it goes in three moves — submit, poll, collect.

# 1. submit — the id comes back as `id`. There is NO `job_id` on THIS response
curl -sS https://api.sociaro.com/v1/byteplus/contents/generations/tasks \
  -H "Authorization: Bearer $SOCIARO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "…", "content": [ … ] }'
# -> { "id": "cgt-…" }

# 2. poll every 5-10 s
curl -sS https://api.sociaro.com/v1/byteplus/contents/generations/tasks/cgt-… \
  -H "Authorization: Bearer $SOCIARO_API_KEY"
# -> { "status": "running", … }
# -> { "status": "succeeded", "content": { "video_url": "<the vendor's own link>" },
#      "media_url": "https://api.sociaro.com/v1/media/<media_id>" }

# 3. collect — the opaque id IS the credential, no header needed
curl -sSL "https://api.sociaro.com/v1/media/<media_id>" -o out.mp4

Images use POST /v1/byteplus/images/async and its poll in the same shape, and the faster models also answer synchronously at POST /v1/byteplus/images/generations — one request, the picture in the response. A model whose render outlasts the gateway's ~2-minute ceiling is refused there by name, telling you to use the async route.

Reading the poll

Two differences will bite a client written against the poll-style routes. The SUBMIT returns id, not job_id — code reading job_id there gets nothing and cannot poll at all. And status is the VENDOR'S vocabulary: running, succeeded, failed — not processing, completed, failed. A check for completed on this route never becomes true, which reads as a job that never finishes rather than as an error.

The POLL response does carry job_id beside the vendor's id, as a convenience, and it returns the vendor's whole body — around twenty fields — rather than the five-field shape the other routes use. Gate on status rather than on the poll's HTTP code.

Prefer media_url over the vendor's raw link. Both are returned: content.video_url (or data[].url for images) is theirs, kept for compatibility and due to be removed once nobody reads it; media_url is ours. Ours points at our host, hides the vendor's storage, and expires. A second asset arrives on the same terms — return_last_frame gives content.last_frame_url with last_frame_media_url beside it.

Collecting the result

media_url is a capability: the opaque id is the credential, so no Authorization header is needed and anyone holding the link can fetch it. It expires within the hour — download rather than store the link.

Rules the gateway adds

Your key must be granted this model — 403 otherwise — and either spelling satisfies the grant: our catalogue id or the vendor's own native one.

Because this is a pass-through, unknown fields go to the generator rather than being refused here, and it is the generator that decides. The exception is resolution, which we check ourselves: an unsupported tier is silently ignored upstream, so you would ask for the higher one, be handed a lower one, and be charged. A tier we cannot price is refused at submit, before any money is spent, with a message naming the ones this model has.

Ranges and enums are the generator's own, so an out-of-range value comes back as its rejection, in its wording and with its numbers.