Seedance Studio API
Generate video, images and music from your application
The API uses the same models, account credits, validation and task records as the studio. Create an API key in Settings → API Keys. A new key is displayed once; keep it on your server.
Base URL: https://seedance-2.app/api/v1
Send Authorization: Bearer YOUR_API_KEY on every API request. Do not embed the key in browser code, URLs, logs or public repositories.
Quick start
First list models. Use the returned public id, not a provider's internal model identifier.
curl https://seedance-2.app/api/v1/models \
-H "Authorization: Bearer $SEEDANCE_API_KEY"Then request a quote. This validates the requested settings and public reference files but does not create a generation or reserve credits.
curl https://seedance-2.app/api/v1/quote \
-H "Authorization: Bearer $SEEDANCE_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"presetId":"studio-seedance25-text-to-video","prompt":"A paper boat moves across a quiet lake at sunrise","options":{"duration":5,"quality":"720p","generate_audio":true}}'Use the returned data.costCredits as the maximum authorized cost when creating the generation. Quotes are in site credits, not dollars. A changed price is rejected before a reservation is made.
const base = 'https://seedance-2.app/api/v1';
const headers = {
Authorization: `Bearer ${process.env.SEEDANCE_API_KEY}`,
'Content-Type': 'application/json',
};
const input = {
presetId: 'studio-seedance25-text-to-video',
prompt: 'A paper boat moves across a quiet lake at sunrise',
options: { duration: 5, quality: '720p', generate_audio: true },
};
async function post(path, body, extra = {}) {
const response = await fetch(base + path, {
method: 'POST', headers: { ...headers, ...extra }, body: JSON.stringify(body),
});
const result = await response.json();
if (!response.ok || result.code !== 0) throw new Error(result.message || result.error?.message || 'Request failed');
return result.data;
}
const quote = await post('/quote', input);
const idempotencyKey = crypto.randomUUID(); // Persist this before submitting.
// Respect Retry-After if requests arrive too quickly.
await new Promise(resolve => setTimeout(resolve, 1500));
const task = await post('/generations', { ...input, maxCostCredits: quote.costCredits }, {
'Idempotency-Key': idempotencyKey,
});
console.log(task.id, task.status);An accepted task is not a completed output. Poll /generations/{id} until its status is success, failed or canceled. Use at least a 15-second polling interval. See the generation guide for safe retries and refunds.