Seedance 2.0 API 사용법: Python으로 텍스트 동영상과 이미지 동영상 만들기
스크립트 하나가 작업을 만들고, 상태를 확인하고, MP4를 저장합니다. 필드와 제한, 그리고 2026년 10월 30일에 만든 5초 영상 2개를 담았습니다.
이 페이지 목차
Seedance 2.0은 OmniaKey 미디어 API에서 모델 ID dreamina-seedance-2-0-260128로 동작합니다. https://api.omniakey.com/v1/media/tasks에 작업을 POST하고, 작업 ID를 폴링한 뒤 MP4를 내려받습니다. 2026년 10월 30일에 만든 5초 720p 영상 2개는 텍스트가 159초, 정지 이미지가 139초 걸렸습니다.
먼저 결론부터
- 모델 ID는
dreamina-seedance-2-0-260128이고 작업은text_to_video,image_to_video,reference_to_video세 가지입니다(OmniaKey 비디오 API 문서, 2026년 10월 30일 확인). - 생성, 폴링, 다운로드 순서입니다. 생성 요청은 202와 작업 ID를 돌려주고, 이후 몇 초마다
GET /v1/media/tasks/{id}로 상태가succeeded가 될 때까지 확인합니다(OmniaKey 미디어 API 개요, 2026년 10월 30일 확인). - 길이는 4~15 정수 초이고, 2.0 전체 ID는 480P, 720P, 1080P를 출력합니다.
- 요청 본문은 인라인 base64를 포함해 전체 1 MiB 미만입니다.
- 5초 720p 영상 2개는 텍스트가 159초, 첫 프레임이 139초에 끝났습니다.
첫 호출 전에 무엇이 필요한가요?
OmniaKey API 키, Python 3, 정확한 모델 ID입니다. 스크립트는 표준 라이브러리만 불러오므로 가상 환경도 패키지 설치도 필요 없습니다.
첫 호출 전에 키를 OMNIAKEY_API_KEY로 내보냅니다. 스크립트는 한 번만 읽고 모든 요청에 Bearer 토큰으로 보냅니다. 키는 파일에도 git 기록에도 남지 않습니다.
생성은 POST https://api.omniakey.com/v1/media/tasks, 폴링은 GET https://api.omniakey.com/v1/media/tasks/{id}입니다. 여기에 POST /v1/media/tasks/{id}/cancel을 더한 세 개가 미디어 API의 전부입니다(미디어 API 개요, 2026년 10월 30일 확인).
모델 ID는 줄이지 말고 그대로 씁니다. OmniaKey는 seedance-2, seedance-2-5 같은 짧은 별칭을 제공하지 않으며 503 no_available_channel이 돌아옵니다(비디오 API 문서, 2026년 10월 30일 확인).
동영상 작업은 생성되는 초 단위로 미리 청구됩니다. 작업이 failed나 canceled로 끝나면 그 금액은 자동으로 돌아옵니다(미디어 API 개요, 2026년 10월 30일 확인). 초당 현재 요금은 Seedance 2.0 모델 페이지에서 확인하세요.
Seedance 2.0 요청에는 어떤 필드가 필요한가요?
최상위 필드 네 개가 작업을 담고, parameters 객체가 출력 설정을 담습니다. 합리적인 기본값이 없는 것은 duration 하나뿐이라 매번 보냅니다.
model: 전체 모델 ID (짧은 별칭은 실패)type:video(미디어 종류)operation:text_to_video,image_to_video,reference_to_video(접두사 없는 이름)input.prompt: 문자열 하나 (장면, 동작, 카메라. 최대 12,000자)input.media[]:{"type": "first_frame", "url": …}(첫 프레임 또는reference_image)
parameters 안에는 다음이 있습니다.
duration: 4~15 정수 초 (요금을 결정)resolution:480P,720P,1080P(Fast와 Mini는 720P까지)ratio:16:9,9:16,1:1,4:3,3:4,21:9,adaptive(adaptive는 화면을 따름)generate_audio: 기본true,false도 가능 (false면 무음 MP4)n:1(1만 가능)
두 목록은 OmniaKey 비디오 API 문서와 미디어 API 개요(2026년 10월 30일 확인)를 따르며, 프롬프트 길이 제한은 모델 페이지에서 가져왔습니다.
작업 이름은 접두사 없이 그대로 보냅니다. 미디어 종류 접두사는 OmniaKey가 붙입니다(비디오 API 문서, 2026년 10월 30일 확인). 알 수 없는 파라미터는 그대로 제공자에게 전달됩니다(미디어 API 개요, 2026년 10월 30일 확인).
벤더 문서부터 시작하면 함정이 하나 있습니다. BytePlus 자체 생성 작업 API는 필드를 content[]와 최상위에 두기 때문에, BytePlus 튜토리얼 코드는 /v1/media/tasks에서 그대로 동작하지 않습니다(BytePlus 생성 작업 API, 2026년 10월 30일 확인).
Python 스크립트
파일 하나로 두 작업을 모두 실행하고, 차이는 만드는 본문뿐입니다. seedance_video.py로 저장하고 python3 seedance_video.py로 실행합니다.
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 = "dreamina-seedance-2-0-260128"
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(180): # 5초마다 확인하고 최대 15분
time.sleep(5)
state = poll(f"{API}/{task}")
if state["status"] not in ("queued", "processing"):
break
else:
raise TimeoutError(f"{task} is still {state['status']}: poll it again later, do not resubmit")
if state["status"] != "succeeded":
raise RuntimeError(state.get("error") or state["status"])
asset_url = state["assets"][0]["url"] # 서명 링크는 수명이 짧으니 바로 내려받기
req = urllib.request.Request(asset_url, headers={"User-Agent": "omniakey-example/1"})
with urllib.request.urlopen(req, timeout=300) as resp, open(out_path, "wb") as f:
f.write(resp.read())
return task
def text_to_video(prompt, out_path, duration=5, resolution="720P", ratio="16:9"):
body = {
"model": MODEL,
"type": "video",
"operation": "text_to_video",
"input": {"prompt": prompt},
"parameters": {"duration": duration, "resolution": resolution, "ratio": ratio},
}
return run_task(body, out_path)
def image_to_video(prompt, image_path, out_path, duration=5, resolution="720P", audio=False):
with open(image_path, "rb") as f:
raw = f.read()
if len(raw) > 700_000: # base64로 3분의 1이 늘어나므로 본문은 1 MiB 미만이어야 함
raise ValueError("first frame too large: save it as a JPEG about 1024 px wide")
frame = "data:image/jpeg;base64," + base64.b64encode(raw).decode()
body = {
"model": MODEL,
"type": "video",
"operation": "image_to_video",
"input": {"prompt": prompt, "media": [{"type": "first_frame", "url": frame}]},
"parameters": {"duration": duration, "resolution": resolution, "ratio": "adaptive",
"generate_audio": audio},
}
return run_task(body, out_path)
if __name__ == "__main__":
print("done", text_to_video(
"Close-up of a tall glass on a wooden bar counter. Sparkling water is poured over ice; bubbles "
"rise and the ice cubes clink and shift. Warm evening light from the left. The camera slowly "
"pushes in. Sound of pouring, fizzing and clinking ice. No people, no text.",
"sparkling-glass.mp4",
))
print("done", image_to_video(
"The cat slowly wakes, stretches its front paws and yawns, then looks toward the window. "
"Raindrops keep running down the glass. The camera stays still.",
"cat.jpg",
"cat-wakes.mp4",
))
코드를 고치기 전에 이 파일에서 중요한 여섯 가지를 확인하세요.
- 생성 요청마다 무작위
Idempotency-Key가 붙습니다. 같은 키와 같은 본문으로 다시 보내면 원래 작업이 HTTP 200으로 돌아오고 두 번 청구되지 않습니다(미디어 API 개요, 2026년 10월 30일 확인). - 루프는 폴링 사이에 5초 쉬고 180번에서 멈춥니다. 15분입니다.
- 타임아웃은 작업이 아직 렌더링 중이라는 뜻입니다. 같은 작업 ID를 나중에 다시 폴링하고, 절대 다시 제출하지 마세요. 두 번째 영상 요금이 나갑니다.
assets[0].url은 약 1시간 유지되는 서명 링크입니다. 그래서 스크립트는 작업이 성공하자마자 내려받습니다(미디어 API 개요, 2026년 10월 30일 확인).progress.percent는 작업이 도는 동안 계속 0일 수 있어서 루프는status만 봅니다(비디오 API 문서, 2026년 10월 30일 확인).- 이미지 함수는 700,000바이트를 넘는 파일을 거부합니다. base64로 3분의 1이 늘고 본문은 1 MiB 미만이어야 하기 때문입니다.
요청 모양은 같은 API의 Kling 3.0과 같습니다. 모델을 바꾸면 모델 ID와 파라미터가 바뀌고 주변 코드는 그대로입니다.
이미지에서 동영상으로 만들 때 첫 프레임은 어떻게 보내나요?
그림을 input.media[]에 넣고 "type": "first_frame"을 지정해 data:image/jpeg;base64,... URI로 인라인 전송합니다. 고양이 영상이 이 방식이었습니다.
input.media[]는 공개 HTTPS URL과 인라인 data URI를 모두 받고, 본문 전체는 1 MiB까지입니다(비디오 API 문서, 2026년 10월 30일 확인). OmniaKey 자체 테스트에서는 일반 HTTPS 프레임 링크가 provider_submit_rejected로 거부되어 예제는 data URI를 보냅니다(비디오 API 문서, 2026년 10월 30일 확인).
base64로 3분의 1이 늘어나므로 폭 1024픽셀 정도의 JPEG에서 시작하세요. 이번 첫 프레임은 134,356바이트였고, 4 MB PNG는 거부됩니다.
여기서는 parameters.ratio를 adaptive로 둡니다. 보낸 그림의 모양을 따르기 때문에 1024×571 정지 이미지가 1280×720 영상이 되었습니다. reference_to_video에서는 같은 배열에 "type": "reference_image"를 넣습니다(비디오 API 문서, 2026년 10월 30일 확인).
직접 만든 영상 2개
두 작업 모두 2026년 10월 30일에 위 파일을 그대로 써서 보냈습니다. 첫 번째는 텍스트 동영상이고 작업은 task_uF2QUtbeAAUW11lXEaLxNw1ZnBUoqvNk, 01:39:43 UTC에 만들어져 01:42:22 UTC에 성공했습니다. 프롬프트가 붓는 소리를 요구해서 오디오는 켜 둔 상태입니다.
Close-up of a tall glass on a wooden bar counter. Sparkling water is poured over ice; bubbles rise and the ice cubes clink and shift. Warm evening light from the left. The camera slowly pushes in. Sound of pouring, fizzing and clinking ice. No people, no text.

