Preview the composition and motion with a 480p draft, then upgrade the completed draft. Billed by actual video tokens. Reference videos count toward usage. Unused reserved balance is returned. Draft and upgrade are charged separately. Content review applies. An unmoderated mode is not offered. Priced at approximately 95% of BytePlus public international USD list rates, rounded to one decimal place. MP4 for general use; MOV may require downloading with a compatible player.
Try in Playground →Generate a finished video, or preview a draft before rendering at 1080p. Choose your workflow to see the parameters and example you need.
What would you like to do?
Generate a finished video
Parameters for this stepInherits prompt, references and duration from the draft. Do not resend those fields.
| Parameter | Type | Description |
|---|---|---|
| modelRequired | string | |
| promptOptional | string | Text prompt. Use prompt OR content. Required except draft upgrades. |
| contentOptional | array | Typed text/image_url/video_url/audio_url blocks. HTTPS reference URLs, at most 30 images, 10 videos and 10 audio files. Supplier asset IDs are not accepted. |
| durationOptional | integer | 4–30 seconds; default 5. Omit for upgrades. |
| resolutionOptional | string | |
| ratioOptional | string | adaptive / 21:9 / 16:9 / 4:3 / 1:1 / 3:4 / 9:16 |
| output_formatOptional | string | mp4 / mov. MP4 for general use; MOV may require downloading with a compatible player. |
| draft_task_idRequired | string | Your successful Token draft gateway task ID. Per-second tasks, ordinary 480p videos and external IDs are not supported. |
Advanced: create a draft using the standard model
Use seedance-2.5-token with draft: true and explicit resolution: 480p. An ordinary 480p request without draft: true is not upgradeable.
Request example
Authentication
Authorization: Bearer YOUR_API_KEY Content-Type: application/json
Create an API Key in the console to start.
Response
Complete workflow example · Python 3 · 480p → 1080p
Draft → 1080p
Only a completed Token draft from your own account can be upgraded. Per-second models, standard 480p videos and external task IDs are not supported.
Create a 480p draft, poll until succeeded, then send its NezhaGate job ID as draft_task_id to the standard Token model. The model uses the original draft task to generate a 1080p final video; this is not an arbitrary MP4 upscaler. Do not repeat prompt, content, duration, ratio, seed or generate_audio. Draft and final video are billed separately.
Python 3, no extra dependencies. Set NEZHAGATE_API_KEY, then run the script once. It creates a draft, polls it and upgrades it. Draft and final video are billed separately. Do not rerun the whole script to check an existing job.
import json
import os
import time
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
BASE_URL = "https://nezhagate.com"
API_KEY = os.environ["NEZHAGATE_API_KEY"]
def request(path, payload=None):
data = json.dumps(payload).encode() if payload is not None else None
req = Request(BASE_URL + path, data=data, headers={
"Authorization": "Bearer " + API_KEY,
"Content-Type": "application/json",
})
with urlopen(req, timeout=60) as response:
return json.load(response)
def submit(payload):
# Do not automatically retry a POST: its outcome may be unknown.
job = request("/v1/videos/generations", payload)
job_id = job["id"]
print("Submitted:", job_id, flush=True)
return job_id
def wait_for(job_id):
deadline = time.monotonic() + 1800
while time.monotonic() < deadline:
try:
job = request("/v1/videos/jobs/" + job_id)
except HTTPError as exc:
if exc.code != 429 and exc.code < 500:
raise
time.sleep(10)
continue
except (URLError, TimeoutError):
time.sleep(10)
continue
progress = job.get("progress", {})
print(job_id, job["status"], progress.get("phase", ""), flush=True)
if job["status"] == "succeeded":
return job
if job["status"] == "failed":
raise RuntimeError(json.dumps(job.get("error"), ensure_ascii=False))
time.sleep(5)
raise TimeoutError("Keep polling this job; do not resubmit: " + job_id)
if __name__ == "__main__":
# Two separately billed tasks. Save the printed IDs to resume polling.
draft_id = submit({
"model": "seedance-2.5-token-draft",
"prompt": "A sunrise over the sea",
"duration": 5,
"resolution": "480p",
})
draft = wait_for(draft_id)
print("Draft video:", draft["data"][0]["url"])
final_id = submit({
"model": "seedance-2.5-token",
"draft_task_id": draft_id,
"resolution": "1080p",
})
final = wait_for(final_id)
print("Final video:", final["data"][0]["url"])
People and reference materials
API: upload local files to publicly accessible HTTPS storage, then send image_url, video_url or audio_url in content. Keep links accessible while the job is running. NezhaGate automatically imports references into the material library and waits until every asset is Active before submitting generation. This also applies to real-person references; uploading a file alone does not make it ready. No separate material-library API call is required from you.
Uploaded files remain available for selection. Material registration and review start only after Run, confirmation and successful balance reservation.
| Step | Description |
|---|---|
| 1. Upload the file | Upload the character image to public HTTPS storage. JSON does not accept local paths or binary files. |
| 2. Submit the reference | Pass image_url in content and refer to the character as @Image1 in the prompt. |
| 3. Import and review | NezhaGate imports and waits for review automatically. You do not need to create asset groups or send asset:// IDs. |
| 4. Check the result | Poll progress on the same job. Rejected references return failed and error without submitting video generation. |
- Up to 30 images, 10 videos and 10 audio files, 50 total. Reference videos combined: at most 30s; reference audio combined: at most 30s. Browser file limits: <30 MB/image, 50 MB/video, 15 MB/audio. API JSON accepts HTTPS URLs, not local paths or file bytes. Original images, under 30 MB each; both dimensions 301–5999 px, aspect ratio 0.4–2.5. Originals are preserved; portrait references must finish material preparation. MP4 for general use; MOV may require downloading with a compatible player.
data[0].source_url— The original link is returned by the video service and may expire. Save it promptly, or download our hosted original file.
curl https://nezhagate.com/v1/videos/generations \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"model": "seedance-2.5-token",
"content": [
{
"type": "text",
"text": "The character from @Image1 walks by the sea."
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/reference.jpg"
}
}
],
"duration": 5,
"resolution": "720p"
}'Task status polling
Use the same Bearer API key to poll GET /v1/videos/jobs/{id} every 3–5 seconds. Only the owning account can read a task. Keep the existing status handling: queued / processing continue polling; succeeded reads data[0].url; failed reads error. progress is an additive field for Token tasks; clients must tolerate missing fields and unknown future phases.
curl https://nezhagate.com/v1/videos/jobs/img_example -H 'Authorization: Bearer YOUR_API_KEY'
| progress.phase | Description |
|---|---|
| queued | Waiting for processing |
| preparing | Preparing references |
| reviewing | Reviewing references |
| submitting | Submitting generation |
| provider_queued | Waiting for generation |
| generating | Generating video |
| delivering | Saving video |
| succeeded | Completed |
| material_failed | Reference rejected or import failed |
| submission_failed | Generation submission failed or unconfirmed |
| generation_failed | Video generation failed |
| delivery_failed | Saving result failed |
| failed | Task failed |
- phase is a machine-readable stage, not a percentage or time estimate. ready / total count unique references ready / required for this task; zero is valid for text-only and draft upgrades. label and detail are English display text in the API; do not branch on them. terminal mirrors the terminal top-level status. failed_stage is null unless failed, when it contains the failure phase. Stages may be skipped or repeated during recovery.
- submitted is true only after a generation task ID is confirmed; false means no submission has started; null means a submission started but its outcome is unconfirmed. Never automatically resubmit on null or a timeout. reservation_released becomes true only after a zero-cost release is recorded; false does not prove a charge. updated_at is the job record timestamp, not a separate phase timestamp; job_id is the NezhaGate task ID.
- Parameter, balance or initial prompt rejection returns an HTTP error before task creation, with no task ID to poll. Initial review does not guarantee final approval. Reference import/review failures stop generation and appear as failed with error. For temporary polling/network failures, back off and query the same ID; do not create another task. No intermediate webhook events are added.
{
"id": "img_example",
"object": "video.generation.job",
"status": "processing",
"created": 1792800000,
"model": "seedance-2.5-token",
"progress": {
"phase": "reviewing",
"label": "Reviewing references",
"detail": "References ready: 3 / 5",
"ready": 3,
"total": 5,
"terminal": false,
"failed_stage": null,
"submitted": false,
"reservation_released": false,
"updated_at": "2026-10-24T05:00:00+00:00",
"job_id": "img_example"
}
}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. When the sound fails review, error.param is audio and the message says whether it was the generated sound or your reference audio.
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
Error codes
Every error carries error.message / error.type / error.code / error.param; branch on code. The most common ones are below; the full list is under Error codes.
| HTTP | code | Description |
|---|---|---|
| 401 | invalid_api_key | No key, or the key is invalid or disabled (missing_api_key, invalid_api_key). |
| 402 | insufficient_quota | Not enough balance, or the key has reached its budget (insufficient_quota). |
| 400 | invalid_request | A bad parameter or an unknown model (such as model_not_found); error.param names the field. |
| — | content_policy | Content review failed: the prompt, a reference, or the generated picture or sound was flagged. Not billed; the same input is rejected again, so reword the prompt or replace the reference. Video jobs return it in error when status is failed. |
| 429 | rate_limit_exceeded | Over the per-minute limit set on this key or a free model's daily quota, or every line for the model is busy (rate_limit_exceeded). Wait the seconds in Retry-After when it is there, otherwise a few seconds, then retry. |
| 502 | upstream_* | The upstream failed or timed out and switching lines did not help; code says why, e.g. upstream_timeout or upstream_unavailable. Not billed; safe to retry. |