Home / API Documentation

API Documentation

Generate AI videos programmatically with Seedance 2.0. REST API with async job-based workflow, webhook callbacks, and full multimodal support.

Base URL: https://genray.ai Auth: Bearer Token Format: JSON

Quick Start

Generate your first AI video in 3 steps:

1

Get your API key

Go to Account Settings → API Keys → Create Key. Requires Standard ($39/mo) or Advanced ($80/mo) plan.

2

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"
  }'
3

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

EndpointDescription
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

ParameterTypeDescription
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

FieldTypeDescription
person_image_urlstringPublic URL (https://...) or /v1/files/... path pointing to the person's portrait. Required.
promptstringAction / scene description. Do not describe the person's appearance — identity is extracted from the photo. Max 4000 chars.
durationintVideo length in seconds, 4–15. Default 5.
aspect_ratiostring"9:16", "16:9", "1:1" etc. Default "9:16".
modelstring"seedance-2.0" (pro) or "seedance-2.0-fast". Default pro.
clothing_detailsstringOptional. Extra outfit details (patches, insignia, nameplates) to preserve on the character.
extra_reference_imagesstring[]Optional. Up to 8 additional references for locations or objects in the scene.
webhook_urlstringOptional. 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 credits for a 5-second video at seedance-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_url must 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

StatusDescription
queuedJob is waiting in queue
processingVideo is being generated
completedVideo is ready. video_url field available.
failedGeneration 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

CodeErrorDescription
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

PlanAPI KeysRequests/minConcurrent 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

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