Preços+7% bônus

Como Gerar Imagens de Produto em Massa com a API Seedream 5.0 Pro

Crie um fluxo de trabalho da API Seedream 5.0 Pro orientado por CSV com entradas de múltiplas referências, concorrência controlada, novas tentativas, estimativas de custo e revisão humana.

Como Gerar Imagens de Produto em Massa com a API Seedream 5.0 Pro

Um catálogo de 100 SKUs não é uma única solicitação de imagem. Cada produto precisa de suas próprias imagens de referência, variáveis de prompt, nome de arquivo, registro de tentativas e estado de aprovação. Copiar prompts para um playground pode gerar imagens, mas não consegue dizer a qual versão pertence MUG-001 nem se uma imagem de bolsa que falhou deve ser tentada novamente.

A solução prática é uma solicitação do Seedream por SKU, executada por meio de um pequeno pool de workers. É isso que “em massa” significa aqui. Atualmente, a BytePlus lista texto para múltiplas imagens como não suportado para o Seedream 5.0 Pro, então um único prompt não retorna um catálogo inteiro. Em vez disso, um CSV fornece os dados do produto, o Python envia várias solicitações independentes a uma taxa controlada, e um manifesto registra cada resultado.

O fluxo de trabalho final usa a API do Seedream 5.0 Pro no GPTProto e produz:

  • products.csv, com uma linha por imagem de produto;

  • bulk_seedream_products.py, que cria e executa os trabalhos;

  • generated/, com nomes de arquivo vinculados aos SKUs; e

  • manifest.json, com tentativas, erros, locais de saída e status de revisão.

Índice

O que “Seedream 5.0 Pro API Batch” realmente significa

Três frases são frequentemente tratadas como sinônimos. Elas não são.

Termo Significado Comportamento atual
Entrada com múltiplas referências Várias imagens de origem orientam uma edição O endpoint de edição de imagem da GPT Proto aceita até 10 referências
Múltiplas saídas Um prompt retorna várias imagens separadas Não exposto para o Seedream 5.0 Pro
Geração em massa Muitas solicitações específicas por SKU passam por uma fila O fluxo de trabalho criado abaixo

A entrada com múltiplas referências melhora um resultado. Ela não cria dez resultados. Uma vista frontal do produto, um close-up do material e uma referência de iluminação podem todos orientar uma única imagem final. Para criar imagens para 100 produtos, o cliente ainda envia 100 solicitações.

Essa distinção afeta tudo a jusante. Um SKU por solicitação dá a cada saída um proprietário, nome de arquivo, registro de custo e histórico de tentativas. A concorrência faz a fila andar mais rápido; ela não transforma o modelo em um endpoint de múltiplas saídas. O comportamento subjacente do modelo está documentado na matriz de geração de imagens da BytePlus, enquanto a GPT Proto expõe o modelo por meio de suas próprias rotas /api/v3/.

O que você vai construir

Os dados passam por cinco estágios:

catálogo CSV → prompt e referências → pool de solicitações controlado → arquivos baixados → revisão humana

O script usa solicitações síncronas com enable_sync_mode: true. Essa escolha é deliberada. A página ao vivo do modelo da GPT Proto documenta um fluxo de trabalho assíncrono com result_id, mas o esquema completo de resposta de status e saída não é publicado nessa página. Um tutorial de produção não deve inventar esses campos.

O modo síncrono ainda oferece suporte à geração em massa. Cada worker processa um produto desde a solicitação até o download, enquanto o ThreadPoolExecutor permite que um pequeno número de workers seja executado ao mesmo tempo. O manifesto é escrito pela thread principal após cada worker terminar, para que execuções interrompidas possam pular arquivos que já estão presentes.

Para uma versão posterior de alto rendimento, defina enable_sync_mode como false somente depois de capturar uma resposta ao vivo e confirmar os campos atuais de task ID, status, output e error. Em seguida, armazene o result_id retornado antes de consultar /api/v3/predictions/{result_id}/result.

O que você precisa antes de começar

Instale o Python e a única dependência:

python --version
python -m pip install requests

Crie uma chave de API da GPT Proto e mantenha-a em uma variável de ambiente:

export GPTPROTO_API_KEY="your-key-here"

Os exemplos atuais da API ao vivo usam este formato de cabeçalho:

Authorization: Bearer YOUR_API_KEY

Você também precisa de referências de produto em URLs HTTPS públicas. Um caminho como /Users/me/photos/mug.jpg existe apenas no seu computador. A API remota não consegue buscá-lo. Coloque os arquivos em um CDN, bucket de armazenamento de objetos ou outro local HTTPS que a API possa acessar sem cookie de login.

Comece com uma pequena fatia do catálogo. Escolha produtos com formatos, materiais e complexidade de rótulo diferentes. Uma caneca simples pode passar enquanto uma bolsa com várias alças falha, então um teste fácil não é suficiente para aprovar um prompt compartilhado.

Estime o custo do lote antes de executá-lo

O preço ao vivo do Seedream 5.0 Pro da GPT Proto foi verificado em 17 de setembro de 2026. Use a página do modelo para preços por imagem e a página de preços da GPT Proto para informações de preços no nível da conta.

