Dux API
Pelliculam genera
Pelliculae: mitte, interroga, excipe
Pelliculae asynchronae sunt; missio ID operis reddit. Eadem clave statum interroga, post successum URL lege. Studio H3 et Seedance API diversi aditus sunt.
Petitio et effectus
model et content necessaria sunt. Optiones ratio, duration, generate_audio et seed secundum exemplar pone. content textum, imagines, pelliculas et sonos continere potest. Limites et exempla infra lege.
Effectus servandi et solutiones
Nexus pellicularum exspirant; fasciculum mature serva. Solutio token relatis nititur et semel ad primam interrogationem prosperam fit. Iterationes non iterum solvuntur. Pretia in catalogo vide.
Nomina camporum, exemplaria et itinera in codice immutata manent.
Referentia technica integra (Anglice)
Reference
Generate video
Video models work differently from chat completions. Generation is asynchronous: you submit a task, get back a task id, and poll for the result. Currently the only video model is doubao-seedance-2-0-pro (ByteDance Seedance 2.0 Pro on Volcengine Ark).
1. Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | https://chinzy.com/v1/videos/tasks | Submit a generation task. Returns { "id": "cgt-..." } immediately. |
GET | https://chinzy.com/v1/videos/tasks/{taskId} | Poll task status. Returns the upstream payload verbatim. |
Both endpoints take the same Authorization: Bearer tsk_... header you use with chat completions. See Use your API key if you don't have a key yet.
queued → running → succeeded (or failed, cancelled, expired). Typical render time is 60–180 seconds for a 4-second 720p clip.2. Submit a task
curl
curl https://chinzy.com/v1/videos/tasks \
-H "Authorization: Bearer $TOKEN_RELAY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2-0-pro",
"content": [
{"type": "text", "text": "a calm sunset over rolling hills --rs 720p --dur 4"}
],
"ratio": "16:9"
}'Request fields
| Field | Type | Notes |
|---|---|---|
model | string | Required. Use doubao-seedance-2-0-pro. (Other video aliases will appear here as they ship.) |
content | array | Required. Each item is a typed block: text, image_url, video_url, or audio_url. Inline base64 data URLs are accepted. |
ratio | string | Aspect ratio: 16:9, 9:16, 4:3, 3:4, 21:9, 1:1, or adaptive. |
resolution | string | 480p, 720p, 1080p, or 2K. You can also pass it inline in the prompt as --rs 1080p. |
duration | number | 4–15 seconds. Inline form: --dur 5. |
generate_audio | boolean | Whether to render an audio track alongside the video. |
seed | number | Optional reproducibility seed. |
3. Poll until done
# poll-loop.sh
TASK_ID="cgt-..."
while true; do
RESP=$(curl -s "https://chinzy.com/v1/videos/tasks/$TASK_ID" \
-H "Authorization: Bearer $TOKEN_RELAY_KEY")
STATUS=$(echo "$RESP" | jq -r .status)
echo "[\$(date +%T)] $STATUS"
case "$STATUS" in
succeeded)
echo "$RESP" | jq -r .content.video_url
break
;;
failed|cancelled|expired)
echo "$RESP" | jq -r .error
exit 1
;;
esac
sleep 10
donePython
import os, time, requests
BASE = "https://chinzy.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['TOKEN_RELAY_KEY']}"}
def submit(prompt: str, *, ratio="16:9", resolution="720p", duration=4):
r = requests.post(f"{BASE}/videos/tasks", headers=HEADERS, json={
"model": "doubao-seedance-2-0-pro",
"content": [{"type": "text", "text": prompt}],
"ratio": ratio, "resolution": resolution, "duration": duration,
})
r.raise_for_status()
return r.json()["id"]
def wait(task_id: str, *, timeout=600, interval=10):
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(f"{BASE}/videos/tasks/{task_id}", headers=HEADERS)
r.raise_for_status()
body = r.json()
if body["status"] == "succeeded":
return body["content"]["video_url"]
if body["status"] in ("failed", "cancelled", "expired"):
raise RuntimeError(body.get("error") or body["status"])
time.sleep(interval)
raise TimeoutError(task_id)
task_id = submit("a calm sunset over rolling hills")
url = wait(task_id)
print(url)Node.js
const BASE = 'https://chinzy.com/v1';
const headers = { Authorization: `Bearer ${process.env.TOKEN_RELAY_KEY}` };
async function submit(prompt) {
const r = await fetch(`${BASE}/videos/tasks`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'doubao-seedance-2-0-pro',
content: [{ type: 'text', text: prompt }],
ratio: '16:9',
resolution: '720p',
duration: 4,
}),
});
if (!r.ok) throw new Error(await r.text());
return (await r.json()).id;
}
async function wait(taskId, { interval = 10_000, timeoutMs = 600_000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const r = await fetch(`${BASE}/videos/tasks/${taskId}`, { headers });
if (!r.ok) throw new Error(await r.text());
const body = await r.json();
if (body.status === 'succeeded') return body.content.video_url;
if (['failed', 'cancelled', 'expired'].includes(body.status)) {
throw new Error(body.error?.message ?? body.status);
}
await new Promise((res) => setTimeout(res, interval));
}
throw new Error('timeout');
}
const taskId = await submit('a calm sunset over rolling hills');
console.log(await wait(taskId));4. The video URL
On succeeded the response includes content.video_url — a direct MP4 link to ByteDance object storage. Two things to know about it:
- It expires after 24 hours. Download or re-host the file inside that window. Don't store the signed URL itself long-term.
- It bypasses the relay. The download is direct client-to-Volcengine; we never see those bytes, so no relay bandwidth or wallet charge applies to fetching the result.
5. Pricing & metering
Video is billed by tokens, the same way the chat models are. The token count is reported in the usage.total_tokens field of the succeeded response. As a rough guide, a 4-second 720p clip is ≈ 87,000 tokens (~¥0.58 at the current resale rate). 1080p and longer clips scale roughly linearly.
Settlement happens at the moment of the first poll that observes succeeded. Subsequent polls are free — the relay idempotently skips re-charging a task it has already settled.
6. Ownership & access
A task is bound to the API key that created it. The video URL is only retrievable by the same key, for 7 days. After 7 days the task itself falls out of the upstream history and the relay returns 404; download anything you need before then.
7. Common errors
| Status | Meaning | Fix |
|---|---|---|
400 | Body missing model or content, or one of them is malformed. | Confirm content is a non-empty array of typed blocks. |
403 (on GET) | You're polling a task created by a different API key. | Use the same key that created the task. If you rotated keys, re-submit the task. |
404 | Task id is unknown to the relay (typo, expired after 7 days, or created against a different deployment). | Resubmit; verify the id matches the one returned at create. |
503 | The Volcengine Ark backend is unavailable, or DOUBAO_API_KEY isn't configured on this deployment. | Retry with backoff; if persistent, check your status page. |
Found something missing or wrong in this doc? Open an issue or ping an admin.