前回は Seedance 2 API の「送信 → 状態確認 → 保存」をまとめました。今回はその手前、プロンプトづくりを自動化します。
やることはシンプルです。
- 商品の説明文を、無料のテキストモデル
deepseek-v4.1-flash-freeに渡して、動画プロンプトを3案つくる - 3案を
seedance-2-mini(480p・8秒)で下書きする - 一番良い1本だけ、あとで
seedance-2で作り直す
テキストも動画も同じ API キーで呼べるので、1ファイルで完結します。
コストの目安
| 工程 | モデル | 金額 |
|---|---|---|
| プロンプト3案 | deepseek-v4.1-flash-free |
\$0 |
| 下書き3本 |
seedance-2-mini・480p・8秒 |
\$0.22 × 3 = \$0.66 |
料金は 2026年11月2日時点の beatapi.io の表示価格です。無料モデルの提供内容は変わることがあるので、使う前に料金ページで確認し、本番の処理には有料モデルの代わりも用意しておくと安心です。
前提
- Node.js 18 以上(
fetchが標準で使えます) - 環境変数
BEATAPI_API_KEY - テキストは OpenAI 互換のエンドポイント
https://api.beatapi.io/v1/chat/completionsを使います
1. 無料モデルでプロンプトを3案つくる
うまくいく動画プロンプトには型があります。被写体、動き(動詞は1つ)、カメラの動き(1つ)、光(1つ)、変えてはいけないもの。これをシステムプロンプトで縛ります。
const base = "https://api.beatapi.io/v1";
const headers = {
Authorization: `Bearer ${process.env.BEATAPI_API_KEY}`,
"Content-Type": "application/json",
};
async function makePrompts(product: string): Promise<string[]> {
const res = await fetch(`${base}/chat/completions`, {
method: "POST",
headers,
body: JSON.stringify({
model: "deepseek-v4.1-flash-free",
messages: [
{
role: "system",
content:
"You write 8-second product video prompts. Each prompt: subject and look, ONE action, ONE camera move, ONE light source, and what must NOT change (logo, color). English, under 60 words, no text overlay. Return only a JSON array of 3 strings.",
},
{ role: "user", content: product },
],
}),
});
if (!res.ok) throw new Error(`chat failed: ${res.status}`);
const json = await res.json();
const text: string = json.choices[0].message.content;
// モデルが前後に文章を付けても拾えるように、最初の [ から最後の ] までを読む
const arr = JSON.parse(text.slice(text.indexOf("["), text.lastIndexOf("]") + 1));
if (!Array.isArray(arr) || arr.length === 0) throw new Error("no prompts");
return arr.slice(0, 3);
}
2. Seedance 2 Mini で3本送る
1案ごとに Idempotency-Key を分けます。通信が切れて再送するときは、同じキー・同じ本文で送ります。
async function submitDraft(prompt: string, key: string): Promise<string> {
const res = await fetch(`${base}/videos/tasks`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": key },
body: JSON.stringify({
model: "seedance-2-mini",
prompt,
duration: 8,
aspect_ratio: "16:9",
resolution: "480p",
}),
});
if (!res.ok) throw new Error(`submit failed: ${res.status}`);
const { data } = await res.json();
return data.id; // まず保存する
}
3. 終わるまで待って、URL を並べる
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
async function waitForTask(id: string, timeoutMs = 12 * 60_000) {
// 待つ上限(ローカルの期限)。超えてもタスクの失敗ではない
const deadline = Date.now() + timeoutMs;
let delay = 5_000;
while (Date.now() < deadline) {
const res = await fetch(`${base}/tasks/${id}`, { headers });
if (res.status === 429) {
// Retry-After を秒で読む。読めなければ 10 秒待つ
const ra = Number(res.headers.get("retry-after"));
await sleep((Number.isFinite(ra) && ra > 0 ? ra : 10) * 1_000);
continue;
}
if (res.ok) {
const { data } = await res.json();
if (data.status === "succeeded" || data.status === "failed") return data;
} else if (res.status < 500) {
throw new Error(`task query failed: ${res.status}`);
}
// 一時的なエラーは少し待ってから再確認する
await sleep(delay + Math.floor(Math.random() * 1_000));
delay = Math.min(Math.round(delay * 1.35), 15_000);
}
// 待つのをやめるだけ。タスクIDは残して、あとで照合する
throw new Error(`client deadline reached; keep the task id: ${id}`);
}
async function main() {
const product =
"Amber perfume bottle with a gold cap, label 'NOIR'. Premium, warm, for an online store hero video.";
const prompts = await makePrompts(product);
const ids: string[] = [];
for (const [i, p] of prompts.entries()) {
ids.push(await submitDraft(p, `noir-draft-${i}-v1`));
}
const results = await Promise.all(ids.map(waitForTask));
results.forEach((t, i) => {
const url = t.output?.media?.find((m: any) => m.type === "video")?.url;
console.log(`#${i + 1} ${t.status}\n prompt: ${prompts[i]}\n video: ${url ?? t.error_code}`);
});
}
main();
実行すると、3案のプロンプトと、それぞれの下書き動画の URL が並びます。見比べて1本を選び、同じプロンプトで model を seedance-2、resolution を 720p に変えて送れば仕上げです(720p・8秒で \$0.99)。
ハマりどころ
- JSON で返ってこない:テキストモデルが説明文を付けることがあります。上のコードのように配列部分だけを切り出すか、失敗したらもう一度だけ聞き直します。
- 同じ動画が2本できる:タイムアウト後に新しいキーで送り直すと起きます。キーは「1案につき1つ」で固定します。
-
generate_audioを付けてエラー:generate_audioは Fast と Standard のパラメータです。Mini には付けず、必要ならseedance-2-fastを使います。 -
無料モデルが使えない日:提供内容が変わることがあります。
modelを有料のテキストモデルに差し替えられるようにしておきます。
まとめ
プロンプトは無料モデル、下書きは一番安い動画モデル、仕上げだけ良いモデル。1商品の下書き3本で \$0.66 です。商品が10個なら、まず \$6.60 で方向を決められます。
出典・確認日
- テキスト:OpenAI 互換
POST /v1/chat/completions(BeatAPI 公開 API ドキュメント) - 無料モデル:beatapi.io のモデル一覧で
deepseek-v4.1-flash-freeが無料であることを 2026年11月2日に確認 - 動画料金:
seedance-2-mini480p・8秒 \$0.22、seedance-2720p・8秒 \$0.99(2026年11月2日)
モデルの仕様と料金:https://beatapi.io/seedance-2-api
