createTask를 호출했더니 이미지 대신 taskId가 돌아왔습니다. 이제 몇 분 동안 연결을 열어 두거나, 시간 초과 후 두 번 결제하거나, 프로세스가 재시작되면서 작업을 잃어버리지 않고 결과를 받아야 합니다.
핵심은 다음과 같습니다.
- 스크립트에서 빠른 이미지 모델을 사용하는 경우:
?wait=30을createTask에 추가하면 폴링 자체가 필요 없는 경우가 많습니다. - 스크립트, 로컬 개발, 공개 서버가 없는 경우: 아래 예시처럼 횟수가 제한된 반복문으로 작업 상태를 조회합니다.
- 사용자가 기다리는 작업, 영상 또는 많은 작업량:
callBackUrl을 사용하고 서명을 검증한 뒤, 수신을 빠르게 확인하고 큐에서 처리합니다. - 운영 환경: 둘 다 사용합니다. 콜백으로 빠르게 결과를 받고, 주기적인 상태 점검으로 도착하지 않은 콜백을 보완합니다.
API를 직접 호출하면 ?wait=<seconds>(최대 60)가 작업이 끝날 때까지 createTask 연결을 유지합니다. 몇 초 안에 완료되는 이미지 모델에 적합하며 영상에는 적합하지 않습니다. callBackUrl(B는 대문자)은 결과를 받는 공개 HTTPS 주소입니다. recordInfo는 상태 조회 호출이며 SDK의 get_task가 이를 감싸고 있습니다. 작업 참조 문서에서 세 가지를 모두 설명합니다.
먼저 Playground에서 테스트하기
seedream-5-0-pro Playground를 엽니다. 짧은 Prompt를 입력하고 Resolution을 1k로 설정한 다음 Run model을 누르고 Output에서 결과를 확인합니다. History에는 이전 실행이 표시됩니다. 여기서 보이는 대기 과정을 애플리케이션에서 자동화하는 것이 폴링과 Webhook입니다. 두 번째 이미지를 생성하는 것은 아닙니다.
완료 결과를 받을 방법 선택하기
| 애플리케이션 | 먼저 사용할 방법 | 이유 |
|---|---|---|
| 로컬 스크립트 또는 간헐적 배치 | SDK를 통한 폴링 | 공개 수신 서버를 배포할 필요가 없습니다 |
| 대화형 이미지 또는 영상 애플리케이션 | 검증된 Webhook | 작업마다 대기 프로세스를 유지하지 않고 생성이 끝나면 대응합니다 |
| 운영 서비스 | Webhook과 정기적인 대조 점검 | 콜백을 놓쳐도 접수된 작업이 방치되지 않습니다 |
폴링을 선택할 때: 작업 수가 적거나 컴퓨터에서 공개 HTTPS 호출을 받을 수 없는 경우입니다. Webhook을 선택할 때: 사용자가 대화형 애플리케이션에서 기다리고 있거나 긴 영상 작업이 워커를 계속 점유하는 경우입니다.
이미지를 입력하는 작업에서는 SDK의 업로드 흐름이 /files/{fileId}/commit을 실행하고 spicy:// URI를 반환합니다. 미디어 문서를 참조하세요.
SDK로 작업 하나 실행하기
spicyapi를 설치하고 SPICY_API_KEY를 설정한 뒤 예시를 실행합니다. 중단 후 같은 작업을 재개할 수 있도록 map-image.json을 보관하세요. 의도적으로 다른 이미지를 만들 때는 새 실행 이름을 사용합니다. Python SDK는 API 메서드를 제공하고, 작업 참조 문서는 전체 수명 주기를 설명합니다.
import json
import os
import time
import uuid
from pathlib import Path
from spicyapi import SpicyClient, output_assets
client = SpicyClient()
def run(model, inputs, name):
path = Path(name + ".json")
request = {"model": model, "input": inputs}
saved = json.loads(path.read_text()) if path.exists() else {"request": request, "key": str(uuid.uuid4())}
if saved["request"] != request:
raise ValueError("Changed input needs a new run name; preserve the old attempt.")
def save():
temporary = path.with_suffix(".tmp")
temporary.write_text(json.dumps(saved))
temporary.chmod(0o600)
temporary.replace(path)
if not saved.get("taskId"):
if time.time() - saved.get("sentAt", time.time()) >= 23 * 3600:
raise RuntimeError("Reconcile the saved attempt before submitting again.")
if "quote" not in saved:
saved["quote"] = client.quote_task(model=model, input_data=inputs)
if input(f"Maximum ${saved['quote']['maxCharge']}. Create? [y/N] ") != "y":
raise SystemExit("Stopped without creating this task.")
saved.setdefault("sentAt", time.time())
save()
quote = saved["quote"]
task = client.create_task(model=model, input_data=inputs,
idempotency_key=saved["key"], quote_id=quote["quoteId"], expected_cost=quote["estimatedCost"])
saved["taskId"] = task["taskId"]
save()
print("Task:", saved["taskId"])
for _ in range(360):
task = client.get_task(saved["taskId"])
if task["state"] not in {"queued", "running"} and task["state"] != "succeeded":
raise RuntimeError(str(task.get("errorMessage", task["state"])))
if task["state"] == "succeeded":
assets = output_assets(task)
if assets and assets[0].get("unavailable"):
raise RuntimeError("Output unavailable; keep the task ID for reconciliation.")
if assets and assets[0].get("url") and not assets[0].get("pending"):
return assets[0]
time.sleep(5)
raise TimeoutError("Rerun the same saved attempt to resume; no new task is needed.")
asset = run("bytedance/seedream-5.0-pro/text-to-image", {
"prompt": "a folded paper map on a wooden table, overhead", "resolution": "1k", "output_format": "png"
}, "map-image")
print(asset["url"])응답을 올바르게 읽기
작업은 항상 queued, running, succeeded, failed, expired 중 하나의 상태입니다. 처음 두 상태만 진행 중이므로 succeeded만 기다리지 말고, 상태가 queued도 running도 아닐 때 반복문을 종료하세요. 그렇지 않으면 실패한 작업을 끝없이 조회하게 됩니다(이전 기록에는 읽기 전용 상태인 canceled가 표시될 수도 있습니다). 결과 링크는 output.assets[0].url에 있습니다. API를 직접 호출할 때는 응답의 최상위가 아니라 응답을 감싼 구조의 data 객체에서 작업을 읽습니다. 접수된 작업은 취소할 수 없으며 succeeded만 과금됩니다. failed와 expired는 일반적으로 보류된 금액을 해제합니다.
시간 초과가 애플리케이션에 의미하는 것
HTTP 202는 작업이 접수되었다는 뜻이며 생성 완료를 뜻하지 않습니다. 로컬 시간 초과는 대기를 멈추지만 접수된 작업을 취소하지 않습니다. 작업 ID를 보관하고 다른 생성에 비용을 지불하는 대신 해당 작업을 재개하세요. 작업이 반환한 기한은 복구할 수 있는 범위를 정하며 렌더링 시간의 예측값이 아닙니다.
- 제출 응답이 유실된 경우: 복구 시 원래 작업을 찾을 수 있도록 요청과 키를 보관합니다.
- 생성에 실패한 경우: 모델 오류를 표시하고 사용자가 의도적으로 다음 시도를 선택하게 합니다.
- 생성은 성공했지만 파일이 준비 중인 경우: 같은 작업을 계속 기다립니다. 이미지나 영상은 아직 전달 준비 중입니다.
새 요청에는 새 멱등성 키를 사용합니다. 같은 요청을 복구할 때는 키와 입력을 바꾸지 않습니다. 멱등성 참조 문서는 24시간의 재실행 허용 기간을 설명합니다.
Webhook으로 빠르게 결과 받기
완료 이벤트를 애플리케이션 백엔드로 보낸 뒤, 평소 사용하는 알림 경로로 사용자 인터페이스를 업데이트합니다. 콜백은 사용자 브라우저로 보내는 직접 메시지가 아닙니다.
수신 측에서는 네 가지를 수행해야 합니다.
- 공식 SDK와 원본 요청 본문으로 이벤트를 검증합니다.
- 해당 작업이 애플리케이션에서 관리하는 작업인지 확인합니다.
- 수신 확인을 보내기 전에 이벤트를 영구 저장하고 중복을 제거합니다.
- HTTP 응답과 별도로 출력 복사와 애플리케이션 업데이트를 큐에 넣습니다.
선택할 때: 이미 백엔드를 운영하며 완료 알림을 빠르게 받고 싶은 경우입니다. 다른 방법을 고려할 때: 공개 수신 서버가 없는 짧은 로컬 스크립트라면 폴링이 더 간단합니다.
작업 완료와 미디어 전달은 별도로 확인하세요. 형식이 올바른 이벤트도 실패한 작업을 알릴 수 있으며, 성공한 작업도 애플리케이션에서 준비 완료로 표시하기 전에 파일 처리가 필요할 수 있습니다. Webhook 문서에 검증과 전달 규약이 있습니다.
정기적인 대조 점검 유지하기
접수된 작업 ID를 모두 저장하세요. 애플리케이션에서 아직 해결하지 못한 작업을 주기적으로 다시 확인합니다. 특히 배포, 네트워크 장애, 서명 비밀값 교체 후에 필요합니다. 복구한 결과를 Webhook과 같은 멱등 완료 처리기로 보내면 갤러리에 같은 파일이 두 번 추가되지 않습니다.
정산이 끝날 때까지 미해결 과금 내역을 기록에 유지하세요. 로컬 기록이 유실되면 현재 키의 작업 이력에서 ID를 복구할 수 있습니다. 운영 환경 통합에서 이 방법을 설명합니다.
만료 전에 결과 복사하기
준비된 출력 링크는 임시 링크입니다. User-Agent 요청 헤더를 넣고 반환된 형식과 일치하는 파일 이름으로, 직접 관리하는 저장소에 파일을 복사하세요. 다운로드 실패는 기존 결과를 복구할 이유이지 다시 생성할 이유가 아닙니다.
기한은 미디어 보관과 전달, 복사 흐름은 영구 저장소를 참조하세요.
선택의 결론
작은 스크립트라면 SDK 폴링으로 시작하세요. 대화형 서비스를 운영하게 되면 검증된 Webhook을 추가하고 복구를 위한 정기 점검을 유지합니다. 저장한 작업 ID가 세 경로를 연결합니다. 새 작업으로 대체하는 대신 한 번의 생성 결과를 가져옵니다.
동시에 더 많은 작업을 처리하려면 상한이 있는 배치 전송을 추가하세요. 첫 이미지·영상 조합에는 시작 가이드를 사용합니다.
Agent에게 호출 맡기기
입력을 준비하고 결과를 가져오는 일을 맡기고, 가격과 창작상의 선택은 직접 승인하고 싶다면 Agent를 사용하세요.
- CLI:
{"prompt": "a folded paper map on a wooden table, overhead", "resolution": "1k", "output_format": "png"}를input.json으로 저장하고npx @spicyapi/cli tasks create --model bytedance/seedream-5.0-pro/text-to-image --input-file input.json --wait를 사용합니다. CLI에서 가격 승인을 받습니다. 반환된 작업 ID는 복구를 위해 보관하세요. - MCP: 어시스턴트에게
spicyapi_task_create를bytedance/seedream-5.0-pro/text-to-image에 사용한 뒤spicyapi_task_wait를 사용하도록 요청합니다. MCP server는 생성 전에 가격 확인을 요청합니다. - Skill:
npx skills add spicyapi-ai/spicy-skill로 설치한 뒤$spicyapi에게 같은 흐름을 실행하고 승인을 받기 위해 멈추도록 요청합니다. Skill은 설정된 CLI 또는 MCP 연결을 사용합니다.







