Check your dashboard for the current per-million-token price for each model.
Note: Only successful generations are charged. Tasks that fail due to content policy or upstream errors incur no cost. Billing is reconciled against the actual token count reported by the upstream API on task completion.
content on the response is a single object ({ video_url }), not the part-array format used in the requestcontent field. Don’t reuse your request-parsing code for the response.
To animate a still image instead of generating from text alone, add an image_url part to the content array alongside your text prompt. There are three ways to supply that image, in order of preference:
POST /v1/assets/upload with a multipart/form-data body containing the file and the target model. It returns a hosted assetUrl you pass straight into image_url — your generation request body stays small no matter how large the source photo is.
# Step 1: upload the imagecurl https://oss.chatgpttech.mobi/v1/assets/upload \ -H "Authorization: Bearer sk-hub-your-key-here" \ -F "model=doubao/doubao-seedance-2-0-260128" \ -F "file=@./photo.jpg"# → { "assetUrl": "https://..." }# Step 2: create the task with that URLcurl https://oss.chatgpttech.mobi/v1/generation/tasks \ -H "Authorization: Bearer sk-hub-your-key-here" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao/doubao-seedance-2-0-260128", "content": [ {"type": "text", "text": "Make the scene come alive"}, {"type": "image_url", "image_url": {"url": "ASSET_URL_FROM_STEP_1"}} ], "ratio": "16:9", "resolution": "1080p", "duration": 5 }'
import { readFileSync } from 'node:fs';// Step 1: upload the imageconst form = new FormData();form.append('model', 'doubao/doubao-seedance-2-0-260128');form.append('file', new Blob([readFileSync('./photo.jpg')]), 'photo.jpg');const { assetUrl } = await fetch('https://oss.chatgpttech.mobi/v1/assets/upload', { method: 'POST', headers: { 'Authorization': 'Bearer sk-hub-your-key-here' }, body: form,}).then(r => r.json());// Step 2: create the task with that URLconst task = await fetch('https://oss.chatgpttech.mobi/v1/generation/tasks', { method: 'POST', headers: { 'Authorization': 'Bearer sk-hub-your-key-here', 'Content-Type': 'application/json', }, body: JSON.stringify({ model: 'doubao/doubao-seedance-2-0-260128', content: [ { type: 'text', text: 'Make the scene come alive' }, { type: 'image_url', image_url: { url: assetUrl } }, ], ratio: '16:9', resolution: '1080p', duration: 5, }),}).then(r => r.json());console.log(task.id); // use this to poll, same as text-to-video above
import requests# Step 1: upload the imageupload = requests.post( 'https://oss.chatgpttech.mobi/v1/assets/upload', headers={'Authorization': 'Bearer sk-hub-your-key-here'}, data={'model': 'doubao/doubao-seedance-2-0-260128'}, files={'file': open('./photo.jpg', 'rb')},)asset_url = upload.json()['assetUrl']# Step 2: create the task with that URLres = requests.post( 'https://oss.chatgpttech.mobi/v1/generation/tasks', headers={'Authorization': 'Bearer sk-hub-your-key-here'}, json={ 'model': 'doubao/doubao-seedance-2-0-260128', 'content': [ {'type': 'text', 'text': 'Make the scene come alive'}, {'type': 'image_url', 'image_url': {'url': asset_url}}, ], 'ratio': '16:9', 'resolution': '1080p', 'duration': 5, })task = res.json()print(task['id']) # use this to poll, same as text-to-video above
Accepted file types: image/png, image/jpeg, image/webp (max 10 MB) and video/mp4 (max 50 MB, for video-editing tasks).
Base64 data URIs count toward the request body size. Large source photos (multi-megapixel phone camera shots) can produce multi-megabyte payloads — downscale to around 1024px on the long edge before encoding, or use Option 1 instead, if you hit a 413 Request Entity Too Large.
Whichever option you use, poll /v1/generation/tasks/{id} exactly as shown above — the response shape and polling logic are identical regardless of how the image was supplied.