Submit a video, poll it, fetch the clip, or be told by a webhook — and list the video models.
A video is answered later: you submit it, then poll it — or name a callback_url and be told — until it ends. It is billed once, at delivery, on the seconds produced. The full guide is Video Generation.
| Method | Path | Billed | Does |
|---|---|---|---|
POST | /v1/videos | at delivery | submits a video |
GET | /v1/videos/{id} | free | polls it |
GET | /v1/videos/{id}/content | free | the clip's bytes |
GET | /v1/videos/models | free | lists the video models on sale |
Every route takes an API key: Authorization: Bearer $OPENDUNES_API_KEY.
curl https://opendunes.com/v1/videos \
-H "Authorization: Bearer $OPENDUNES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/veo-3.1-fast",
"prompt": "A sunrise over sand dunes, aerial shot",
"duration": 8,
"callback_url": "https://soukbot.example/hooks/video"
}'| Field | Description |
|---|---|
model | Required |
prompt | Required, unless a start picture (frame_images or input_references) is the whole input |
duration | Seconds of video |
aspect_ratio · resolution · size · seed · generate_audio · frame_images · input_references … | The model's own parameters, sent as you wrote them |
callback_url | Optional. A public HTTPS address to be told when the video ends |
The answer is 202:
{
"id": "a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d",
"polling_url": "https://opendunes.com/v1/videos/a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d",
"status": "pending"
}A model that is not a video model is a 404, and nothing is submitted.
curl https://opendunes.com/v1/videos/a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d \
-H "Authorization: Bearer $OPENDUNES_API_KEY"status is pending, in_progress, then one of completed, failed, cancelled or expired. Polling is free at any balance. Once completed:
{
"id": "a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d",
"generation_id": "gen-3bhGkxlo4XFrqiabUM7NDtwDzWwG",
"polling_url": "https://opendunes.com/v1/videos/a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d",
"status": "completed",
"unsigned_urls": ["https://opendunes.com/v1/videos/a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d/content?index=0"],
"file_ids": ["a2d16fe6-c19c-4f85-8759-937da517410d"],
"usage": { "cost": 129.6, "is_byok": false }
}usage.cost is the charge in DA. A failed video carries the provider's reason in error; failed, cancelled and expired videos charge nothing.
GET /v1/videos/{id}/content answers the clip's bytes, with your API key. The clip is also in Files under its file_id.
With a callback_url, the video's end is sent there as a POST, on every end: completed, failed, cancelled or expired.
{
"type": "video.generation.completed",
"created_at": "2026-09-24T09:20:11.482Z",
"data": {
"id": "a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d",
"status": "completed",
"generation_id": "gen-3bhGkxlo4XFrqiabUM7NDtwDzWwG",
"model": "google/veo-3.1-fast",
"unsigned_urls": ["https://opendunes.com/v1/videos/a2d19c12-4b5e-4d7a-9c10-2f3e4a5b6c7d/content?index=0"],
"usage": { "cost": 129.6, "is_byok": false }
}
}X-OpenDunes-Idempotency-Key: <id>-<status> — the same value on every retry of the same event.2xx. Anything else is retried — six tries in all, the waits growing from 10 seconds to 2 hours.GET /v1/videos/models answers { "data": [ … ] } with the model object, and after it what each model takes:
| Field | Description |
|---|---|
supported_durations | The lengths it makes, in seconds — the values duration takes |
supported_resolutions · supported_aspect_ratios · supported_sizes | The values resolution, aspect_ratio and size take |
supported_frame_images | first_frame, last_frame, or both: the frames frame_images takes. null when it takes none |
generate_audio · seed | Whether it makes sound, and whether it takes a seed |
upscale_factor · creativity | For an upscaling model, the range and the levels it takes |
allowed_passthrough_parameters | The provider's own parameters it takes, sent under provider.options |
pricing_skus | Each price in DA, per its unit. A price quoted in cents upstream loses cents_ from its name |
null means the model publishes no list for that field.
{
"id": "google/veo-3.1",
"supported_durations": [4, 6, 8],
"supported_resolutions": ["720p", "1080p", "4K"],
"supported_aspect_ratios": ["16:9", "9:16"],
"supported_frame_images": ["first_frame", "last_frame"],
"generate_audio": true,
"seed": true,
"pricing_skus": {
"duration_seconds_with_audio": "83.2",
"duration_seconds_without_audio": "41.6"
},
"allowed_passthrough_parameters": ["personGeneration", "negativePrompt", "enhancePrompt"]
}