Saída Preço base, incluindo a primeira referência Cada referência adicional
1K $0.0405 por imagem +$0.0027
2K $0.0810 por imagem +$0.0027

A atual API de Edição de Imagem Seedream 5.0 Pro aceita até 10 imagens de referência. A primeira está incluída no preço base. As referências de dois a dez adicionam $0.0027 cada.

Isso produz estes números de planejamento antes de novas tentativas ou gerações rejeitadas:

Lote Custo estimado da API
100 imagens com uma referência em 1K $4.05
100 imagens com uma referência em 2K $8.10
1.000 imagens com uma referência em 1K $40.50
1.000 imagens com uma referência em 2K $81.00
100 imagens com três referências em 1K $4.59
100 imagens com três referências em 2K $8.64

A conta de geração bruta é fácil de estimar. O custo por imagem aprovada não é. Se um produto precisar de várias tentativas, cada imagem gerada conta. Use esta fórmula para um orçamento mais realista:

estimated catalog cost =
  number of SKUs
  × average attempts per approved image
  × (base output price + paid reference-image cost)

Use 1K para testar o sistema de prompt em produtos representativos. Não o trate como uma prévia garantida de uma chamada posterior em 2K. A solicitação em 2K cria uma nova imagem, então a composição e os detalhes podem mudar. Revise novamente o resultado na resolução final.

Etapa 1: Crie um CSV de produtos

Salve isto como products.csv:

sku,name,color,material,angle,background,product_image_url,detail_image_url,style_reference_url,avoid
MUG-001,Stackable coffee mug,matte navy,stoneware,three-quarter,soft warm gray,https://cdn.example.com/mug-front.jpg,https://cdn.example.com/mug-glaze.jpg,,steam or hands
BAG-014,Compact crossbody bag,forest green,pebbled leather,front three-quarter,light beige,https://cdn.example.com/bag-front.jpg,https://cdn.example.com/bag-texture.jpg,https://cdn.example.com/style-neutral.jpg,model or extra straps

Substitua todas as URLs cdn.example.com antes de executar o script. Os campos do CSV têm funções diferentes:

  • sku é a chave permanente do produto. Ele também se torna parte do nome do arquivo de saída.

  • name, color, material, angle e background contêm fatos que mudam por produto.

  • product_image_url é a referência de identidade e é obrigatória.

  • detail_image_url pode mostrar material, controles, ferragens ou um close-up do rótulo.

  • style_reference_url controla iluminação e tratamento de fundo, não a identidade do produto.

  • avoid lista alguns erros visíveis que importam para aquele SKU.

Mantenha os valores curtos e factuais. Não coloque um briefing criativo inteiro em uma célula. Não coloque a chave de API no CSV, no código-fonte ou no manifesto.

O script abaixo rejeita SKUs duplicados, URLs de produto ausentes, referências não HTTPS e mais de dez referências antes de enviar uma solicitação paga.

Etapa 2: Dê a cada imagem de referência uma função

Uma solicitação de imagem com múltiplas referências da API Seedream 5.0 Pro funciona melhor quando o prompt explica por que cada imagem existe. Use uma ordem estável:

  1. Referência de identidade do produto: silhueta, proporções, design visível e embalagem.

  2. Referência de detalhe: material, ferragens, controles ou tratamento de rótulo.

  3. Referência de estilo: apenas iluminação, superfície, paleta e fundo.

O prompt gerado incluirá instruções como:

Image 1 is the product identity reference. Preserve its silhouette, proportions, visible design, and packaging. Image 2 is the detail reference. Use it for material and hardware details. Image 3 is the lighting and background reference only. Do not copy objects from it.

Comece com uma a três referências úteis. Dez entradas não rotuladas custam mais e podem entrar em conflito umas com as outras. Mais imagens não são automaticamente mais precisas.

Um post de lançamento do ComfyUI descreve características estáveis de produto em variações de cena como um caso de uso de produção. Trate isso como uma afirmação de fluxo de trabalho, não como um benchmark independente. A identidade do produto ainda precisa de uma revisão lado a lado com a origem.

Etapa 3: Crie um prompt de produto reutilizável

Mantenha regras para todo o catálogo no código e fatos do SKU no CSV. Uma ordem útil é:

asset type
→ product identity and protected details
→ requested background
→ camera and composition
→ lighting and shadow
→ material behavior
→ exclusions

Para a linha da caneca, o script produz instruções próximas a isto:

Create one square e-commerce hero photograph of the Stackable coffee mug.
The product is matte navy stoneware. Preserve the product identity, silhouette,
proportions, visible design, color family, material, and packaging details from
the references. Show a three-quarter view on a soft warm gray background.
Use a large diffused key light from the upper left and a natural contact shadow.
Keep the whole product inside the frame with crop-safe space. Return one product
image, not a collage or contact sheet. Do not add steam or hands. Do not invent
logos, labels, accessories, duplicate products, handles, straps, or controls.

Nomeie detalhes observáveis. “Qualidade premium” fornece pouca informação física ao modelo. “Sombra de contato suave caindo para trás à direita” fornece. Mantenha as exclusões curtas porque a solicitação atual da GPT Proto não expõe um campo separado negative_prompt.

