Choose any integer duration from 4–30 seconds, 720P, 16:9 or 9:16. Up to 30 reference images and 10 reference audio clips; no reference video. $2/clip (400 credits) without reference audio, $3/clip (600 credits) with reference audio, regardless of duration. A generated soundtrack is not reference audio. Available temporarily; ongoing supply is not guaranteed. Render time varies with the queue. Poll the original task instead of resubmitting. Failed jobs are fully refunded.
Try in Playground →Quick start
Three steps: submit a job, poll it, take the video link. Replace YOUR_API_KEY with your key and the commands below run as they are.
Step 1 · Submit (returns a job id at once, HTTP 202)
curl https://kovapi.com/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "seedance-2.5-30s", "prompt": "A traveller walks through an autumn valley town from dawn to dusk, the camera following all the way", "duration": 25, "resolution": "720P", "size": "16:9"}'Response (HTTP 202)
{
"id": "img_5c2e8f1a9b3d7e6f0a4c8b2d",
"object": "video.generation.job",
"status": "queued",
"created": 1791281400,
"model": "seedance-2.5-30s"
}Step 2 · Poll every 5–10 seconds until status is succeeded or failed
curl https://kovapi.com/v1/videos/jobs/img_5c2e8f1a9b3d7e6f0a4c8b2d \ -H "Authorization: Bearer YOUR_API_KEY"
Response when it succeeds
{
"id": "img_5c2e8f1a9b3d7e6f0a4c8b2d",
"object": "video.generation.job",
"status": "succeeded",
"created": 1791281400,
"model": "seedance-2.5-30s",
"data": [
{"url": "https://img.kovapi.com/i/8c1f0e5d2b7a49e3a6d0c4f1b9e2a7d5.mp4"}
],
"usage": {"clips": 1, "model": "seedance-2.5-30s"}
}Step 3 · data[0].url is the clip (MP4). It is kept for 15 days; download it to your own storage to keep it longer.
Complete example (Python: submit, wait, save to disk)
import time
import requests
BASE = "https://kovapi.com/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
# 1. submit
job = requests.post(f"{BASE}/videos/generations", headers=HEADERS, json={
"model": "seedance-2.5-30s",
"prompt": "A traveller walks through an autumn valley town from dawn to dusk, the camera following all the way",
"duration": 25,
"resolution": "720P",
"size": "16:9",
}).json()
if "error" in job:
raise SystemExit(job["error"]["message"])
# 2. poll every 10 seconds, for up to an hour
deadline = time.time() + 3600
while job["status"] not in ("succeeded", "failed") and time.time() < deadline:
time.sleep(10)
job = requests.get(f"{BASE}/videos/jobs/{job['id']}", headers=HEADERS).json()
# 3. download the video
if job["status"] == "succeeded":
url = job["data"][0]["url"]
with open("seedance-2.5-30s.mp4", "wb") as f:
f.write(requests.get(url).content)
print("saved", url)
elif job["status"] == "failed":
print("failed:", job["error"]["code"], job["error"]["message"])
else:
print("still", job["status"], job["id"])While queued, the poll also returns queue_position (jobs ahead of yours) and eta_seconds (expected wait). If your time limit passes, do not resubmit: the job is still there, so keep polling the same id later.
Authentication
Authorization: Bearer YOUR_API_KEY Content-Type: application/json
Create an API Key in the console to start.
At a glance
| Duration | 4–30 s |
| Resolution | 720P |
| Aspect ratio | 16:9 / 9:16 |
| Reference images | up to 30 · used as references (subject, scene, style), never as a frame |
| Reference videos | not supported |
| Reference audio | up to 10 |
| Sound | always on; cannot be switched off (audio: false is refused) |
| Billing | per clip: one price whatever the length |
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Model ID, here seedance-2.5-30s. |
| prompt | string | Yes | Describe the content, camera and actions. |
| duration | integer | No | Any integer from 4 to 30 seconds (default 30). Alias: seconds. Duration does not change the per-clip price. |
| resolution | string | No | 720P |
| size | string | No | Aspect ratio: 16:9 / 9:16 (default 16:9); alias aspect_ratio. 21:9 is not available on this model. |
| image | string | No | Image-to-video: include a reference image (public URL, data: URI or base64); omit for text-to-video. |
| images | array | No | Multiple reference images, up to 30. Exceeding the cap is rejected, never truncated. Alias: reference_images. |
| audio_reference | string / array | No | Public HTTPS URL or array, up to 10 clips; requires at least one reference image. Upload MP3/WAV/M4A, up to 15 MB and 30 s each. Any reference audio makes this clip $3 (600 credits). |
| audio | boolean | No | Every clip comes with sound and it cannot be switched off; false is refused (400), so leave it out. |
| callback_url | string | No | Where to push the result when the job ends; see Webhooks below. |
| n | integer | No | Each job makes one clip; leave it out. A value above 1 still makes one clip, but n clips are held at submit and the extra is returned when the job ends. |
Price
Temporary availability · per clip
| Credits / clip | USD / clip | |
|---|---|---|
| Without reference audio | 400 | $2.00 |
| With reference audio | 600 | $3.00 |
Choose any integer duration from 4–30 seconds, 720P, 16:9 or 9:16. Up to 30 reference images and 10 reference audio clips; no reference video. $2/clip (400 credits) without reference audio, $3/clip (600 credits) with reference audio, regardless of duration. A generated soundtrack is not reference audio. Available temporarily; ongoing supply is not guaranteed. Render time varies with the queue. Poll the original task instead of resubmitting. Failed jobs are fully refunded.
Reference material
Choose any integer duration from 4–30 seconds, 720P, 16:9 or 9:16. Up to 30 reference images and 10 reference audio clips; no reference video. $2/clip (400 credits) without reference audio, $3/clip (600 credits) with reference audio, regardless of duration. A generated soundtrack is not reference audio. Available temporarily; ongoing supply is not guaranteed. Render time varies with the queue. Poll the original task instead of resubmitting. Failed jobs are fully refunded.
Up to 30 reference images set the subject, scene and style and are never used as a frame; name them @Image1, @Image2 in the prompt.
Real human faces work as reference images, no masking needed. Please make sure you have the right to use the likeness of anyone pictured.
{
"model": "seedance-2.5-30s",
"prompt": "Follow @Image1 and use @Audio1 as the soundtrack",
"duration": 25, "resolution": "720P", "size": "16:9",
"images": ["https://example.com/reference.png"],
"audio_reference": ["https://example.com/reference.mp3"]
}Content requirements
Every Seedance job goes through content review: the prompt, the reference files and the finished clip, picture and sound alike. A job that fails review ends with content_policy and is not charged.
When review happens
The prompt and references are reviewed at submit; the finished clip is reviewed again, picture and sound, once it is generated. A rejection can therefore arrive seconds after submitting or only when rendering ends, which can take over half an hour. A job that ends with status failed and error.code content_policy failed review; with a webhook you receive a video.failed event.
Not charged
A rejected job costs nothing: the amount held at submit is refunded in full when the job ends, and the call log shows it as not billed.
Sound
The soundtrack (dialogue, narration, singing and effects) is generated by the model from your prompt and reviewed with the picture: spoken lines, narration and lyrics in the prompt are voiced or sung. For a clip without voices, leave lines and lyrics out of the prompt. This model always adds a soundtrack; it cannot be turned off. Uploaded reference audio is reviewed too, and copyrighted music used as reference audio may be rejected.
Commonly rejected content
- Sexual content, nudity or sexual suggestion, including AI-generated or composited images
- Gore, cruelty, self-harm, and violent injury such as gunshot wounds and bleeding
- Firearms, explosives and drugs
- Any sexualised content involving minors; minors shown with alcohol, tobacco, drugs or in dangerous situations are rejected too
- Politicians, celebrities, athletes, business figures and other public figures, in looks or voice, including look-alikes
- Copyrighted characters and IP, such as Disney or Marvel characters
- Hateful or discriminatory content, and fake news
Long storyboard scripts
One shot that breaks the rules fails the whole clip, and long storyboard scripts are where it happens most: a gunfight or a close-up of bleeding in an action scene, or minors in a drinking or smoking scene, sink the whole clip. Read the script through before submitting and remove or rewrite such shots.
Real people
To use a photo of a real person as a reference you need the right to use their likeness; whether a model accepts real-person references is stated above on this page.
After a rejection
Resubmitting the same input is rejected again. Find the part that is likely at fault, reword the prompt or replace the reference, then submit again.
Error code: content_policy · status: failed
upstream_line_unavailable: If the generation service is overloaded or available capacity is exhausted, the error is upstream_line_unavailable. Failed tasks are not charged and reserved credits are refunded; retry later. This error does not identify a specific parameter or material problem. Explicit duration limits, unreadable references and content-review rejections retain duration_rejected, reference_unreadable and content_policy respectively; follow the relevant guidance.
Job status and fields
GET /v1/videos/jobs/{id} reports these statuses; every field is described in Async jobs. Poll every 5–10 seconds, or let a webhook (below) push the result. Polling is free, and any key of the same account can read the job.
| Status | Meaning |
|---|---|
| queued | Waiting in line. Carries queue_position (jobs ahead of yours) and eta_seconds (expected wait). |
| processing | Rendering. |
| succeeded | Done. The result is in data[0].url, the billing detail in usage. |
| failed | Failed. The reason is in error; the credits held for the job are refunded in full. |
processingcan go back toqueued, for example while the service is busy, while a finished clip is fetched again, or after a restart. If a job cannot be finished, the gateway may render it again automatically; however many retries it takes, a job is charged once.- One account runs up to 50 jobs at a time; further jobs wait in submission order, and a job that has not started after 15 minutes fails and is refunded in full. Job records are kept for 3 days and video links for 15 days.
How long it takes
Choose any integer duration from 4–30 seconds, 720P, 16:9 or 9:16. Up to 30 reference images and 10 reference audio clips; no reference video. $2/clip (400 credits) without reference audio, $3/clip (600 credits) with reference audio, regardless of duration. A generated soundtrack is not reference audio. Available temporarily; ongoing supply is not guaranteed. Render time varies with the queue. Poll the original task instead of resubmitting. Failed jobs are fully refunded.
Errors and refunds
An error at submit comes back at once as HTTP 4xx / 5xx: no job is created and nothing is charged. A failure after the job was accepted is reported by the job endpoint, and the hold is refunded in full. Every code is listed under Error codes.
Errors at submit
| HTTP | code | Cause | What to do |
|---|---|---|---|
| 400 | invalid_request | A parameter is not valid: the duration, resolution, ratio or number of references is outside this model's range, the prompt is too long, a reference image or video cannot be read, audio: false was sent to a model whose clips always have sound, and so on. message says which. | Fix what message says and submit again. |
| 400 | invalid_callback_url | callback_url is not a public http(s) address, or is longer than 2000 characters. | Use an address that can be reached from the internet. |
| 400 | model_not_found | The model id is misspelled, model is missing, or the model cannot be used on this endpoint (a chat model on the image endpoint, for example). | Use a model id from GET /v1/models or the API reference. |
| 401 | invalid_api_key | The key does not exist, was deleted or is disabled, or the account is disabled. | Check the key on the API Keys page of the console. |
| 402 | insufficient_quota | Not enough balance to hold this request. The gateway holds the highest possible cost of the request first, so a burst of concurrent calls can trigger it too. | After a burst, wait a few seconds and retry; otherwise top up. |
| 429 | rate_limit_exceeded | Over the requests-per-minute limit set on this key (counted over the last 60 seconds). No Retry-After header. | Wait a few seconds and retry, or raise the key's limit in the console. |
| 503 | model_maintenance | The model is under maintenance and takes no requests; no other model answers in its place meanwhile. | Retry when maintenance ends; see Status. |
| 503 | service_unavailable | A temporary failure on our side, such as storing a reference file. No job was created and nothing was charged. | Retry shortly. |
Failed jobs (status failed)
| code | When you see it | What to do |
|---|---|---|
| content_policy | Video: the prompt, a reference, or the generated picture or sound did not pass content review (param is audio when the sound was flagged). Wan Standard and Prime: input material did not pass review. error.reason is FaceDetectionSuspect (possible real human face) or DataInspectionFailed (input content review failed). error.message explains the cause and recommended change. Failed tasks are not billed. | Rewrite the prompt or replace the reference before you submit again; the same input is refused again. |
| duration_rejected | Video: the length was not accepted. Most often a reference audio or video is over the limit, even by a fraction of a second. | Trim the reference audio or video to within the limit, then submit again; the same input fails again. |
| reference_unreadable | Video: the model could not download a reference image, video or audio clip, most often because the link is a web page such as YouTube or a cloud-drive share page. | Use a link that downloads the file itself, or upload the file in the Playground; the same input fails again. |
| invalid_video_request | The parameters or references were refused at submission; nothing was generated. | Check the duration, aspect ratio and references, then resubmit. Not charged. |
| special_failed | Generation failed and no clip was produced. | Not charged. You can resubmit it unchanged. |
| special_unknown | There was no clear answer on whether the submission was taken. To avoid generating twice, it was not submitted again. | Not charged. You can submit it again. |
| special_fetch | The clip was rendered, but retrieving the file failed repeatedly. | Not charged. Please submit it again. |
| special_unavailable | This tier was unavailable, or the job did not start within 120 minutes. | Not charged. Please try again later. |
Branch on code, not on the text of message. Treat any code you do not recognise (or a missing one) as upstream_error: resubmitting usually works. Every failed job is refunded in full.
Webhooks instead of polling
Add callback_url when you submit and the moment the job ends, success or failure, the gateway pushes exactly what the poll endpoint returns to that URL, signed and retried on failure. You can also set an account-wide address under Settings → Webhook in the console. Webhook docs →
curl https://kovapi.com/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "seedance-2.5-30s", "prompt": "A traveller walks through an autumn valley town from dawn to dusk, the camera following all the way", "duration": 25, "resolution": "720P", "size": "16:9", "callback_url": "https://your-domain.com/webhook"}'