Video Generation API
The Video Generation API creates asynchronous video jobs. A create request returns HTTP 202 with a job ID; use that ID to poll until the job reaches succeeded or failed, then download the completed MP4.
The examples use https://api.icodeeasy.cc as the base URL. Every endpoint call documented below requires your API key; a signed result-file URL returned after success is the only exception:
Authorization: Bearer <your API key>
Endpoints
| Purpose | Method and route |
|---|---|
| Create a video job | POST /v1/videos/generations |
| Get a job | GET /v1/videos/tasks/{id} |
| Download completed content | GET /v1/videos/tasks/{id}/content |
| Delete a completed or failed job | DELETE /v1/videos/tasks/{id} |
Five-minute quickstart
1. Create a job
curl -X POST https://api.icodeeasy.cc/v1/videos/generations \
-H 'Authorization: Bearer <your API key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "doubao-seedance-2.5",
"prompt": "a futuristic city street in the rain, slow forward camera movement, neon reflections on wet pavement",
"resolution": "480p",
"ratio": "9:16",
"duration": 4,
"generate_audio": true
}'
Creation returns HTTP 202:
{
"id": "vid_1785398400000000000",
"task_id": "vid_1785398400000000000",
"object": "video.generation.job",
"status": "queued",
"model": "doubao-seedance-2.5",
"created_at": "2026-07-30T08:00:00Z",
"updated_at": "2026-07-30T08:00:00Z"
}
Use task_id as the recommended job identifier for polling, downloading, and deletion; id remains available with the same value for compatibility. A 202 response means the job was accepted, not that the video is already complete.
2. Poll the job
Copy the task_id from the create response into TASK_ID:
TASK_ID='vid_1785398400000000000'
curl "https://api.icodeeasy.cc/v1/videos/tasks/${TASK_ID}" \
-H 'Authorization: Bearer <your API key>'
Poll about once every 5 seconds. Do not create another job while the current one is queued or running.
{
"id": "vid_1785398400000000000",
"object": "video.generation.job",
"status": "running",
"model": "doubao-seedance-2.0",
"created_at": "2026-07-30T08:00:00Z",
"updated_at": "2026-07-30T08:00:20Z",
"progress": 42
}
progress is best-effort and may be absent. Treat status, not progress, as authoritative.
3. Download the video
After status becomes succeeded, the response includes url and the final cost_rmb:
{
"id": "vid_1785398400000000000",
"object": "video.generation.job",
"status": "succeeded",
"model": "doubao-seedance-2.0",
"created_at": "2026-07-30T08:00:00Z",
"updated_at": "2026-07-30T08:01:18Z",
"progress": 100,
"url": "/v1/videos/tasks/vid_1785398400000000000/content",
"cost_rmb": 0.528
}
Download through the canonical authenticated content route and follow redirects:
TASK_ID='vid_1785398400000000000'
curl -L "https://api.icodeeasy.cc/v1/videos/tasks/${TASK_ID}/content" \
-H 'Authorization: Bearer <your API key>' \
-o result.mp4
The content route supports HTTP Range and common conditional download headers. Requesting content before success returns 409 video_not_ready.
Job lifecycle and safe retries
| Status | Meaning | Client action |
|---|---|---|
queued | Accepted and waiting to start | Keep polling the same job |
running | The video is being generated | Keep polling the same job |
succeeded | Terminal success; content is ready | Download and store the result |
failed | Terminal failure | Read error.code; create a new job only if you intentionally try again |
Retry rules:
- After receiving HTTP
202, keep polling the returnedtask_id. Repeating the create request may create another job. - Polling and download
GETrequests are safe to retry. - If creation returns
submission_unknown, do not submit another request. The job may already have been accepted; contact support with the request time, model, and prompt summary. - After a confirmed terminal
failedjob, create a new job only when you intentionally want to generate another video.
Webhooks and callback_url are not supported. Poll the job endpoint instead.
Create request reference
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Canonical model ID. There is no default; missing or blank values return 400 invalid_request. |
prompt | string | Text required for standard models | Put all text instructions here. For Motion Control it is optional when both reference assets are supplied. |
content | array | When using references | Reference-media list containing public HTTPS image/video URL items only. |
resolution | string | No | Output tier. The default and allowed values depend on the model. Not accepted by Motion Control. |
ratio | string | No | Output aspect ratio. aspect_ratio is accepted as an alias. Not accepted by Motion Control. |
duration | integer | No | Requested seconds. The default and allowed values depend on the model. Not accepted by Motion Control. |
generate_audio | boolean | No | Generate audio when supported. Defaults are listed in the model table. Not accepted by Motion Control. |
motion_mode | string | Motion Control only | std or pro; defaults to std. |
character_orientation | string | Motion Control only | image or video; defaults to image. Controls the reference-video duration limit. |
keep_original_sound | boolean | Motion Control only | Keep reference-video sound; defaults to true. |
Put all text instructions in prompt. Use content only for reference images and videos.
Reference-media list (content)
content is a list because one job may need a first and last frame, several unordered reference images, or one image plus one video for Motion Control. Put one HTTPS URL string directly in each item's image_url or video_url field.
[
{ "type": "image_url", "image_url": "https://cdn.example.com/first.png", "role": "first_frame" },
{ "type": "image_url", "image_url": "https://cdn.example.com/last.png", "role": "last_frame" }
]
- Image types:
image_urlorinput_image. - Video types:
video_urlorinput_video. - Frame roles:
first_frameandlast_frame. A last frame always requires a first frame. - Motion roles: one
reference_imageand onereference_video. - Grok reference images have no role;
reference_imageis also accepted. - Reference audio is not currently supported.
- Media must use a publicly reachable HTTPS URL that remains valid while the job reads it.
- Put images in
image_urland videos invideo_url.
Complete first-frame and last-frame request
Host both images at publicly readable HTTPS URLs, then put the URLs directly in content:
curl -X POST https://api.icodeeasy.cc/v1/videos/generations \
-H 'Authorization: Bearer <your API key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "doubao-seedance-2.0",
"prompt": "the camera moves smoothly from the opening frame to the ending frame",
"content": [
{"type":"image_url","image_url":"https://cdn.example.com/first.png","role":"first_frame"},
{"type":"image_url","image_url":"https://cdn.example.com/last.png","role":"last_frame"}
],
"resolution": "480p",
"ratio": "9:16",
"duration": 4,
"generate_audio": true
}'
You may send only the first frame. You cannot send a last frame unless the same request also contains a first frame.
Model cookbook
Seedance: text, first frame, and last frame
Seedance 2.5, Seedance 2.0 Standard, Fast, Mini, and Seedance 1.5 Pro support text-to-video and first/last-frame generation. Reference images require explicit frame roles. Seedance 2.5 is listed first in this family.
{
"model": "doubao-seedance-2.5",
"prompt": "the camera circles a glass sculpture as morning light changes",
"content": [
{ "type": "image_url", "image_url": "https://cdn.example.com/seedance-first.png", "role": "first_frame" },
{ "type": "image_url", "image_url": "https://cdn.example.com/seedance-last.png", "role": "last_frame" }
],
"resolution": "720p",
"ratio": "16:9",
"duration": 6,
"generate_audio": true
}
Seedance 2.5 defaults to 480p, 9:16, 4 seconds, and generated audio enabled. With a first frame or first-plus-last frames, its ratio is normalized to adaptive.
MiniMax H3: text, first frame, and last frame
MiniMax-H3 supports text-to-video and first/last-frame generation through the same public request. It accepts 768P or 2K, durations from 4 to 15 seconds, and the 21:9, 16:9, 4:3, 1:1, 3:4, and 9:16 ratios. Its defaults are 768P, 9:16, and 5 seconds. Do not send generate_audio: this public contract has no generated-audio request toggle for H3.
Kling: frame-guided generation
{
"model": "kling-v3",
"prompt": "a train enters the station through light fog",
"content": [
{ "type": "image_url", "image_url": "https://cdn.example.com/kling-first.png", "role": "first_frame" },
{ "type": "image_url", "image_url": "https://cdn.example.com/kling-last.png", "role": "last_frame" }
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5,
"generate_audio": true
}
Important Kling differences:
kling-v2-6: a last frame requires1080p; generated audio is available only at1080pand cannot be combined with a last frame.kling-3.0-turbo: first frame only; no generated audio.kling-v3andkling-v3-omni: first/last frames and generated audio are supported.kling-v3-omnialso accepts up to two roleless reference images, but do not mix roleless and frame-role images in one request.kling-video-o1: first/last frames; no generated audio.
Grok: unordered reference images
Grok accepts up to seven reference-image URLs. Do not assign first_frame or last_frame roles.
{
"model": "grok-imagine-1.5-video",
"prompt": "clouds roll over a canyon while the camera rises",
"content": [
{ "type": "image_url", "image_url": "https://cdn.example.com/grok-reference.png" }
],
"resolution": "480p",
"ratio": "16:9",
"duration": 6
}
Grok defaults to 480p, 9:16, and 6 seconds. Generated audio is not supported.
Kling Motion Control
Provide one public HTTPS image and one public HTTPS MP4 video:
{
"model": "kling-v3-motion-control",
"prompt": "preserve the character identity and follow the reference motion",
"content": [
{ "type": "image_url", "image_url": "https://cdn.example.com/character.png", "role": "reference_image" },
{ "type": "video_url", "video_url": "https://cdn.example.com/motion.mp4", "role": "reference_video" }
],
"motion_mode": "std",
"character_orientation": "image",
"keep_original_sound": true
}
character_orientation: "image": reference video must be 3–10 seconds.character_orientation: "video": reference video must be 3–30 seconds.- Do not send
resolution,ratio,duration, orgenerate_audiowith a Motion Control model.
Model capability table
| Canonical model ID | Resolution / mode | Duration | Ratio | References | Audio default |
|---|---|---|---|---|---|
doubao-seedance-2.5 | 480p, 720p | 4–30 s | 16:9, 4:3, 1:1, 3:4, 9:16, 21:9, adaptive | First + last frame; frame-guided ratio normalizes to adaptive | On |
doubao-seedance-2.0 | 480p, 720p, 1080p, 4K | 4–15 s | 16:9, 9:16, 1:1, 4:3, 3:4, 21:9, adaptive | First + last frame | On |
doubao-seedance-2.0-fast | 480p, 720p | 4–15 s | Same as Seedance 2.0 | First + last frame | On |
doubao-seedance-2.0-mini | 480p, 720p | 4–15 s | Same as Seedance 2.0 | First + last frame | On |
doubao-seedance-1-5-pro | 480p, 720p, 1080p | 4–12 s | 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 | First + last frame | On |
MiniMax-H3 | 768P, 2K | 4–15 s | 21:9, 16:9, 4:3, 1:1, 3:4, 9:16 | First + last frame | No public generated-audio toggle |
kling-v2-6 | 720p, 1080p | 5 or 10 s | 16:9, 9:16, 1:1 | First + conditional last frame | Off |
kling-3.0-turbo | 720p, 1080p | 3–15 s | 16:9, 9:16, 1:1 | First frame only | Unsupported |
kling-v3 | 720p, 1080p, 4K | 3–15 s | 16:9, 9:16, 1:1 | First + last frame | On |
kling-v3-omni | 720p, 1080p, 4K | 3–15 s | 16:9, 9:16, 1:1 | First + last or up to 2 roleless images | On |
kling-video-o1 | 720p, 1080p | 5 or 10 s | 16:9, 9:16, 1:1 | First + last frame | Unsupported |
kling-v2-6-motion-control | std, pro | Reference video | — | 1 image + 1 video | Original sound on |
kling-v3-motion-control | std, pro | Reference video | — | 1 image + 1 video | Original sound on |
grok-imagine-1.5-video | 480p, 720p | 6–30 s | 16:9, 9:16, 1:1, 3:2, 2:3 | Up to 7 roleless images | Unsupported |
For standard models, omitted option values use each model's defaults shown above. model itself is always required. The service validates combinations before starting the job.
Response and download behavior
All job responses use this shape:
| Field | Meaning |
|---|---|
id | Owner-scoped job ID used for polling, content, deletion, and support |
object | Always video.generation.job |
status | queued, running, succeeded, or failed |
model | Canonical model ID after alias normalization |
created_at, updated_at | UTC RFC 3339 timestamps |
progress | Best-effort generation progress, omitted when unavailable |
url | Present only after success; may be a relay content path or a signed relay file path |
cost_rmb | Final task charge, present after successful settlement |
error.code | Stable failure category on a failed job |
Prefer GET /v1/videos/tasks/{id}/content instead of storing assumptions about the returned URL shape. Use -L or enable redirects in your HTTP client. The authenticated content endpoint can return 200, 206 Partial Content, or a redirect to relay-managed storage.
A signed relay file URL can be opened without an API key; possession of the complete URL grants access to that video. Do not expose it in public logs, pages, or client-side analytics.
Deleting a safe terminal job returns HTTP 204 and may also remove its relay-managed video file. Download and store any result you need before deleting the job. Deletion is rejected with 409 video_delete_unsafe while a job is running or billing is unsettled. Deleting a local job record does not cancel generation that has already started.
Error handling
HTTP errors use:
{
"error": {
"type": "invalid_request",
"message": "model is not supported"
}
}
| HTTP | Error type | Meaning / action |
|---|---|---|
400 | invalid_request | Invalid model, field, media URL, role, duration, ratio, or model combination; fix the request |
401 | unauthorized | API key is missing, invalid, or disabled |
402 | insufficient_balance | Balance cannot cover the requested job; generation is not started |
404 | not_found | The job does not exist or belongs to another account |
409 | video_not_ready | Content was requested before terminal success |
409 | video_delete_unsafe | The job is not safe to delete yet |
413 | request_too_large | JSON create body exceeds its limit |
502 | upstream_error | Video generation or result download failed |
503 | video_service_unavailable, upstream_quota_exceeded, video_price_unavailable | Video service capacity or current public pricing is temporarily unavailable |
503 | submission_unknown | Job acceptance is uncertain; do not submit a duplicate |
A failed job response may look like:
{
"id": "vid_1785398400000000000",
"object": "video.generation.job",
"status": "failed",
"model": "doubao-seedance-2.0",
"error": { "code": "generation_failed" }
}
Billing
Video generation uses account balance, not monthly quota.
task price = displayed RMB price per second for the selected model/specification × billable seconds
Check the current per-second prices on the Pricing page. Resolution, generated audio, or Motion Control mode may select a different displayed per-second row. For standard models, billable seconds are the requested duration; for Motion Control, they are the detected reference-video duration rounded up to a whole second.
The balance must be able to cover the job when it is created. Insufficient balance returns 402 before generation starts. The successful job's cost_rmb is the authoritative final charge. A definitely failed job is not charged.
Related guides
Image Generation API
Submit prompts and size options to the OpenAI-compatible image API, then understand synchronous results, image links, errors, and billing.
Balance Query API
Call the balance endpoint with a Bearer token to inspect CNY balance, daily plan allowance, expiration, and each response field.