Para mais padrões de prompt, use o Guia de Prompt do Seedream 5.0 Pro.

Etapa 4: Teste um produto antes de iniciar a fila

Execute uma solicitação síncrona de edição de imagem primeiro:

curl --request POST \
  --url "https://gptproto.com/api/v3/doubao/dola-seedream-5-0-pro-260628/image-edit" \
  --header "Authorization: Bearer $GPTPROTO_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "images": [
      "https://your-cdn.example.com/mug-front.jpg",
      "https://your-cdn.example.com/mug-glaze.jpg"
    ],
    "prompt": "Create one square e-commerce hero photograph of the referenced mug. Image 1 is the product identity reference. Preserve its silhouette, proportions, handle, color, and visible design. Image 2 is the glaze detail reference. Show a three-quarter view on a soft warm gray background with a natural contact shadow. Keep the entire product inside the frame. No hands, steam, extra text, duplicate products, or collage.",
    "size": "1024x1024",
    "enable_base64_output": false,
    "enable_sync_mode": true,
    "output_format": "png"
  }'

O endpoint e o cabeçalho Bearer atual correspondem ao exemplo do modelo ao vivo da GPT Proto. Imprima e retenha a primeira resposta JSON bem-sucedida antes de adicionar mais automação. Verifique se ela contém uma saída que seu cliente possa baixar.

Não continue só porque a imagem parece atraente. Compare-a com as referências:

  • A silhueta é reconhecível?

  • A cor e o material estão aceitavelmente próximos?

  • O modelo adicionou ou removeu uma alça, correia, botão, tampa ou rótulo?

  • O texto obrigatório está correto caractere por caractere?

  • O produto faz contato físico com a superfície?

  • Há espaço suficiente seguro para corte para o posicionamento pretendido no marketplace?

Teste um produto representativo e um produto difícil. Ambos devem passar antes que a fila completa comece.

Etapa 5: Execute uma solicitação por SKU

O script em lote envia o mesmo payload de edição de imagem usado no teste cURL. As variáveis vêm de cada linha do CSV. MAX_WORKERS controla quantas solicitações independentes podem estar ativas ao mesmo tempo.

Comece com um valor pequeno, como 2 ou 3. A documentação da BytePlus pode publicar limites para contas diretas da BytePlus, mas esses números não são automaticamente os limites da sua conta GPT Proto. Se a API retornar 429, reduza a contagem de workers e siga as orientações de nova tentativa da resposta.

O script usa nomes determinísticos:

{sku}_hero_{attempt:02d}.{extension}

Exemplos incluem MUG-001_hero_01.png e BAG-014_hero_02.png. A tentativa permanece visível, para que um revisor possa rastrear o arquivo selecionado de volta ao manifesto.

Etapa 6: Adicione novas tentativas e suporte à retomada

Nem toda falha deve disparar outra solicitação paga.

O script tenta novamente respostas explícitas 429 e 5xx com backoff exponencial e jitter. Ele também respeita um cabeçalho numérico Retry-After quando presente. Erros de autenticação, parâmetro inválido, saldo e moderação são registrados sem um reenvio inalterado.

Timeouts de leitura e conexões interrompidas são diferentes. O servidor pode ter aceitado a solicitação mesmo que o cliente não tenha recebido a resposta. Sem uma chave de idempotência documentada, um reenvio automático poderia criar uma segunda imagem e uma segunda cobrança. O script marca esses casos como manual_check. Isso é mais lento do que tentar novamente às cegas. Também é mais seguro para um orçamento de produção.

Após cada worker terminar, a thread principal atualiza manifest.json por meio de uma substituição atômica de arquivo. Ao reiniciar:

  • um item com status downloaded, approved ou manual_review e um arquivo local existente é ignorado;

  • um item failed_retryable continua somente se ainda houver tentativas;

  • um item manual_check não é enviado novamente de forma automática; e

  • um item failed mantém seu erro e é ignorado até que um operador corrija a causa, então remove essa entrada de SKU do manifesto ou altera seu status para failed_retryable.

Este design síncrono não pode salvar um ID de tarefa assíncrona. Se você adotar posteriormente o fluxo de trabalho em segundo plano documentado, adicione task_id ao mesmo manifesto e salve o result_id ao vivo imediatamente após o envio.

Script Python completo para imagens de produtos Seedream em massa

Salve o seguinte como bulk_seedream_products.py ao lado de products.csv.

from __future__ import annotations

import base64
import csv
import json
import os
import random
import re
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from urllib.parse import urlparse

import requests

API_URL = (
    "https://gptproto.com/api/v3/doubao/"
    "dola-seedream-5-0-pro-260628/image-edit"
)
CSV_PATH = Path(os.getenv("PRODUCTS_CSV", "products.csv"))
OUTPUT_DIR = Path(os.getenv("OUTPUT_DIR", "generated"))
MANIFEST_PATH = Path(os.getenv("MANIFEST_PATH", "manifest.json"))

SIZE = os.getenv("SEEDREAM_SIZE", "1024x1024")
OUTPUT_FORMAT = os.getenv("SEEDREAM_OUTPUT_FORMAT", "png")
MAX_WORKERS = int(os.getenv("MAX_WORKERS", "3"))
MAX_ATTEMPTS = int(os.getenv("MAX_ATTEMPTS", "4"))
REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "300"))
PROMPT_VERSION = os.getenv("PROMPT_VERSION", "product-hero-v1")

