Seedance 2.0 API: Complete Integration Guide (2026)

Feb 11, 2026

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.

CapabilityEndpointMethod
Text-to-Video/v2/generate/textPOST
Image-to-Video/v2/generate/imagePOST
Status Check/v2/tasks/{task_id}GET
Result Download/v2/tasks/{task_id}/resultGET
Webhook Config/v2/webhooksPOST
Account Info/v2/accountGET

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/account

Security 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:

ParameterTypeRequiredDefaultDescription
modelstringYesModel version (seedance-2.0)
promptstringYesVideo description (30-500 chars)
negative_promptstringNoElements to exclude
aspect_ratiostringNo16:916:9, 9:16, or 1:1
durationintegerNo44 or 8 seconds
seedintegerNorandomReproducibility seed
webhook_urlstringNoURL 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_id

For 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:

ParameterTypeRequiredDescription
modelstringYesseedance-2.0
imagefile/URLYesSource image (JPEG/PNG, max 10MB)
motion_promptstringYesDescription of desired motion
durationintegerNo4 or 8 seconds
seedintegerNoReproducibility seed
webhook_urlstringNoCompletion 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:

StatusDescription
pendingRequest received, queued for processing
processingGeneration in progress
completedVideo ready for download
failedGeneration 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 StatusError CodeDescriptionAction
400INVALID_PARAMSBad request parametersFix request body
401UNAUTHORIZEDInvalid or expired API keyCheck/rotate key
403FORBIDDENPlan does not allow this actionUpgrade plan
429RATE_LIMITEDToo many concurrent requestsWait and retry
500INTERNAL_ERRORServer-side failureRetry with backoff
503SERVICE_UNAVAILABLETemporary maintenanceRetry 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.

PlanConcurrent RequestsRequests/MinuteDaily Limit
Free2105 generations
Pro1060100 generations
Business50+CustomUnlimited

Rate limit headers: Every API response includes rate limit information:

X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1707654321

Monitor 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 / Polling

Recommended 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.

AIVidPipeline

AIVidPipeline