The Seedance 2.0 API provides programmatic access to ByteDance's AI video generation model. This guide covers everything developers need to integrate Seedance into applications and production workflows, from authentication to async polling to webhook-driven architectures.
API Overview
The Seedance 2.0 REST API exposes endpoints for text-to-video generation, image-to-video generation, status polling, and result retrieval. All generation is asynchronous: you submit a request, receive a task ID, and poll or wait for a webhook to get the result.
| Capability | Endpoint | Method |
|---|---|---|
| Text-to-Video | /v2/generate/text | POST |
| Image-to-Video | /v2/generate/image | POST |
| Status Check | /v2/tasks/{task_id} | GET |
| Result Download | /v2/tasks/{task_id}/result | GET |
| Webhook Config | /v2/webhooks | POST |
| Account Info | /v2/account | GET |
Base URL: https://api.seedance.ai
Response format: All endpoints return JSON with a consistent structure including status, data, and error fields.
Quick Start (5 Minutes)
Get from zero to your first generated video in three steps.
Step 1: Get your API key. Create an account at the Dreamina developer portal and navigate to Settings > API Keys to generate a key.
Step 2: Submit a generation request.
curl -X POST https://api.seedance.ai/v2/generate/text \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"prompt": "A golden retriever running through a field of sunflowers at sunset, slow motion, cinematic lighting",
"aspect_ratio": "16:9",
"duration": 4
}'Step 3: Poll for the result.
curl https://api.seedance.ai/v2/tasks/TASK_ID \
-H "Authorization: Bearer YOUR_API_KEY"When status returns "completed", the response includes a video_url field with a signed download link valid for 24 hours.
Authentication
All API requests require a Bearer token in the Authorization header. Your API key is tied to your account and billing plan.
JavaScript
const SEEDANCE_API_KEY = process.env.SEEDANCE_API_KEY
const headers = {
'Authorization': `Bearer ${SEEDANCE_API_KEY}`,
'Content-Type': 'application/json',
}Python
import os
import requests
SEEDANCE_API_KEY = os.environ["SEEDANCE_API_KEY"]
headers = {
"Authorization": f"Bearer {SEEDANCE_API_KEY}",
"Content-Type": "application/json",
}cURL
curl -H "Authorization: Bearer $SEEDANCE_API_KEY" \
-H "Content-Type: application/json" \
https://api.seedance.ai/v2/accountSecurity best practices:
- Store API keys in environment variables, never in source code
- Rotate keys regularly through the developer dashboard
- Use separate keys for development and production environments
- Monitor usage through the account dashboard to detect unauthorized use
Text-to-Video Endpoint
Generate videos from text descriptions. This is the primary endpoint for most use cases.
Request parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
model | string | Yes | — | Model version (seedance-2.0) |
prompt | string | Yes | — | Video description (30-500 chars) |
negative_prompt | string | No | — | Elements to exclude |
aspect_ratio | string | No | 16:9 | 16:9, 9:16, or 1:1 |
duration | integer | No | 4 | 4 or 8 seconds |
seed | integer | No | random | Reproducibility seed |
webhook_url | string | No | — | URL for completion notification |
JavaScript Example
async function generateTextToVideo(prompt) {
const response = await fetch('https://api.seedance.ai/v2/generate/text', {
method: 'POST',
headers,
body: JSON.stringify({
model: 'seedance-2.0',
prompt,
aspect_ratio: '16:9',
duration: 4,
}),
})
const data = await response.json()
if (!data.data?.task_id) {
throw new Error(`Generation failed: ${data.error?.message ?? 'Unknown error'}`)
}
return data.data.task_id
}Python Example
def generate_text_to_video(prompt: str) -> str:
response = requests.post(
"https://api.seedance.ai/v2/generate/text",
headers=headers,
json={
"model": "seedance-2.0",
"prompt": prompt,
"aspect_ratio": "16:9",
"duration": 4,
},
)
response.raise_for_status()
data = response.json()
task_id = data.get("data", {}).get("task_id")
if not task_id:
raise ValueError(f"Generation failed: {data.get('error', {}).get('message', 'Unknown')}")
return task_idFor writing effective prompts, see our Seedance prompt guide with 50+ ready-to-use templates.
Image-to-Video Endpoint
Animate static images with motion prompts. The image provides the visual starting point, and the motion prompt describes how the scene should move.
Request parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | seedance-2.0 |
image | file/URL | Yes | Source image (JPEG/PNG, max 10MB) |
motion_prompt | string | Yes | Description of desired motion |
duration | integer | No | 4 or 8 seconds |
seed | integer | No | Reproducibility seed |
webhook_url | string | No | Completion notification URL |
JavaScript Example (File Upload)
async function generateImageToVideo(imagePath, motionPrompt) {
const imageBuffer = await fs.promises.readFile(imagePath)
const blob = new Blob([imageBuffer], { type: 'image/png' })
const formData = new FormData()
formData.append('model', 'seedance-2.0')
formData.append('image', blob, 'input.png')
formData.append('motion_prompt', motionPrompt)
formData.append('duration', '4')
const response = await fetch('https://api.seedance.ai/v2/generate/image', {
method: 'POST',
headers: { 'Authorization': `Bearer ${SEEDANCE_API_KEY}` },
body: formData,
})
const data = await response.json()
return data.data.task_id
}JavaScript Example (URL Reference)
async function generateImageToVideoFromUrl(imageUrl, motionPrompt) {
const response = await fetch('https://api.seedance.ai/v2/generate/image', {
method: 'POST',
headers,
body: JSON.stringify({
model: 'seedance-2.0',
image_url: imageUrl,
motion_prompt: motionPrompt,
duration: 4,
}),
})
const data = await response.json()
return data.data.task_id
}Python Example
def generate_image_to_video(image_path: str, motion_prompt: str) -> str:
with open(image_path, "rb") as f:
files = {"image": ("input.png", f, "image/png")}
data = {
"model": "seedance-2.0",
"motion_prompt": motion_prompt,
"duration": "4",
}
response = requests.post(
"https://api.seedance.ai/v2/generate/image",
headers={"Authorization": f"Bearer {SEEDANCE_API_KEY}"},
files=files,
data=data,
)
response.raise_for_status()
return response.json()["data"]["task_id"]Async Generation and Status Polling
All Seedance API generation is asynchronous. After submitting a request, you receive a task_id and must poll for completion. Typical generation time is 30-120 seconds depending on duration and server load.
Task status values:
| Status | Description |
|---|---|
pending | Request received, queued for processing |
processing | Generation in progress |
completed | Video ready for download |
failed | Generation failed (see error field) |
JavaScript Polling Implementation
async function pollForResult(taskId, maxAttempts = 60, intervalMs = 3000) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const response = await fetch(
`https://api.seedance.ai/v2/tasks/${taskId}`,
{ headers }
)
const data = await response.json()
const status = data.data?.status
if (status === 'completed') {
return data.data.video_url
}
if (status === 'failed') {
throw new Error(`Generation failed: ${data.data?.error ?? 'Unknown error'}`)
}
await new Promise((resolve) => setTimeout(resolve, intervalMs))
}
throw new Error('Generation timed out after maximum polling attempts')
}Python Polling Implementation
import time
def poll_for_result(task_id: str, max_attempts: int = 60, interval: float = 3.0) -> str:
for _ in range(max_attempts):
response = requests.get(
f"https://api.seedance.ai/v2/tasks/{task_id}",
headers=headers,
)
response.raise_for_status()
data = response.json()["data"]
if data["status"] == "completed":
return data["video_url"]
if data["status"] == "failed":
raise RuntimeError(f"Generation failed: {data.get('error', 'Unknown')}")
time.sleep(interval)
raise TimeoutError("Generation timed out")Polling best practices:
- Start with 3-second intervals for the first 30 seconds
- Increase to 5-second intervals after 30 seconds
- Set a maximum timeout of 3-5 minutes
- Implement exponential backoff for rate-limited responses
Webhook Integration
Webhooks eliminate the need for polling by pushing a notification to your server when generation completes. This is the recommended approach for production systems.
Webhook Setup
// Register a webhook endpoint
async function registerWebhook(url) {
const response = await fetch('https://api.seedance.ai/v2/webhooks', {
method: 'POST',
headers,
body: JSON.stringify({
url,
events: ['generation.completed', 'generation.failed'],
}),
})
return response.json()
}Webhook Payload
When generation completes, Seedance sends a POST request to your webhook URL:
{
"event": "generation.completed",
"task_id": "task_abc123",
"status": "completed",
"video_url": "https://cdn.seedance.ai/results/...",
"duration": 4,
"created_at": "2026-02-11T10:30:00Z",
"completed_at": "2026-02-11T10:31:15Z"
}Express.js Webhook Handler
app.post('/webhooks/seedance', express.json(), (req, res) => {
const { event, task_id, video_url, status } = req.body
if (event === 'generation.completed') {
// Process the completed video
processCompletedVideo(task_id, video_url)
}
if (event === 'generation.failed') {
// Handle failure
handleGenerationFailure(task_id)
}
res.status(200).json({ received: true })
})Webhook security:
- Verify the webhook signature header to confirm requests are from Seedance
- Use HTTPS endpoints only
- Respond with 200 status within 5 seconds to avoid retries
- Implement idempotent processing to handle duplicate deliveries
Error Handling
The Seedance API uses standard HTTP status codes with structured error responses. Proper error handling is critical for production reliability.
Error response format:
{
"status": "error",
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 30 seconds.",
"retry_after": 30
}
}Common error codes:
| HTTP Status | Error Code | Description | Action |
|---|---|---|---|
| 400 | INVALID_PARAMS | Bad request parameters | Fix request body |
| 401 | UNAUTHORIZED | Invalid or expired API key | Check/rotate key |
| 403 | FORBIDDEN | Plan does not allow this action | Upgrade plan |
| 429 | RATE_LIMITED | Too many concurrent requests | Wait and retry |
| 500 | INTERNAL_ERROR | Server-side failure | Retry with backoff |
| 503 | SERVICE_UNAVAILABLE | Temporary maintenance | Retry after delay |
JavaScript Error Handling with Retry
async function apiRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options)
if (response.ok) {
return response.json()
}
if (response.status === 429) {
const data = await response.json()
const retryAfter = data.error?.retry_after ?? 30
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000))
continue
}
if (response.status >= 500) {
const backoffMs = Math.pow(2, attempt) * 1000
await new Promise((resolve) => setTimeout(resolve, backoffMs))
continue
}
const errorData = await response.json()
throw new Error(`API error ${response.status}: ${errorData.error?.message ?? 'Unknown'}`)
}
throw new Error('Maximum retries exceeded')
}Python Error Handling with Retry
def api_request_with_retry(method: str, url: str, max_retries: int = 3, **kwargs) -> dict:
for attempt in range(max_retries):
response = requests.request(method, url, headers=headers, **kwargs)
if response.ok:
return response.json()
if response.status_code == 429:
retry_after = response.json().get("error", {}).get("retry_after", 30)
time.sleep(retry_after)
continue
if response.status_code >= 500:
time.sleep(2 ** attempt)
continue
response.raise_for_status()
raise RuntimeError("Maximum retries exceeded")Rate Limits and Quotas
Rate limits vary by plan and apply to concurrent requests, not total daily volume.
| Plan | Concurrent Requests | Requests/Minute | Daily Limit |
|---|---|---|---|
| Free | 2 | 10 | 5 generations |
| Pro | 10 | 60 | 100 generations |
| Business | 50+ | Custom | Unlimited |
Rate limit headers: Every API response includes rate limit information:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1707654321Monitor these headers to proactively manage your request rate and avoid hitting limits during peak usage.
Building Production Workflows
For production systems generating video at scale, implement queue-based processing to manage concurrency, handle failures gracefully, and optimize throughput.
Batch Processing Pattern
async function processBatch(prompts) {
const concurrencyLimit = 5
const results = []
for (let i = 0; i < prompts.length; i += concurrencyLimit) {
const batch = prompts.slice(i, i + concurrencyLimit)
const batchResults = await Promise.allSettled(
batch.map(async (prompt) => {
const taskId = await generateTextToVideo(prompt)
const videoUrl = await pollForResult(taskId)
return { prompt, videoUrl }
})
)
const settled = batchResults.map((result) =>
result.status === 'fulfilled'
? result.value
: { error: result.reason.message }
)
results.push(...settled)
}
return results
}Queue-Based Architecture
For high-volume applications, use a message queue to decouple submission from processing:
Client → API Gateway → Message Queue → Worker Pool → Seedance API
↓
Status Store → Webhook / PollingRecommended queue services: Bull (Redis), AWS SQS, Google Cloud Tasks, or RabbitMQ.
Cost Monitoring
Track API spend programmatically by monitoring the account endpoint:
async function checkUsage() {
const response = await fetch('https://api.seedance.ai/v2/account', {
headers,
})
const data = await response.json()
return {
plan: data.data.plan,
usedToday: data.data.generations_today,
limitToday: data.data.generations_limit,
billingCycleSpend: data.data.current_spend,
}
}For detailed pricing information including volume discounts, see our Seedance pricing guide.
FAQ
How do I get a Seedance API key?
Create an account at the Seedance developer portal (Dreamina platform), navigate to your dashboard, and generate an API key. Free tier API access is available.
What programming languages does the Seedance API support?
The Seedance API is a REST API that works with any language that can make HTTP requests. Official examples are provided in JavaScript, Python, and cURL.
How long does Seedance API generation take?
Generation typically takes 30-120 seconds depending on resolution and duration settings. The API uses async polling to check generation status.
What is the Seedance API rate limit?
Rate limits depend on your plan. Free tier allows approximately 2 concurrent requests, Pro allows up to 10, and Business plans have custom limits.
Does the Seedance API support webhooks?
Yes. You can configure a webhook URL to receive notifications when video generation completes, avoiding the need for status polling.
Can I use the Seedance API for commercial projects?
Yes. Pro and Business plan API usage includes commercial licensing. Check the terms of service for specific usage rights.
Related Articles
- Seedance 2.0 Tutorial — Complete beginner guide to Seedance
- Seedance Prompt Guide — 50+ prompt templates for better API results
- Seedance Pricing — API pricing tiers and volume discounts
- Seedance vs Sora 2026 — API comparison with OpenAI Sora