REFERENCE_FIELDS = (
    (
        "product_image_url",
        "the product identity reference. Preserve its silhouette, proportions, "
        "visible design, color family, material, and packaging",
    ),
    (
        "detail_image_url",
        "the detail reference. Use it for material, hardware, controls, and label details",
    ),
    (
        "style_reference_url",
        "the lighting and background reference only. Do not copy products or props from it",
    ),
)

def now_utc() -> str:
    return datetime.now(timezone.utc).isoformat()

def is_https_url(value: str) -> bool:
    parsed = urlparse(value)
    return parsed.scheme == "https" and bool(parsed.netloc)

def clean_filename(value: str) -> str:
    cleaned = re.sub(r"[^A-Za-z0-9._-]+", "-", value.strip())
    return cleaned.strip("-._") or "product"

def reference_data(row: dict[str, str]) -> tuple[list[str], list[str]]:
    urls: list[str] = []
    role_lines: list[str] = []
    for field, role in REFERENCE_FIELDS:
        url = row.get(field, "").strip()
        if not url:
            continue
        urls.append(url)
        role_lines.append(f"Image {len(urls)} is {role}.")
    return urls, role_lines

def validate_and_load_rows(path: Path) -> list[dict[str, str]]:
    required = {
        "sku",
        "name",
        "color",
        "material",
        "angle",
        "background",
        "product_image_url",
        "detail_image_url",
        "style_reference_url",
        "avoid",
    }

    with path.open("r", encoding="utf-8-sig", newline="") as handle:
        reader = csv.DictReader(handle)
        fields = set(reader.fieldnames or [])
        missing = required - fields
        if missing:
            raise ValueError(f"CSV is missing columns: {sorted(missing)}")

        rows: list[dict[str, str]] = []
        seen_skus: set[str] = set()

        for line_number, raw_row in enumerate(reader, start=2):
            row = {key: (value or "").strip() for key, value in raw_row.items()}
            sku = row["sku"]
            if not sku:
                raise ValueError(f"Line {line_number}: sku is required")
            if sku in seen_skus:
                raise ValueError(f"Line {line_number}: duplicate sku {sku!r}")
            seen_skus.add(sku)

            urls, _ = reference_data(row)
            if not row["product_image_url"]:
                raise ValueError(
                    f"Line {line_number}: product_image_url is required"
                )
            if len(urls) > 10:
                raise ValueError(
                    f"Line {line_number}: {sku} has more than 10 references"
                )
            invalid_urls = [url for url in urls if not is_https_url(url)]
            if invalid_urls:
                raise ValueError(
                    f"Line {line_number}: references must be public HTTPS URLs: "
                    f"{invalid_urls}"
                )
            rows.append(row)

    if not rows:
        raise ValueError("The CSV contains no product rows")
    return rows

def build_prompt(row: dict[str, str], role_lines: list[str]) -> str:
    avoid = row.get("avoid") or "extra products or unrelated props"
    references = " ".join(role_lines)
    return (
        f"Create one square e-commerce hero photograph of the {row['name']}. "
        f"{references} "
        f"The product is {row['color']} {row['material']}. "
        "Preserve the product identity, silhouette, proportions, visible design, "
        "color family, material, and packaging details from the references. "
        f"Show a {row['angle']} view on a {row['background']} background. "
        "Use a large diffused key light from the upper left and a natural contact "
        "shadow falling back-right. Keep the whole product inside the frame with "
        "crop-safe space. Return one product image, not a collage or contact sheet. "
        f"Do not add {avoid}. Do not invent logos, labels, accessories, duplicate "
        "products, handles, straps, buttons, caps, or controls."
    )

