bytedance/seedance-2.0-mini/video-extend19크레딧부터 / 5초 영상· 영상 이어 만들기Seedance 2.0 Mini로 클립을 이어 붙입니다. 영상의 마지막 프레임에서 4초에서 15초 길이의 새 구간이 시작되고(소리 포함), 원본과 이어진 부분이 합쳐진 파일 하나를 480p 또는 720p로 받습니다.
POST /jobs/createTask에서 모델 이름을 bytedance/seedance-2.0-mini/video-extend(으)로 지정하면 호출할 수 있습니다. 19크레딧/ 5초 영상부터(US$0.19). 새 계정에는 30크레딧이 지급되니 Playground에서 먼저 무료로 써 보세요.
- 동영상 만들기
- 이미지로 영상 만들기
- 레퍼런스로 영상 만들기
- 영상 이어 만들기
- 최대 720p
- 4–15초
- 사운드 포함
- 시작·끝 프레임
- 참조 이미지 최대 9장
- 화면비 6종
bytedance/seedance-2.0-mini/video-extendPOST https://api.popnow.ai/api/v1/jobs/createTask# 1. Create the task. Keep the Idempotency-Key if you retry after a network error.
export IDEMPOTENCY_KEY="$(uuidgen)"
curl -X POST https://api.popnow.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"model": "bytedance/seedance-2.0-mini/video-extend",
"input": {
"video_url": "https://media.popnow.ai/effects/flower-bloom-1abd6efb.mp4",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}'
# 2. Copy data.taskId from the response, then poll until state is succeeded, failed, canceled or expired.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"// Node 18+ (or any runtime with fetch). Keep the API key on your server.
const BASE = "https://api.popnow.ai/api/v1";
const headers = { Authorization: `Bearer ${process.env.POPNOW_API_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(path, init = {}) {
const res = await fetch(BASE + path, { ...init, headers: { ...headers, ...init.headers } });
const body = await res.json();
if (body.code !== 200) throw new Error(`${body.code} ${body.msg}`);
return body.data;
}
const { taskId } = await call("/jobs/createTask", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
"model": "bytedance/seedance-2.0-mini/video-extend",
"input": {
"video_url": "https://media.popnow.ai/effects/flower-bloom-1abd6efb.mp4",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}),
});
// Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
let task;
for (let delay = 2000; ; delay = Math.min(delay * 1.5, 15000)) {
await sleep(delay);
task = await call(`/jobs/recordInfo?taskId=${taskId}`);
const copying = (task.output?.assets ?? []).some((a) => a.pending && !a.unavailable);
if (["succeeded", "failed", "canceled", "expired"].includes(task.state) && !copying) break;
}
if (task.state !== "succeeded") throw new Error(`${task.state}: ${task.errorMessage ?? "no detail"}`);
// Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for (const asset of task.output.assets) console.log(asset.url);import os, time, uuid, requests
BASE = "https://api.popnow.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['POPNOW_API_KEY']}"}
def call(method, path, **kwargs):
body = requests.request(method, BASE + path, headers={**HEADERS, **kwargs.pop("headers", {})}, timeout=30, **kwargs).json()
if body["code"] != 200:
raise RuntimeError(f'{body["code"]} {body["msg"]}')
return body["data"]
task_id = call(
"POST",
"/jobs/createTask",
headers={"Idempotency-Key": str(uuid.uuid4())}, # reuse it if you retry
json={
"model": "bytedance/seedance-2.0-mini/video-extend",
"input": {
"video_url": "https://media.popnow.ai/effects/flower-bloom-1abd6efb.mp4",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": True
}
},
)["taskId"]
# Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
delay = 2.0
while True:
time.sleep(delay)
task = call("GET", "/jobs/recordInfo", params={"taskId": task_id})
assets = (task.get("output") or {}).get("assets") or []
copying = any(a.get("pending") and not a.get("unavailable") for a in assets)
if task["state"] in ("succeeded", "failed", "canceled", "expired") and not copying:
break
delay = min(delay * 1.5, 15.0)
if task["state"] != "succeeded":
raise RuntimeError(f'{task["state"]}: {task.get("errorMessage", "no detail")}')
# Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for asset in assets:
print(asset["url"])input은 아래 스키마로 만들었기 때문에 그대로 검증을 통과합니다. 프롬프트와 링크만 내 것으로 바꾸세요.
가격
| 옵션 | 크레딧 /초 | ≈ USD |
|---|---|---|
resolution 480p | 3.69 | US$0.037 |
enable_web_search trueresolution 480p | 5.4 | US$0.054 |
resolution 720p | 7.94 | US$0.079 |
enable_web_search trueresolution 720p | 10.8 | US$0.11 |
1크레딧 = $0.01 · 모든 옵션과 가격: GET /models/bytedance/seedance-2.0-mini/video-extend
작업마다 크레딧은 정수로 올림 처리되므로, 가장 낮은 옵션으로 한 번 실행하면 19크레딧/ 5초 영상(US$0.19)입니다.
제한
- 영상 길이
- 4–15 s
- 해상도
- 480p · 720p
- 입력 클립 최대 길이
- 60 s
파라미터
이 필드들은 모든 요청을 검사할 때 쓰는 것과 같은 스키마에서 나옵니다. 목록에 없는 필드는 거부됩니다.
video_urlstring필수작업할 영상.
promptstring선택Describe what happens next: the action that follows, the camera move and the sound.
last_image_urlstring선택선택 사항인 마지막 프레임. 원하는 장면에서 클립을 끝냅니다.
resolutionstring선택가격이 달라짐출력 크기. 클수록 선명하고 가격도 올라갑니다.
duration_secondsinteger선택가격이 달라짐클립 길이. 초 단위로 요금이 붙습니다.
generate_audioboolean선택가격이 달라짐화면에 맞는 소리(목소리, 주변음, 효과음)를 더합니다.
enable_web_searchboolean선택가격이 달라짐날짜, 장소, 최신 정보를 먼저 웹에서 확인합니다.
seedinteger선택같은 숫자를 쓰면 같은 결과를 다시 얻을 수 있습니다.
bitrate_modestring선택Encoding bitrate of the new segment: standard, or high for crisper detail and a larger file.
watermarkboolean선택Add the model’s visible "AI generated" watermark to the new segment.
원본 JSON 스키마
{
"additionalProperties": false,
"properties": {
"bitrate_mode": {
"default": "standard",
"description": "Encoding bitrate of the new segment: standard, or high for crisper detail and a larger file.",
"enum": [
"standard",
"high"
],
"type": "string"
},
"duration_seconds": {
"default": 5,
"description": "Length of the new segment in seconds. The source clip is added on top and is not billed.",
"enum": [
-1,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15
],
"type": "integer"
},
"enable_web_search": {
"default": false,
"description": "Let the model search the web for information related to the prompt before rendering. Requests with it on are billed at the web-search rate.",
"type": "boolean"
},
"generate_audio": {
"default": true,
"description": "Generate sound for the new segment. The source keeps its own sound either way.",
"type": "boolean"
},
"last_image_url": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"description": "Optional target end frame: the new segment lands on this picture.",
"type": "string"
},
"prompt": {
"description": "Describe what happens next: the action that follows, the camera move and the sound.",
"minLength": 1,
"type": "string"
},
"resolution": {
"default": "720p",
"description": "Resolution of the new segment; match your source clip.",
"enum": [
"480p",
"720p"
],
"type": "string"
},
"seed": {
"description": "Random seed. Omit to let the model pick one.",
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"video_url": {
"contentMediaType": [
"video/mp4",
"video/webm",
"video/quicktime"
],
"description": "The clip to continue: MP4, MOV or WebM, up to 60 seconds. Its final frame becomes the opening frame of the new segment, and the result is your clip with the new segment joined on.",
"format": "uri",
"minLength": 1,
"type": "string"
},
"watermark": {
"default": false,
"description": "Add the model’s visible \"AI generated\" watermark to the new segment.",
"type": "boolean"
}
},
"required": [
"video_url"
],
"type": "object"
}응답, 폴링, 콜백
모든 모델은 같은 방식으로 동작합니다. 작업을 만들고, 폴링하거나 콜백을 기다리세요.
먼저 견적 받기
curl -X POST https://api.popnow.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bytedance/seedance-2.0-mini/video-extend",
"input": {
"video_url": "https://media.popnow.ai/effects/flower-bloom-1abd6efb.mp4",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}'본문은 createTask와 같습니다. 예상 청구액과 최대 청구액을 돌려주며, 아무것도 실행하지 않습니다.
접수됨
HTTP/1.1 202 Accepted
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"state": "queued"
},
"request_id": "req_example_create"
}HTTP 202는 작업이 접수되었고 크레딧이 보류만 되었을 뿐 아직 차감되지 않았다는 뜻입니다. 최종 청구액은 보류액을 넘지 않습니다.
상태 확인
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"간격을 조금씩 늘리며 몇 초마다 확인하고, state가 succeeded, failed, canceled, expired 중 하나가 되면 멈추세요.
완료된 작업
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"model": "bytedance/seedance-2.0-mini/video-extend",
"state": "succeeded",
"output": {
"assets": [
{
"url": "https://cdn.popnow.ai/tasks/task_01k3m8x9q2z4v7n5p6r8s0t1w3/output.mp4?signature=FROM_RESPONSE",
"expiresAt": "2026-10-17T12:20:00Z",
"mime": "video/mp4",
"width": 1280,
"height": 720,
"durationSeconds": 5
}
]
},
"settled": true
},
"request_id": "req_example_record"
}expiresAt 전에 url에서 내려받으세요. 나중에 새 링크가 필요하면 POST /common/download-url을 호출하세요.
콜백 검증하기
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
// express.raw keeps the exact bytes that were signed; express.json would not match.
app.post("/hooks/popnow", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.header("X-Webhook-Timestamp");
const signature = req.header("X-Webhook-Signature") ?? "";
const body = JSON.parse(req.body.toString("utf8"));
const taskId = body.data?.taskId ?? body.task_id;
const digest = createHash("sha256").update(req.body).digest("hex");
const expected = createHmac("sha256", process.env.POPNOW_WEBHOOK_SECRET)
.update(`${taskId}.${timestamp}.${digest}`)
.digest("base64");
const ok = expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
// Check the signature first, then reject anything older than five minutes.
if (!ok || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(401).end();
res.status(200).end(); // answer fast, then fetch the task and store its files
});callBackUrl을 설정하면 완료된 작업을 보내 드립니다. 믿기 전에 원본 요청 본문으로 서명을 확인하세요.
로컬 파일 보내기
curl -X POST https://api.popnow.ai/api/v1/common/upload-url \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contentType": "image/png", "bytes": 482113}'
# PUT the file bytes to data.uploadUrl with every header in data.headers, then commit it:
curl -X POST https://api.popnow.ai/api/v1/files/$FILE_ID/commit \
-H "Authorization: Bearer $POPNOW_API_KEY"
# The commit returns a popnow://f/... reference. Put it in any image, video or
# audio field of the task input instead of a public https URL.공개 https 링크는 그대로 쓸 수 있습니다. 로컬 파일은 먼저 upload-url과 commit을 거칩니다.

