AionRouter Video Generation API Guide
Video generation usually runs as a task. Your application must submit a request, save the task ID, query status, then download and review the video. A submission response is not the final video.
This guide uses Seedance JSON requests for text-to-video and reference images. It covers state management, retries, downloads, video editing and the different OpenAI Video query contract, with macOS/Linux, Windows PowerShell/CMD and Python examples.
Choose a protocol, model and output settings
List your delivery requirements first: orientation, duration, audio, reference images or source video, and output specifications. Check the model's endpoints and capabilities in the model catalog before adding fields to the request.
| Generation type | Main input | Check first |
|---|---|---|
| Text-to-video | Prompt | Duration, aspect ratio, resolution and synchronized audio support |
| Image-guided video | Image and motion/camera instructions | Reference format, count and first-frame or other role definitions |
| Video editing or extension | Source video and requested changes | Model support, asset roles, aspect ratio and special duration rules |
| Batch jobs in an application | Those inputs plus a business task ID | Deduplication, state persistence, polling and archival |
Image input and video output are separate capabilities. An image model generating reference assets does not mean it accepts video tasks. Models from the same video provider can also have different limits.
Endpoints and response formats
| Endpoint | Purpose | Notes |
|---|---|---|
POST /v1/video/generations |
Create a video task | Used by this guide's Seedance examples |
GET /v1/video/generations/{task_id} |
Query a generic video task | Usually wrapped in data; fields vary by task platform |
POST /v1/videos |
OpenAI Video-compatible creation | Capabilities depend on model and channel |
GET /v1/videos/{task_id} |
Query an OpenAI Video-compatible task | Requires channel conversion support; differs from the generic response |
GET /v1/videos/{task_id}/content |
Proxy video content | Requires appropriate access, task state and result URL |
Available models may expose dedicated protocols such as Kling-compatible endpoints. Use an endpoint only when the selected model and channel support it; an existing route is not universal model support.
Choose a model and configure the environment
Check capabilities and prices in the Model Marketplace, then verify visibility with /v1/models using your key.
These integrated Dreamina Seedance models have the following manual duration validation ranges. The table does not grant your account access to them.
| Model | Manually requested duration |
|---|---|
dreamina-seedance-2-0-260128 |
4–15 seconds |
dreamina-seedance-2-0-fast-260128 |
4–15 seconds |
dreamina-seedance-2-0-mini-260615 |
4–15 seconds |
dreamina-seedance-2-5-260628 |
4–30 seconds |
Automatic or editing duration conventions are model-specific; -1 is not valid for arbitrary video models. Resolution, reference media and enhancement have separate rules and cannot be inferred from maximum duration.
Terminal compatibility
Use the examples for your shell: macOS/Linux use Bash or Zsh; Windows uses PowerShell or CMD. Git Bash/WSL can use the Bash examples, but run all setup and requests in the same environment. The current API root is https://test.aionrouter.io.
Windows examples explicitly use curl.exe to avoid the curl alias in Windows PowerShell 5.1. Check curl --version or curl.exe --version; --fail-with-body requires cURL 7.76.0 or later. Microsoft: curl on Windows · cURL options
Run one shell variant only. Environment variables apply to that terminal and programs started from it. In CMD, run each line separately; at the AIONROUTER_BASE_URL prompt, paste the API root shown above, without a trailing slash. Then enter the actual key, model or voice when prompted. Do not paste Bash export, backslash continuations or heredocs into CMD or PowerShell.
macOS / Linux — Bash / Zsh
export AIONROUTER_BASE_URL='https://test.aionrouter.io'
export AIONROUTER_API_KEY='替换为控制台复制的完整密钥'
export AIONROUTER_VIDEO_MODEL='dreamina-seedance-2-0-fast-260128'
Windows — PowerShell
$env:AIONROUTER_BASE_URL = 'https://test.aionrouter.io'
$env:AIONROUTER_API_KEY = Read-Host 'AIONROUTER_API_KEY'
$env:AIONROUTER_VIDEO_MODEL = 'dreamina-seedance-2-0-fast-260128'
Windows — CMD
set "AIONROUTER_BASE_URL="
set /p "AIONROUTER_BASE_URL=AIONROUTER_BASE_URL: "
set "AIONROUTER_API_KEY="
set /p "AIONROUTER_API_KEY=AIONROUTER_API_KEY: "
set "AIONROUTER_VIDEO_MODEL=dreamina-seedance-2-0-fast-260128"
Use the console's service root without a trailing slash. If the sample model is unavailable, replace it with an accessible model using this protocol. A different protocol also requires different parameters.
Submit, poll and complete a full request
Submit a Seedance text-to-video task
macOS / Linux — Bash / Zsh
curl --fail-with-body --silent --show-error \
"$AIONROUTER_BASE_URL/v1/video/generations" \
-H "Authorization: Bearer $AIONROUTER_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary @- <<JSON
{
"model": "$AIONROUTER_VIDEO_MODEL",
"prompt": "白色陶瓷杯放在木桌上,清晨窗光从左侧照入,镜头缓慢向前推进,画面稳定,无文字。",
"duration": 5,
"metadata": {
"resolution": "720p",
"ratio": "16:9"
}
}
JSON
Windows request body: video-generation-2.json
Save this JSON in the current directory as UTF-8 without BOM, replacing YOUR_... values with actual model IDs or other required values. JSON files do not expand environment variables: changing a terminal model variable does not change this file. Do not use Windows PowerShell 5.1 > to create it, because that defaults to UTF-16LE. Both Windows commands below read the same file. Microsoft: character encoding
{
"model": "dreamina-seedance-2-0-fast-260128",
"prompt": "白色陶瓷杯放在木桌上,清晨窗光从左侧照入,镜头缓慢向前推进,画面稳定,无文字。",
"duration": 5,
"metadata": {
"resolution": "720p",
"ratio": "16:9"
}
}
Windows — PowerShell
curl.exe --fail-with-body --silent --show-error "${env:AIONROUTER_BASE_URL}/v1/video/generations" -H "Authorization: Bearer ${env:AIONROUTER_API_KEY}" -H 'Content-Type: application/json' --data-binary '@video-generation-2.json'
Windows — CMD
curl.exe --fail-with-body --silent --show-error "%AIONROUTER_BASE_URL%/v1/video/generations" -H "Authorization: Bearer %AIONROUTER_API_KEY%" -H "Content-Type: application/json" --data-binary "@video-generation-2.json"
For this Seedance integration, the task ID is at top-level id, with the compatibility field task_id retained. This is an illustrative excerpt:
{
"id": "task_example",
"task_id": "task_example",
"object": "video",
"model": "dreamina-seedance-2-0-fast-260128",
"status": "queued",
"progress": 0
}
Save the returned public task ID immediately and use it for queries; no internal provider ID is needed. Other models may return a different submission structure, so verify the extraction path when switching.
Poll status and retrieve the result
macOS / Linux — Bash / Zsh
export AIONROUTER_TASK_ID='替换为提交响应中的任务 ID'
curl --fail-with-body --silent --show-error \
"$AIONROUTER_BASE_URL/v1/video/generations/$AIONROUTER_TASK_ID" \
-H "Authorization: Bearer $AIONROUTER_API_KEY"
Windows — PowerShell
$env:AIONROUTER_TASK_ID = Read-Host 'AIONROUTER_TASK_ID'
curl.exe --fail-with-body --silent --show-error "${env:AIONROUTER_BASE_URL}/v1/video/generations/${env:AIONROUTER_TASK_ID}" -H "Authorization: Bearer ${env:AIONROUTER_API_KEY}"
Windows — CMD
set "AIONROUTER_TASK_ID="
set /p "AIONROUTER_TASK_ID=AIONROUTER_TASK_ID: "
curl.exe --fail-with-body --silent --show-error "%AIONROUTER_BASE_URL%/v1/video/generations/%AIONROUTER_TASK_ID%" -H "Authorization: Bearer %AIONROUTER_API_KEY%"
Seedance generic queries use a task wrapper. code: "success" means the query succeeded; check data.status to determine whether generation is complete.
data.status |
Meaning | Application action |
|---|---|---|
NOT_START, SUBMITTED, QUEUED |
Not started or queued | Keep waiting |
IN_PROGRESS |
Processing | Continue polling |
SUCCESS |
Succeeded | Read data.result_url |
FAILURE |
Failed | Read data.fail_reason and stop polling |
UNKNOWN or another unrecognized value |
No definitive state yet | Keep the task ID; do not declare success |
Example excerpt of a completed task:
{
"code": "success",
"message": "",
"data": {
"task_id": "task_example",
"status": "SUCCESS",
"progress": "100%",
"result_url": "https://assets.example.com/generated-video.mp4"
}
}
Some other video platforms use simplified generic states such as queued, processing, succeeded and failed, with data.url. Adapt parsing to the actual response when switching platforms; progress: 100% alone is not proof of success.
Queries look up tasks within the account and remain subject to key and model rules. Prefer the creating account and key; a task ID is not an unauthenticated public download credential.
Python: submit and poll a Seedance task
This standard-library example uses the Seedance submission and generic-query structures above. It polls every 5 seconds for up to 10 minutes: a client strategy, not a platform SLA.
import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request
base_url = os.environ["AIONROUTER_BASE_URL"].rstrip("/")
api_key = os.environ["AIONROUTER_API_KEY"]
def call_api(path, payload=None):
data = None
headers = {"Authorization": f"Bearer {api_key}"}
if payload is not None:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
headers["Content-Type"] = "application/json"
request = urllib.request.Request(
base_url + path,
data=data,
headers=headers,
method="POST" if payload is not None else "GET",
)
try:
with urllib.request.urlopen(request, timeout=120) as response:
return json.load(response)
except urllib.error.HTTPError as error:
detail = error.read().decode("utf-8", errors="replace")
raise RuntimeError(f"HTTP {error.code}: {detail}") from error
created = call_api("/v1/video/generations", {
"model": os.environ["AIONROUTER_VIDEO_MODEL"],
"prompt": "木桌上的陶瓷杯,柔和自然光,镜头缓慢推进,无文字。",
"duration": 5,
"metadata": {"resolution": "720p", "ratio": "16:9"},
})
task_id = created.get("id") or created.get("task_id")
if not isinstance(task_id, str) or not task_id:
raise RuntimeError(f"没有收到任务 ID:{created}")
print("已提交,请保存任务 ID:", task_id, flush=True)
query_path = "/v1/video/generations/" + urllib.parse.quote(task_id, safe="")
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
result = call_api(query_path)
if result.get("code") != "success":
raise RuntimeError(f"查询失败,保留任务 ID 后排查:{result}")
task = result["data"]
status = task.get("status")
print("状态:", status, "进度:", task.get("progress"), flush=True)
if status == "SUCCESS":
url = task.get("result_url")
if not url:
raise RuntimeError("任务成功但未取得结果地址,请保留任务 ID 排查。")
print("视频地址:", url)
break
if status == "FAILURE":
raise RuntimeError(task.get("fail_reason") or "视频生成失败")
time.sleep(5)
else:
raise TimeoutError(f"客户端停止等待。请继续查询任务 {task_id},不要重复提交。")
Run the Python example on macOS or Windows
Save the code as example.py in UTF-8. In the terminal where you set the variables above, use python3 example.py on macOS/Linux or py -3 example.py on Windows. If the Windows launcher is unavailable but Python 3 is installed, use python example.py. The standard-library code is shared; variables set in PowerShell, CMD and WSL are not automatically shared.
A client polling timeout does not cancel an accepted task. Keep the ID even if the program exits so you can resume querying.
Save the task ID immediately. Associate at least your business task ID, platform task ID, model, submission time, parameters and current state. After a page refresh or restart, continue querying the same task rather than creating another.
Give polling an interval and stopping conditions. Wait while processing, and stop on terminal success or failure. Adjust intervals for rate limits or temporary query errors. An HTTP status query is not a new generation, and closing the page should not lose its ID.
Match fields rather than searching status text. Identify the query format you use and read its status and result fields. Request-level success, queued state and completed generation are different levels. A word such as “success” in the body is insufficient.
Use reference assets and Seedance editing
Generate video from a reference image
Seedance models supporting image references accept images in the generation request. Replace the sample URL with media that stays accessible throughout execution.
{
"model": "dreamina-seedance-2-0-fast-260128",
"prompt": "保持参考图中杯子的外观,镜头轻微绕杯移动,光线自然,主体不变形。",
"duration": 5,
"images": [
"https://assets.example.com/cup-reference.png"
],
"metadata": {
"resolution": "720p",
"ratio": "16:9"
}
}
Multimodal references can also use model-specific metadata.content, including image_url, video_url, audio_url and roles. These are not universal video fields; follow the selected model's limits for counts, duration, dimensions and combinations.
Common parameters and Seedance editing rules
| Field | Meaning |
|---|---|
model |
Actual accessible video model ID |
prompt |
Text description; this example protocol requires nonempty input |
duration |
Requested integer seconds, within model-specific limits |
seconds |
String compatibility field; must match duration when both are supplied |
images |
Supported image-reference array |
metadata.resolution |
Output resolution such as 720p, subject to model and site rules |
metadata.ratio |
Supported aspect ratio such as 16:9 or 9:16 |
metadata.generate_audio |
Synchronized audio where supported, not an independent TTS endpoint |
metadata.seed |
Supported random seed, without exact reproduction guarantees |
metadata.content |
Model-specific multimodal references |
Avoid contradictory duration or resolution fields. High resolution, audio and video references are not supported uniformly across models.
Seedance 2.5 video editing
The capable dreamina-seedance-2-5-260628 integration uses metadata.omni_reference_task_type for auto, reference, edit or extend. Editing example:
{
"model": "dreamina-seedance-2-5-260628",
"prompt": "保留原视频的镜头运动,将背景改成明亮的现代厨房。",
"duration": -1,
"metadata": {
"omni_reference_task_type": "edit",
"ratio": "adaptive",
"output_format": "mp4",
"content": [
{
"type": "video_url",
"role": "reference_video",
"video_url": {
"url": "https://assets.example.com/source-video.mp4"
}
}
]
}
}
edit and extend require a reference video and adaptive ratio. Editing uses duration: -1 to follow source duration and omits frames. Extension must use duration settings appropriate to the desired output, rather than mechanically copying editing settings.
These fields are specific to Seedance 2.5 and cannot be assumed for other video models without the same declared capabilities.
Describe subject, motion and camera changes separately. Keep a product's appearance fixed while slowly moving the camera, or ask for one clear character action. Many scene changes, complex text and long camera movements are harder to review. Start with one short scene to assess the model.
Keep references available throughout processing. A local path is not a public URL. Temporary links must last long enough for execution. Where access is restricted, provide assets in a form the generation service can actually read through the selected protocol.
Editing is not another text-to-video request. Preserve the source-video association, task type and required asset roles. Special duration and adaptive settings in examples apply only to the specified model and operation, not all generation requests.
Read query formats, download and review videos
How OpenAI Video-compatible queries differ
Where the channel supports it, GET /v1/videos/{task_id} returns an OpenAI Video-compatible object. Its top-level status uses values such as queued, in_progress, completed and failed, unlike generic data.status: "SUCCESS".
For example, Seedance can return the successful URL in metadata.url and failures in error. Implement one verified query format rather than mixing top-level fields with the data wrapper.
For proxied downloads, call /v1/videos/{task_id}/content with the required authentication. When downloading a result URL directly, do not forward the AionRouter key to the media-storage domain.
After generation, check the result URL, complete download and playback in that order. Validate required duration, resolution, aspect ratio and audio tracks against the actual downloaded media, not only submitted parameters.
Use the external result URL or an authenticated content proxy when supported. Authenticate to the platform proxy as required, but do not forward the platform key to provider or storage hosts. For redirects such as 302, inspect the destination and actual response type.
External APIs do not inherit playground archival. Web trial tasks have a separate video archive flow. This does not mean external video tasks are copied to that storage. For long-term access, reliable downloads or a business CDN URL, save the final file to your storage and keep its task association.
Handle duplicates, timeouts, usage and failed tasks
Costs, duplicate submissions and saving results
Tasks may reserve quota on submission and settle under the actual rules on completion; failed terminal tasks have refund handling. Current pricing, funding sources, refund timing and reconciliation depend on the deployed service and console records. A query timeout is not evidence of task failure or a completed refund.
After receiving an ID, continue querying that task. Disconnection, temporary query failure or a long wait should not automatically create another task, which can generate new output and charges.
Result URLs may expire. Download completed videos into your own storage; do not treat provider URLs as permanent hosting. If the application requires fixed dimensions or media specifications, inspect the downloaded file too.
| Situation | Suggested action | What not to assume |
|---|---|---|
| An ID exists but processing is taking a long time | Keep checking that task and its errors | Elapsed time alone does not prove failure |
| Creation timed out without returning an ID | Save request details and check usage before retrying | The server may have received it |
| A status query temporarily fails | Back off and query the same ID | A new task is not automatically needed |
| Generation succeeded but downloading failed | Check the link, proxy and expiry, then retry the download | Download failure is not generation failure |
| The task reaches terminal failure | Inspect its error and actual settlement records | Not every error implies an immediate refund |
A local business ID can prevent double clicks and duplicate queue entries, but does not give the external endpoint idempotency. Playground deduplication also cannot be assumed for all external video requests.
For reconciliation, retain the model, group, duration, resolution, inputs and final output specifications. Models may charge on different dimensions. Do not derive a universal video price from another model's rate or a displayed discount percentage.
FAQ and related guides
Frequently asked questions
Why is submission queued without a video URL?
Generation is asynchronous. Save the ID and query it; retrieve the result URL only after successful completion.
Why is code: success returned before completion?
It means the status query succeeded. Generation state is in data.status; they are separate outcomes.
Can all models create 30-second, 1080p or 4K videos?
No. Duration, resolution, inputs and enhancement depend on model and site configuration. The duration table applies only to its listed Seedance models.
Should I resubmit after a timeout?
If you have an ID, query the original task first. If no submission response arrived, inspect the request ID and console records; do not assume the server created nothing.