프레임에는 나무 바 카운터 위 얼음이 든 유리잔이 보이고, 뒤쪽으로 병과 따뜻한 조명이 흐릿합니다. 잔은 첫 프레임에서 마지막 프레임으로 커지며 프롬프트가 요청한 푸시인을 만들고, 마지막에는 거품이 바닥에 깔린 가득 찬 잔으로 끝납니다.
두 번째는 이미지 동영상이고 작업은 task_u85W0jn1sQLkrszJlxqXF1hxJYhsndxK, 01:42:33 UTC에 만들어져 01:44:52 UTC에 성공했습니다. 첫 프레임은 비 내리는 창가에서 낡은 책 더미 위에 잠든 치즈태비 고양이 정지 이미지(1024×571픽셀)이고, "type": "first_frame"을 붙인 data URI로 보냈습니다.
The cat slowly wakes, stretches its front paws and yawns, then looks toward the window. Raindrops keep running down the glass. The camera stays still.

고양이는 머리를 들어 눈을 뜨고, 앞발 두 개를 책 가장자리 밖으로 뻗어 기지개를 켜고, 크게 하품한 뒤 창 쪽으로 머리를 돌립니다. 순서는 프롬프트와 같습니다. 비는 계속 유리창을 타고 흐르고 카메라는 움직이지 않습니다.