def load_manifest(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {"prompt_version": PROMPT_VERSION, "items": {}}
    with path.open("r", encoding="utf-8") as handle:
        data = json.load(handle)
    if not isinstance(data.get("items"), dict):
        raise ValueError("manifest.json must contain an object named 'items'")
    return data

def save_manifest(path: Path, manifest: dict[str, Any]) -> None:
    temporary = path.with_suffix(path.suffix + ".tmp")
    with temporary.open("w", encoding="utf-8") as handle:
        json.dump(manifest, handle, indent=2, ensure_ascii=False)
        handle.write("\n")
    os.replace(temporary, path)

def output_items(payload: dict[str, Any]) -> list[Any]:
    data = payload.get("data")
    containers = [data, payload] if isinstance(data, dict) else [payload]
    for container in containers:
        for key in ("outputs", "output", "images", "image"):
            value = container.get(key)
            if value:
                return value if isinstance(value, list) else [value]
    raise ValueError(
        "No output field found. Inspect the saved response and update output_items() "
        "to match the current API response."
    )

def output_value(item: Any) -> str:
    if isinstance(item, str):
        return item
    if isinstance(item, dict):
        for key in ("url", "image_url", "b64_json", "base64"):
            value = item.get(key)
            if isinstance(value, str) and value:
                return value
    raise ValueError(f"Unsupported output item: {item!r}")

def extension_from_content_type(content_type: str) -> str:
    mime = content_type.split(";", 1)[0].strip().lower()
    return {
        "image/png": ".png",
        "image/jpeg": ".jpg",
        "image/webp": ".webp",
    }.get(mime, f".{OUTPUT_FORMAT}")

def save_output(value: str, file_stem: Path) -> tuple[Path, str | None]:
    if value.startswith("data:image/"):
        header, encoded = value.split(",", 1)
        mime = header.split(";", 1)[0].removeprefix("data:")
        destination = file_stem.with_suffix(extension_from_content_type(mime))
        destination.write_bytes(base64.b64decode(encoded))
        return destination, None

    if value.startswith("https://") or value.startswith("http://"):
        response = requests.get(value, timeout=REQUEST_TIMEOUT)
        response.raise_for_status()
        extension = extension_from_content_type(
            response.headers.get("Content-Type", "")
        )
        destination = file_stem.with_suffix(extension)
        destination.write_bytes(response.content)
        return destination, value

# Some responses return raw Base64 without a data URI.
    try:
        decoded = base64.b64decode(value, validate=True)
    except ValueError as exc:
        raise ValueError("Output is neither a URL nor valid Base64") from exc
    destination = file_stem.with_suffix(f".{OUTPUT_FORMAT}")
    destination.write_bytes(decoded)
    return destination, None

def backoff_seconds(attempt: int, response: requests.Response | None) -> float:
    if response is not None:
        retry_after = response.headers.get("Retry-After")
        if retry_after:
            try:
                return max(0.0, float(retry_after))
            except ValueError:
                pass
    return min(60.0, (2 ** (attempt - 1)) + random.uniform(0.0, 1.0))

def result_record(
    row: dict[str, str],
    references: list[str],
    status: str,
    attempt: int,
    *,
    output_url: str | None = None,
    local_path: str | None = None,
    error: str | None = None,
    created_at: str | None = None,
) -> dict[str, Any]:
    timestamp = now_utc()
    return {
        "sku": row["sku"],
        "asset_type": "hero",
        "status": status,
        "attempt": attempt,
        "prompt_version": PROMPT_VERSION,
        "reference_urls": references,
        "output_url": output_url,
        "local_path": local_path,
        "error": error,
        "requester": "",
        "reviewer": "",
        "approval_status": "pending" if status == "downloaded" else "",
        "created_at": created_at or timestamp,
        "updated_at": timestamp,
    }

def process_product(
    row: dict[str, str], previous: dict[str, Any] | None
) -> dict[str, Any]:
    api_key = os.environ["GPTPROTO_API_KEY"]
    references, role_lines = reference_data(row)
    prompt = build_prompt(row, role_lines)
    created_at = (previous or {}).get("created_at") or now_utc()
    first_attempt = int((previous or {}).get("attempt", 0)) + 1

    if first_attempt > MAX_ATTEMPTS:
        return result_record(
            row,
            references,
            "failed",
            first_attempt - 1,
            error="Maximum attempts already reached",
            created_at=created_at,
        )

    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }
    body = {
        "images": references,
        "prompt": prompt,
        "size": SIZE,
        "enable_base64_output": False,
        "enable_sync_mode": True,
        "output_format": OUTPUT_FORMAT,
    }

    for attempt in range(first_attempt, MAX_ATTEMPTS + 1):
        response: requests.Response | None = None
        try:
            response = requests.post(
                API_URL,
                headers=headers,
                json=body,
                timeout=REQUEST_TIMEOUT,
            )

            if response.status_code == 429 or response.status_code >= 500:
                message = f"HTTP {response.status_code}: {response.text[:500]}"
                if attempt == MAX_ATTEMPTS:
                    return result_record(
                        row,
                        references,
                        "failed_retryable",
                        attempt,
                        error=message,
                        created_at=created_at,
                    )
                time.sleep(backoff_seconds(attempt, response))
                continue

            response.raise_for_status()
            payload = response.json()
            item = output_items(payload)[0]
            value = output_value(item)
            file_stem = OUTPUT_DIR / (
                f"{clean_filename(row['sku'])}_hero_{attempt:02d}"
            )
            local_file, output_url = save_output(value, file_stem)
            return result_record(
                row,
                references,
                "downloaded",
                attempt,
                output_url=output_url,
                local_path=str(local_file),
                created_at=created_at,
            )

        except requests.exceptions.ConnectTimeout as exc:

# No connection was established; a bounded retry is reasonable.
            if attempt == MAX_ATTEMPTS:
                return result_record(
                    row,
                    references,
                    "failed_retryable",
                    attempt,
                    error=str(exc),
                    created_at=created_at,
                )
            time.sleep(backoff_seconds(attempt, response))

        except (
            requests.exceptions.ReadTimeout,
            requests.exceptions.ConnectionError,
        ) as exc:

