API Documentation
Generate AI videos programmatically with Seedance 2.0. REST API with async job-based workflow, webhook callbacks, and full multimodal support.
Quick Start
Generate your first AI video in 3 steps:
Get your API key
Go to Account Settings → API Keys → Create Key. Requires Standard ($39/mo) or Advanced ($80/mo) plan.
Submit a generation request
# Submit a text-to-video job
curl -X POST https://genray.ai/api/v1/videos \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A golden retriever running through a sunlit meadow, cinematic lighting",
"duration": 5,
"aspect_ratio": "16:9"
}'
Poll for the result
# Poll until status is "completed"
curl https://genray.ai/api/v1/videos/JOB_ID \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Generation takes 30-120 seconds. When status is "completed", the response includes a video_url with a direct download link.
Authentication
All API requests require a Bearer token in the Authorization header:
Authorization: Bearer sk_live_YOUR_API_KEY
Security: API keys are shown only once at creation. Store them securely. If compromised, revoke immediately in Account Settings. Keys are hashed (SHA-256) in our database — we cannot recover lost keys.
API Key Management
| Endpoint | Description |
|---|---|
POST /api/keys |
Create a new API key |
GET /api/keys |
List all your API keys |
DELETE /api/keys/{id} |
Revoke an API key |
Generate Video
POST /api/v1/videos
Create a video generation job. Supports text-to-video, image-to-video (first/last frame), and omni reference (multi-modal inputs).
Request Body
| Parameter | Type | Description |
|---|---|---|
prompt required |
string | Text description of the video. 1-4000 characters. |
model optional |
string | Model to use. "seedance-2.0" (default, highest quality) or "seedance-2.0-fast" (faster, cheaper). |
duration optional |
integer | Video length in seconds. 4-15. Default: 5. |
aspect_ratio optional |
string | Output aspect ratio. Default: "16:9". Options: 16:9, 9:16, 1:1, 4:3, 3:4, 21:9. |
first_frame_url optional |
string | URL to an image for the first frame. Enables image-to-video mode. |
end_frame_url optional |
string | URL to an image for the last frame. Requires first_frame_url. |
reference_images optional |
string[] | Up to 9 image URLs for visual reference (omni mode). |
reference_videos optional |
string[] | Up to 3 video URLs for motion/style reference. |
reference_audios optional |
string[] | Up to 3 audio URLs for audio-synced generation. |
webhook_url optional |
string | HTTPS URL to receive a callback when the job finishes. Overrides key's default webhook. |
Response 202 Accepted
{
"id": "a1b2c3d4-e5f6-...",
"status": "queued",
"model": "seedance-2.0",
"duration": 5,
"aspect_ratio": "16:9",
"estimated_cost": 170,
"credits_remaining": 10630,
"created_at": "2026-04-02T12:00:00"
}
Python Example
import requests
import time
API_KEY = "sk_live_YOUR_KEY"
BASE = "https://genray.ai/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Step 1: Submit generation
resp = requests.post(f"{BASE}/videos", headers=headers, json={
"prompt": "A cat wearing a tiny chef hat, cooking pasta in a sunlit kitchen",
"duration": 10,
"aspect_ratio": "16:9",
"model": "seedance-2.0"
})
job = resp.json()
print(f"Job submitted: {job['id']}")
# Step 2: Poll for result
while True:
status = requests.get(f"{BASE}/videos/{job['id']}", headers=headers).json()
if status["status"] == "completed":
print(f"Video ready: {status['video_url']}")
break
elif status["status"] == "failed":
print(f"Failed: {status['error']}")
break
time.sleep(5)
JavaScript Example
const API_KEY = 'sk_live_YOUR_KEY';
const BASE = 'https://genray.ai/api/v1';
// Submit generation
const job = await fetch(`${BASE}/videos`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: 'A golden retriever running through a sunlit meadow',
duration: 5,
aspect_ratio: '16:9',
}),
}).then(r => r.json());
console.log('Job:', job.id);
// Poll for result
while (true) {
const status = await fetch(`${BASE}/videos/${job.id}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` },
}).then(r => r.json());
if (status.status === 'completed') {
console.log('Video URL:', status.video_url);
break;
}
if (status.status === 'failed') throw new Error(status.error);
await new Promise(r => setTimeout(r, 5000));
}
Video From Person Photo
POST /api/v1/videos/from-person
Generate a photoreal Seedance video from a single person photo. Pass a URL to the portrait and an action prompt — the endpoint handles identity extraction and produces cinematic live-action video of that character performing the described action.
Example
curl -X POST https://genray.ai/api/v1/videos/from-person \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"person_image_url": "https://your-host.com/photo.jpg",
"prompt": "The character turns head slowly, soft daylight, cinematic close-up, subtle smile",
"duration": 5,
"aspect_ratio": "9:16"
}'
Parameters
| Field | Type | Description |
|---|---|---|
person_image_url | string | Public URL (https://...) or /v1/files/... path pointing to the person's portrait. Required. |
prompt | string | Action / scene description. Do not describe the person's appearance — identity is extracted from the photo. Max 4000 chars. |
duration | int | Video length in seconds, 4–15. Default 5. |
aspect_ratio | string | "9:16", "16:9", "1:1" etc. Default "9:16". |
model | string | "seedance-2.0" (pro) or "seedance-2.0-fast". Default pro. |
clothing_details | string | Optional. Extra outfit details (patches, insignia, nameplates) to preserve on the character. |
extra_reference_images | string[] | Optional. Up to 8 additional references for locations or objects in the scene. |
webhook_url | string | Optional. HTTPS URL called with HMAC signature when video finishes. |
Response 202
{
"id": "a1b2c3d4-...",
"status": "queued",
"model": "seedance-2.0",
"duration": 5,
"aspect_ratio": "9:16",
"total_cost": 142,
"credits_remaining": 9708,
"created_at": "2026-04-21T10:23:21"
}
Then poll GET /api/v1/videos/{id} (see Check Status) until "completed".
Pricing & Refunds
- Success:
142 creditsfor a 5-second video atseedance-2.0(scales with duration and model). - Generation fails: video cost is automatically refunded.
- Moderation reject: video cost refunded automatically.
Notes
- Works best on clear frontal portraits with neutral lighting.
- Processing time: ~2–5 minutes end-to-end.
- In the
prompt, describe the action / scene only. Do not describe the person's appearance — identity is extracted from the photo. person_image_urlmust be publicly fetchable (no auth on the image host). Use /api/v1/upload to host user-submitted images.
Upload Reference Image
POST /api/v1/upload
Host a reference image on genray.ai so you can pass its URL to from-person or regular /videos. Max 10 MB. Accepts multipart/form-data or JSON base64.
curl -X POST https://genray.ai/api/v1/upload \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-F "file=@/path/to/photo.jpg"
Response
{
"url": "/v1/files/upload_u42_abc123_xyz.jpg",
"full_url": "https://genray.ai/v1/files/upload_u42_abc123_xyz.jpg",
"size_bytes": 290150
}
Use full_url as person_image_url or reference_images[0] in subsequent video calls.
Check Status
GET /api/v1/videos/{job_id}
Get the current status of a generation job. Poll this endpoint until status is "completed" or "failed".
Status Values
| Status | Description |
|---|---|
queued | Job is waiting in queue |
processing | Video is being generated |
completed | Video is ready. video_url field available. |
failed | Generation failed. error field has details. |
Response (completed)
{
"id": "a1b2c3d4-e5f6-...",
"status": "completed",
"model": "seedance-2.0-pro",
"duration": 5,
"aspect_ratio": "16:9",
"credits_spent": 170,
"video_url": "https://genray.ai/videos/abc123.mp4",
"width": 1280,
"height": 720,
"duration_ms": 5040,
"cover_url": "https://genray.ai/videos/abc123_cover.jpg",
"created_at": "2026-04-02T12:00:00"
}
Upscale Video
POST /api/v1/videos/{video_id}/upscale
Upscale a completed video to 2x resolution (720p → 1440p). Free — 0 credits. The video_id is the numeric generation ID from your account, not the job_id.
curl -X POST https://genray.ai/api/v1/videos/42/upscale \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Account Info
GET /api/v1/account
Get your current plan, credits balance, and API limits.
{
"plan": "standard",
"plan_name": "Standard",
"plan_expires_at": "2026-05-02T00:00:00",
"credits_remaining": 8450,
"credits_per_month": 4850,
"api_rpm": 10,
"api_concurrent": 2,
"max_api_keys": 2
}
Webhooks
Instead of polling, configure a webhook URL to receive a POST request when your video is ready. Set a default webhook per API key, or pass webhook_url per request.
Webhook Payload
// POST to your webhook_url
// Header: X-Genray-Signature: sha256=abc123...
// Header: X-Genray-Event: video.completed
{
"event": "video.completed",
"data": {
"id": "a1b2c3d4-e5f6-...",
"status": "completed",
"video_url": "https://genray.ai/videos/abc123.mp4",
"width": 1280,
"height": 720,
"duration_ms": 5040
},
"timestamp": "2026-04-02T12:05:00"
}
Verifying Signatures
Every webhook includes an HMAC-SHA256 signature in the X-Genray-Signature header. Verify it using your webhook secret (shown once when creating the API key):
import hmac, hashlib
def verify_webhook(payload_body, signature_header, webhook_secret):
expected = hmac.new(
webhook_secret.encode(),
payload_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature_header)
Delivery: Webhooks retry 3 times with exponential backoff (2s, 4s, 8s). Timeout is 5 seconds. Only HTTPS URLs are accepted.
Errors
| Code | Error | Description |
|---|---|---|
401 |
unauthorized |
Missing or invalid API key |
402 |
insufficient_credits |
Not enough credits for the requested generation |
403 |
plan_required |
API access requires Standard or Advanced plan |
404 |
not_found |
Job not found or doesn't belong to your account |
429 |
rate_limit_exceeded |
Too many requests. Check retry_after field. |
500 |
generation_failed |
Internal error. Credits are refunded automatically. |
Error Response Format
{
"detail": {
"error": "insufficient_credits",
"message": "Not enough credits. Need 170, have 50.",
"credits_required": 170,
"credits_available": 50
}
}
Rate Limits
| Plan | API Keys | Requests/min | Concurrent Jobs |
|---|---|---|---|
| Basic ($19/mo) | No API access | — | — |
| Standard ($39/mo) | 2 keys | 10 req/min | 2 jobs |
| Advanced ($80/mo) | 5 keys | 30 req/min | 5 jobs |
Rate limits apply per API key. Status polling (GET /videos/{id}) counts toward the RPM limit. Need higher limits? Contact us.
Credit Costs
| Operation | Cost | Details |
|---|---|---|
| Video (Pro model) | 26 credits/sec | 5s = 130, 10s = 260, 15s = 390 |
| Video (Fast model) | 22 credits/sec | 5s = 110, 10s = 220, 15s = 330 |
| + Video reference (omni) | +18 cr/sec | Extra cost when using a video reference (routed to premium provider) |
| Upscale (2x) | Free | 720p → 1440p |
Standard plan ($39/mo) gives 4,850 credits = ~37 videos at 5s Pro, or ~12 videos at 15s Pro.
Advanced plan ($80/mo) gives 9,900 credits = ~76 videos at 5s Pro, or ~25 videos at 15s Pro.