이 두 번의 실행은 텍스트 동영상과 720P 5초 첫 프레임을 다룹니다. 1080P, Fast, Mini, 2.5, reference_to_video, HTTPS 프레임 링크, 다른 길이는 시험하지 않았습니다.
파일 실측값
두 다운로드 모두 H.264 MP4이고 1280×720, 24 fps, 121프레임, 5.04초입니다.
| 영상 | 유리잔 | 고양이 |
|---|---|---|
| 작업 | 텍스트에서 동영상 | 이미지에서 동영상 |
| 완료 시간 | 159초 | 139초 |
| 파일 크기 | 2,070,248바이트 | 1,352,027바이트 |
| 화면 | 1280×720, 24 fps, 121프레임 | 1280×720, 24 fps, 121프레임 |
| 오디오 | AAC 트랙 | 없음 |
유리잔 영상에는 붓는 소리, 거품 소리, 얼음 부딪히는 소리가 들어 있고 목소리와 음악은 없습니다. 고양이 영상에는 오디오 트랙이 아예 없습니다. 요청에 generate_audio: false를 보냈기 때문입니다.
두 작업 기록 모두 usage.completion_tokens: 108900을 보고했습니다. 121 × 1280 × 720 ÷ 1024이고, BytePlus가 동영상 요금을 추정할 때 쓰는 토큰 공식입니다(BytePlus 요금 페이지, 2026년 10월 30일 확인).
OmniaKey 문서는 6초 720p 영상이 보통 1분 안에 끝난다고 합니다(비디오 API 문서, 2026년 10월 30일 확인). 이번 두 개는 159초와 139초가 걸렸고, 10월 26일 비교의 5초 720p 영상 3개는 144.8초, 319.0초, 184.7초였습니다. 2~5분을 예상하고 요청을 열어 두지 말고 폴링하세요.
어떤 오류와 제한을 만나나요?
먼저 세 가지를 확인하세요. 모델 ID, 4~15초 길이, 그리고 오디오입니다. 제공자 필터가 오디오를 거부할 수 있습니다.
| 상태 / 코드 | 바꿀 것 |
|---|---|
400 invalid_request | 본문 또는 input.prompt |
400 model_price_error | 길이를 4~15초로 |
403 model_not_allowed | 키의 모델 권한 |
409 idempotency_key_reused | 본문마다 새 키 |
503 no_available_channel | 전체 ID, 접두사 없는 작업 이름 |
| 실패한 작업, 제공자 오류 | error를 읽고 재시도 |
이 코드들은 미디어 API 개요와 비디오 API 문서에서 왔습니다(2026년 10월 30일 확인). failed로 끝난 작업은 미리 낸 요금이 돌아오므로 거부된 렌더링은 시간만 잃습니다.
오디오 설정을 보세요. Seedance는 기본으로 오디오 트랙을 만들고, 켜 둔 상태에서 제공자가 4초 480P 요청을 OutputAudioSensitiveContentDetected.PolicyViolation로 거부했습니다. 장면에 소리가 필요 없다면 parameters.generate_audio: false를 보내세요(비디오 API 문서, 2026년 10월 30일 확인).
BytePlus에 따르면 2.0 시리즈는 실제 사람 얼굴이 들어간 참조 이미지나 참조 동영상을 받지 않습니다(BytePlus Seedance 2.0 페이지, 2026년 10월 30일 확인). Seedance 2.0에는 seed도 camera_fixed도 없지만, 둘 다 벤더 생성 작업 API에는 있는 필드입니다(BytePlus 생성 작업 API, 2026년 10월 30일 확인).
15초가 이 시리즈의 상한이고 duration: -1로도 더 길게 만들 수 없습니다. 20초나 30초 영상은 Seedance 2.5가 맡습니다(Seedance 2.0 모델 페이지, 2026년 10월 30일 확인).
OmniaKey는 고정된 속도 제한을 공개하지 않습니다. HTTP 429는 재시도 가능으로 보고 간격을 늘려 가며 다시 시도하세요(OmniaKey API 오류 페이지, 2026년 10월 30일 확인).
Seedance 2.0, Fast, Mini, 2.5 중 무엇을 고르나요?
네 등급은 같은 본문을 받으므로 전환 비용은 모델 ID 하나이고, Fast와 Mini는 해상도 상한이 낮습니다.
- Seedance 2.0,
dreamina-seedance-2-0-260128: 480P, 720P, 1080P, 4~15초. BytePlus 정가는 5초 영상 기준 720p가 $0.76, 480p가 $0.35입니다(BytePlus 요금 페이지, 2026년 10월 30일 확인). - Seedance 2.0 Fast,
dreamina-seedance-2-0-fast-260128: 480P, 720P, 4~15초. 5초 영상은 720p가 $0.60, 480p가 $0.28입니다. - Seedance 2.0 Mini,
dreamina-seedance-2-0-mini-260615: 480P, 720P, 4~15초. 5초 영상은 720p가 $0.38, 480p가 $0.18입니다. - Seedance 2.5,
dreamina-seedance-2-5-260628: 20초나 30초 영상을 만들 수 있는 이 시리즈의 유일한 ID이며 가장 느려서 4초 480P 영상에 약 4분이 걸립니다(비디오 API 문서, 2026년 10월 30일 확인). 화면 품질은 Seedance 2.5 리뷰에서 다룹니다.
OmniaKey 모델 페이지는 4초 480P 영상을 2.0에서 약 1분, Fast에서 약 2분, Mini에서 100초로 안내합니다(2026년 10월 30일 확인).
1080P 원본이 필요하면 2.0 전체 ID를 고르세요. Fast와 Mini는 720P까지입니다(비디오 API 문서, 2026년 10월 30일 확인). 이 ID들의 요금은 OmniaKey가 직접 정하므로 현재 요금은 Seedance 2.0 모델 페이지에서 확인하세요.
자주 묻는 질문
Seedance 2.0 공식 API가 있나요?
있습니다. BytePlus ModelArk가 판매하고, 활성화하려면 USD 30을 넘는 잔액이나 Savings Plan, 또는 Seedance 리소스 팩이 필요합니다(BytePlus Seedance 2.0 페이지, 2026년 10월 30일 확인). 위 스크립트는 같은 모델 ID를 OmniaKey 미디어 API로 호출합니다.
base64 대신 이미지 URL을 보낼 수 있나요?
OmniaKey 문서는 인라인 data URI와 함께 공개 HTTPS URL도 적어 두었습니다(비디오 API 문서, 2026년 10월 30일 확인). OmniaKey 테스트에서 HTTPS 프레임 링크가 provider_submit_rejected로 돌아왔기 때문에 이번 실행은 인라인을 썼습니다. 테스트 영상으로 URL을 먼저 시험해 보세요.
소리를 끄려면 어떻게 하나요?
parameters.generate_audio: false를 보내면 MP4에 오디오 트랙이 전혀 없습니다. 고양이 영상이 그렇습니다. 이 모델은 기본이 오디오 켜짐입니다.
요청이 왜 타임아웃되나요?
영상이 보통 아직 렌더링 중입니다. 5초 720p 작업은 이번에 139~319초 걸렸습니다. 같은 작업 ID를 다시 폴링하고 다시 제출하지 마세요. 15초 영상은 15분 루프 상한을 올리세요.
영상 링크는 얼마나 유지되나요?
assets[0].url의 서명 링크는 약 1시간 유지되고, 작업 기록 자체는 생성 후 24시간 보관됩니다(미디어 API 개요, 2026년 10월 30일 확인). 처음 성공할 때 내려받거나 작업을 다시 폴링해 새 링크를 받으세요.
확인한 자료
- BytePlus ModelArk: Seedance 2.0 가이드, 2026년 10월 30일 확인
- BytePlus ModelArk: 동영상 생성 작업 API, 2026년 10월 30일 확인
- BytePlus ModelArk: 모델 요금과 토큰 공식, 2026년 10월 30일 확인
OmniaKey 자체 문서(2026년 10월 30일 확인): 비디오 API, 미디어 API 개요, API 오류.
이 글에서 다루는 모델
관련 글
Seedance 2.5 리뷰
의미 있는 업그레이드는 주변에 떠돌고 있는 검증되지 않은 4K 헤드라인이 아니라 30 2차 스토리텔링과 생산 관리입니다.
HappyHorse 1.1 vs Seedance 2.0: 같은 프롬프트로 비교한 결과와 가격
같은 프롬프트 3개를 같은 날 돌렸습니다. 결과물은 Seedance 2.0, 속도는 HappyHorse 1.1이 두 배였습니다.
Kling 3.0 API: 텍스트 투 비디오 요청 코드와 실측 클립
텍스트 투 비디오 작업은 model에 kling-3.0을 넣는 POST 한 번으로 시작합니다. 요청 코드와 필수 필드, 2026년 10월 29일에 만들어 측정한 5초 클립 2개를 담았습니다.