# The server may have accepted the request. Avoid a blind resubmission.
            return result_record(
                row,
                references,
                "manual_check",
                attempt,
                error=f"Ambiguous network failure; verify account history: {exc}",
                created_at=created_at,
            )

        except requests.exceptions.HTTPError as exc:
            status_code = response.status_code if response is not None else "unknown"
            details = response.text[:500] if response is not None else str(exc)
            return result_record(
                row,
                references,
                "failed",
                attempt,
                error=f"HTTP {status_code}: {details}",
                created_at=created_at,
            )

        except (ValueError, KeyError, json.JSONDecodeError) as exc:
            return result_record(
                row,
                references,
                "manual_check",
                attempt,
                error=str(exc),
                created_at=created_at,
            )

    return result_record(
        row,
        references,
        "failed",
        MAX_ATTEMPTS,
        error="Request ended without a result",
        created_at=created_at,
    )

def should_skip(entry: dict[str, Any] | None) -> bool:
    if not entry:
        return False
    if entry.get("status") in {"manual_check", "failed"}:
        return True
    if entry.get("status") not in {"downloaded", "manual_review", "approved"}:
        return False
    local_path = entry.get("local_path")
    return bool(local_path and Path(local_path).exists())

def main() -> None:
    if not os.getenv("GPTPROTO_API_KEY"):
        raise SystemExit("Set GPTPROTO_API_KEY before running the script")
    if MAX_WORKERS < 1:
        raise SystemExit("MAX_WORKERS must be at least 1")

    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    rows = validate_and_load_rows(CSV_PATH)
    manifest = load_manifest(MANIFEST_PATH)
    items: dict[str, Any] = manifest["items"]

    pending: list[dict[str, str]] = []
    for row in rows:
        entry = items.get(row["sku"])
        if should_skip(entry):
            print(f"SKIP {row['sku']}: {entry['status']}")
        else:
            pending.append(row)

    if not pending:
        print("Nothing to submit")
        return

    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        future_to_row = {
            executor.submit(process_product, row, items.get(row["sku"])): row
            for row in pending
        }
        for future in as_completed(future_to_row):
            row = future_to_row[future]
            try:
                record = future.result()
            except Exception as exc:  # Preserve an unexpected worker failure.
                references, _ = reference_data(row)
                record = result_record(
                    row,
                    references,
                    "failed",
                    int(items.get(row["sku"], {}).get("attempt", 0)),
                    error=f"Unexpected worker error: {exc}",
                )
            items[row["sku"]] = record
            manifest["prompt_version"] = PROMPT_VERSION
            manifest["updated_at"] = now_utc()
            save_manifest(MANIFEST_PATH, manifest)
            print(f"{record['status'].upper()} {row['sku']}: {record.get('local_path') or record.get('error')}")

if __name__ == "__main__":
    main()

Execute com:

python bulk_seedream_products.py

Você pode alterar a contagem de workers e o tamanho de saída sem editar o arquivo:

MAX_WORKERS=2 SEEDREAM_SIZE=2048x2048 python bulk_seedream_products.py

O console informa cada SKU quando ele termina. Abra manifest.json para ver quais arquivos foram baixados, falharam ou estão retidos para verificação manual. Se a resposta ao vivo usar um formato de campo não coberto por output_items(), o script registra a incompatibilidade em vez de tratar silenciosamente o trabalho como concluído. Atualize esse pequeno parser usando o JSON capturado durante o teste de produto único.

Revise cada imagem antes que ela chegue à loja

Uma imagem baixada não é uma imagem aprovada. Mantenha o status de geração e o status de aprovação separados.

Verificação Falha comum
Geometria Tampa, correia, botão, alça ou proporções alteradas
Cor Tom, saturação ou acabamento de superfície errados
Material Plástico substitui metal; textura de couro fica plana
Embalagem Logo inventado, rótulo com erro de ortografia ou volume alterado
Cena Objeto flutuando ou sombra de contato implausível
Corte Produto cortado em miniaturas do marketplace
Duplicação Um produto ou acessório extra aparece

Mova um registro de pending para approved, rejected ou needs_edit somente depois que uma pessoa comparar a saída com as imagens de origem. Se o texto do rótulo precisar ser legalmente exato, mantenha o rótulo de origem na pós-produção em vez de confiar em texto gerado sem verificação.

Para um benchmark de qualidade próprio, execute uma pequena matriz antes de escalar:

  • um frasco rotulado em um fundo de catálogo, cena lifestyle e layout de campanha;

  • uma bolsa de couro nos mesmos três tipos de cena; e

  • um abajur com geometria e detalhes de controle visíveis.

Registre a taxa de aprovação na primeira passagem, tentativas por imagem aprovada, falhas de identidade, erros de rótulo e custo de API por imagem aprovada. Essas medições descrevem seu catálogo melhor do que uma demonstração genérica de modelo.

Torne o fluxo de trabalho utilizável por uma equipe

O manifesto é o contrato de transferência entre o operador, o designer e o revisor. Mantenha estes campos mesmo que uma pessoa atualmente faça todo o trabalho:

Campo Por que importa
sku Chave primária permanente para o produto
prompt_version Identifica as regras compartilhadas usadas para a saída
reference_urls Reconstrói o conjunto exato de origens
attempt Separa reexecuções e dá suporte à análise de custos
requester e reviewer Atribui propriedade
approval_status Mantém ativos gerados e publicáveis distintos
output_url e local_path Conecta o resultado da API ao arquivo armazenado
error Preserva o motivo pelo qual uma linha parou
timestamps Mostra quando o registro mudou

Trate o SKU como imutável. Se o prompt compartilhado mudar, incremente PROMPT_VERSION em vez de sobrescrever o histórico. Reexecute linhas rejeitadas, não o catálogo inteiro. Armazene ativos aprovados separadamente dos rascunhos e nunca faça commit de uma chave de API no mesmo repositório.

Esta é a diferença entre um script em lote e um fluxo de trabalho de equipe. O script produz arquivos. Os registros explicam como esses arquivos foram produzidos e se eles podem ser publicados.

Problemas comuns e correções

Problema Causa provável Correção
401 ou 403 Chave, cabeçalho, acesso à conta ou saldo Confirme o cabeçalho Bearer atual na página do modelo ao vivo e verifique a conta
A referência não carrega URL privada, local, expirada ou não HTTPS Use uma URL HTTPS pública estável e teste-a fora do seu navegador com login
429 Muitas solicitações simultâneas Reduza MAX_WORKERS; respeite a resposta de nova tentativa documentada
A solicitação retorna uma tarefa, mas nenhuma imagem O modo assíncrono está ativo Use enable_sync_mode: true para este script, ou implemente polling a partir de uma resposta ao vivo verificada
Possível duplicata após timeout O cliente perdeu uma resposta após o envio Não tente novamente às cegas; verifique o histórico da conta e marque a linha manualmente
O produto muda demais As funções das referências ou detalhes protegidos estão vagos Nomeie a referência de identidade e liste as partes visíveis que devem permanecer
Objetos extras ou colagem O entregável está subespecificado Peça uma imagem de produto e rejeite explicitamente colagens e duplicatas
O texto do rótulo está errado A renderização generativa não é exata Verifique cada caractere; preserve texto regulamentado exato na pós-produção
O parser de saída não encontra imagem O formato JSON atual difere Inspecione a resposta do teste único e atualize output_items()

Não adicione novas tentativas até saber se a falha ocorreu antes ou depois de o servidor aceitar a solicitação. Confiabilidade não é o mesmo que reenviar tudo.

Quando o Seedream 5.0 Pro é a escolha certa

O Seedream 5.0 Pro é adequado para cenas lifestyle de produtos, layouts de campanha, criativos localizados e variações de catálogo guiadas por imagens de referência. Seus níveis fixos por imagem também tornam o orçamento de geração bruta simples de calcular.

Ele não substitui fotografia de produto ou pós-produção quando a saída precisa preservar geometria idêntica em pixels, embalagem legalmente exata ou texto de rótulo regulamentado. Também não é adequado para um sistema que publica toda imagem gerada sem revisão, ou para um requisito de que uma solicitação retorne muitos arquivos de produto separados.

Use-o para produção criativa escalável. Mantenha uma pessoa entre a geração e a publicação.

Comece com uma fatia representativa do catálogo

Escolha um pequeno conjunto de SKUs que cubra formatos, materiais, rótulos e acessórios diferentes. Bloqueie o esquema CSV e a versão do prompt. Meça tentativas, motivos de rejeição e custo por imagem aprovada. Expanda somente depois que o fluxo de retomada e revisão funcionar.

Revise os parâmetros e preços atuais na página da API Seedream 5.0 Pro, então execute um produto antes de financiar um lote de catálogo completo. Para referências de produto em vez de geração apenas de texto, abra a API de Edição de Imagem Seedream 5.0 Pro.

Dê vida às suas ideias

Transforme um simples prompt ou referência em imagens e vídeos de IA refinados em segundos—sem necessidade de configuração.

Comece a criar
Dê vida às suas ideias
Modelos relacionados
Todos os modelos
Bytedance
10% OFF
OpenAI
20% OFF
Claude
10% OFF
OpenAI
20% OFF

Perguntas frequentes

Como criar várias imagens de produtos com a API Seedream 5.0 Pro?

Crie uma requisição por SKU. Leia as variáveis do produto e as URLs de referência de um CSV, execute um pequeno número de requisições em simultâneo, use nomes de arquivo vinculados aos SKUs e registre cada resultado em um manifesto. O script acima implementa esse padrão.

O Seedream 5.0 Pro pode gerar várias imagens de uma só vez?

Não como uma única requisição que retorna um catálogo de imagens separadas. O modelo Pro atual não expõe texto para várias imagens. Você ainda pode gerar várias imagens no Seedream 5 Pro executando requisições independentes em simultâneo por meio de uma fila controlada.

Quantas imagens de referência posso usar com o Seedream 5.0 Pro no GPTProto?

O endpoint atual de edição de imagem aceita até 10 referências. A primeira está incluída no preço base, e cada referência adicional acrescenta $0.0027. Atribua a cada referência um papel explícito no prompt.

Quanto custa um lote da API Seedream 5.0 Pro?

Ao preço verificado em 17 de setembro de 2026, uma imagem 1K custa $0.0405 e uma imagem 2K custa $0.081. Adicione $0.0027 para cada referência após a primeira e multiplique pelo número médio de tentativas necessárias para um resultado aprovado. Confira a página do modelo ao vivo antes de orçar uma execução de produção.

O Seedream 5.0 Pro é adequado para um fluxo de trabalho empresarial ou de equipe?

Sim, quando a geração é combinada com mapeamento estável de SKUs, versões de prompt, um manifesto retomável, chaves de API protegidas, revisores nomeados e estados de aprovação. A API cria ativos em rascunho. Seu fluxo de trabalho decide quais ativos podem ser publicados.

Devo gerar imagens de produtos em 1K ou 2K?

Use 1K para testar o prompt compartilhado em SKUs representativos. Quando o prompt estiver estável, use a resolução exigida pelo posicionamento final. Trate cada requisição 2K como uma nova geração e revise-a novamente em vez de presumir que ela corresponderá à composição 1K.

Artigos relacionados

Mais blogs
5 Melhores APIs de Vídeo com IA Acessíveis em 2026: Preços, E-commerce e Drama Curto

5 Melhores APIs de Vídeo com IA Acessíveis em 2026: Preços, E-commerce e Drama Curto

Vidu Q3 Turbo é a melhor API de vídeo com IA acessível para a maioria dos desenvolvedores em 2026. Um clipe de cinco segundos em 720p custa cerca de $0.24, enquanto 1080p custa $0.056 por segundo gerado. Seedance 2.0 Mini é melhor para rascunhos baratos, Hailuo 2.3 Standard para clipes de ação fixos de seis segundos, Kling 3.0 Standard para diálogos, e Wan 3.0 para histórias mais longas com várias cenas. Essas escolhas vencedoras mudam quando você adiciona resolução, duração mínima do clipe, áudio e tentativas malsucedidas. Esta comparação vai além do menor preço anunciado para estimar quanto cada API custa para a tomada que você realmente pode usar. Nota sobre preços: Os preços da GPTProto neste guia foram verificados em 15 de setembro de 2026. As tarifas das APIs de vídeo e as configurações disponíveis podem mudar, então confirme a página do modelo em tempo real antes de orçar uma execução de produção. Experimente o Vidu Q3 Turbo

Tiffany Layne | 2026-09-16

As 6 melhores APIs de LLM acessíveis para agentes de IA em 2026

As 6 melhores APIs de LLM acessíveis para agentes de IA em 2026

Uma API de LLM acessível para um agente de IA não é necessariamente o modelo com o preço mais baixo por token de entrada. Um agente pode escolher uma ferramenta, construir argumentos, ler o resultado, revisar seu plano e chamar outra ferramenta antes de produzir uma resposta útil. Um modelo barato que faz chamadas inválidas ou precisa de várias tentativas pode, por isso, custar mais do que um modelo um pouco mais caro que conclui a tarefa de uma só vez. Este guia compara seis modelos prontos para agentes disponíveis através da GPTProto. A classificação considera o preço da API, o uso de ferramentas, evidências independentes de desempenho, velocidade, limites de contexto e o risco prático de pagar por ciclos desnecessários do agente. Trata-se de uma comparação de benchmarks públicos e de preços — não de uma alegação de que realizamos um teste privado frente a frente. Uma Chave para a Sua Equipe Resposta rápida: GLM-5.3 Flash é a opção padrão mais forte para a maioria dos agentes sensíveis a custos. DeepSeek Flash é a alternativa de pesos abertos mais rápida, enquanto GPT-5.6 Luna é uma opção promissora para trabalho leve e de alto volume, assim que o preço de sua rota em tempo real for confirmado. MiniMax M3 é adequado para sessões longas com documentos, Gemini 3.8 Flash lidera em velocidade multimodal, e Grok 4.6 é melhor tratado como um modelo de escalonamento para tarefas mais difíceis.

Michael Johnson | 2026-09-15

Como fazer papel de parede animado com IA usando Midjourney e Seedance 2.5

Como fazer papel de parede animado com IA usando Midjourney e Seedance 2.5

Você pode seguir o mesmo processo sem escrever código: crie ou escolha uma imagem, peça a um modelo de chat com capacidade para imagens um prompt de movimento, anime a imagem e baixe o resultado. A etapa final depende do seu dispositivo. Windows e Android podem usar aplicativos de papel de parede em vídeo; o iPhone precisa de uma Live Photo compatível para sua Tela de Bloqueio animada.

Tiffany Layne | 2026-09-09

Gerar várias imagens de uma vez no ChatGPT

Gerar várias imagens de uma vez no ChatGPT

Resumo Dominar como gerar várias imagens de uma vez no ChatGPT envolve uma combinação de prompts estruturados na interface de chat e o uso de parâmetros ou loops específicos via API da OpenAI. Embora a interface padrão seja configurada para saídas únicas, você pode contornar esse gargalo com layouts em grade e comandos em lote. A maioria dos usuários tem dificuldades porque a interface web foi projetada para simplicidade conversacional, não para produção em alto volume. Ao entender a mecânica subjacente do DALL-E 3, você pode começar a tratar a ferramenta como uma fábrica criativa em vez de um simples chatbot. Este guia explora a transição de solicitações manuais isoladas para fluxos de trabalho em lote automatizados ou estruturados, garantindo que você nunca precise esperar uma única imagem ser renderizada antes de começar o próximo conceito criativo.

Schuyler Stacy | 2026-08-31