> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nunchux.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Authenticate every request with your API key in the `X-API-Key` header or `Authorization: Bearer`. Keys start with `sk-nunchux-`.
> Image generation is synchronous: the image is in the response.
> Video generation is asynchronous: submit the job, poll its task until it reaches a terminal status, then download the output promptly, because output URLs expire.

# Vidu Q4

> Vidu Q4 preview for image-to-video and reference-to-video, with sound generated with the picture, through one asynchronous route family.

# Vidu Q4

Vidu Q4 preview is Vidu's newest video model. It animates a still image, or it carries the subjects of up to 15 reference images into a new scene. It generates dialogue and sound effects with the picture, and it can switch camera angles within one clip. Each task has its own submit path. Generation is asynchronous. You submit a task, poll it until the state is terminal, then download the clip. The route mirrors Vidu's own API, so the request and response shapes are the provider's.

> [!NOTE]
>
> Best for
>
> * Animating a still. The image becomes the first frame, and the prompt drives the motion.
> * Consistent subjects. Up to 15 reference images keep characters and objects the same across the clip.
> * Clips with sound. Speech and sound effects come out with the video in one request.
> * Resolution range. From 540p drafts to 4K finals.

## Models

| Model | Model ID | Tasks | Resolution | Duration | Images |
| - | - | - | - | - | - |
| Vidu Q4 preview | `viduq4-preview` | i2v, r2v | 540p, 720p, 1080p, 2K, 4K | 3 to 16 s | 1 (i2v), 1 to 15 (r2v) |

Durations are whole seconds. Both tasks send `viduq4-preview` as the `model` value. Text-to-video is not offered on this model: a submit to `text2video` returns 400.

| Task | Path | Images | Prompt | Aspect ratio |
| - | - | - | - | - |
| Image-to-video | `img2video` | Exactly 1 | Optional | Follows the image |
| Reference-to-video | `reference2video` | 1 to 15 | Required | `16:9` (default), `9:16`, `1:1`, `4:3` or `3:4` |

## Endpoints

| Step | Route |
| - | - |
| Submit | `POST /v1/vidu/video/{capability}` |
| Poll | `GET /v1/vidu/video/{task_id}` |

`{capability}` is `img2video` or `reference2video`. The poll path is the same for both.

## Request

Send your API key in the `X-API-Key` header. See [Authentication](https://docs.nunchux.ai/authentication.md). Set `Content-Type: application/json`. The examples also send a `User-Agent` header that names your application.

The route accepts only the fields below. A body with any other top-level field, such as Vidu's `sounds` or `callback_url`, returns 400 before any charge.

**model** `string` *required*

The model ID: `viduq4-preview`.

**images** `string[]` *required*

The images. On `img2video`, exactly one: the first frame. On `reference2video`, 1 to 15 reference images. Each item is a public `https` URL or a base64 `data:image/...` URI. Vidu takes PNG, JPEG, JPG and WEBP up to 50 MB each. The whole request body must stay under 20 MB, so pass large images by URL.

**prompt** `string`

The scene, up to 20,000 characters. Required on `reference2video`. Optional on `img2video`.

**duration** `integer` (default: 5)

The clip length in whole seconds, 3 to 16. You are billed per second of output.

**resolution** `string` (default: 720p)

The output tier: `540p`, `720p`, `1080p`, `2K` or `4K`. A higher tier costs more per second.

**audio** `boolean` (default: true)

Whether the clip carries generated sound: dialogue and sound effects. It defaults to on, and sound adds a surcharge to the task. Send `false` for a silent clip.

**aspect\_ratio** `string` (default: 16:9)

The output shape on `reference2video`: `16:9`, `9:16`, `1:1`, `4:3` or `3:4`. On `img2video`, the clip follows the image.

**is\_rec** `boolean`

On `img2video`, `true` lets Vidu write its own prompt and ignore yours. When you send a `prompt` and leave `is_rec` out, Nunchux sends `false`, so your prompt is used.

**seed** `integer` (default: 0)

The random seed. `0` picks a random seed.

**payload** `string`

Free text, up to 4,096 characters, that Vidu stores with the task and does not read.

## Response

### Submit

A 200 status means that Vidu accepted the task. It does not mean that the clip is ready.

**task\_id** `string` *required*

The task ID. Poll `GET /v1/vidu/video/{task_id}` with it. It works for 7 days.

**state** `string`

The task state when it was accepted, such as `created`.

### Poll

**state** `string` *required*

`processing` while the task runs. `success` or `failed` when it ends. Poll until you read one of the two terminal values.

**creations\[].url** `string`

The URL of the clip, in `creations[0].url`. Present only when `state` is `success`. The URL is time-limited. Download the clip as soon as the task succeeds. A later poll returns a fresh URL.

**credits** `integer`

The Vidu credits that the task used. Present once the task is billed.

**error** `object`

Present when `state` is `failed`. `error.message` explains why. Read it before you resubmit.

The poll also echoes `model`, `capability`, `duration` and `resolution`.

## Example

Submit, poll, download. The submit path names the task, and the poll path takes the task ID.

```bash cURL theme={"system"}
BASE=https://api.nunchux.com

# 1. Submit
SUBMIT=$(curl -sS --fail-with-body "$BASE/v1/vidu/video/img2video" \
  -H "X-API-Key: $NUNCHUX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "User-Agent: YourApp/1.0" \
  -d '{
    "model": "viduq4-preview",
    "images": ["https://example.com/frame.jpg"],
    "prompt": "the astronaut waves and the camera rises",
    "duration": 5,
    "resolution": "720p",
    "audio": false
  }') || { echo "submit failed: $SUBMIT" >&2; exit 1; }
TASK=$(jq -er '.task_id' <<<"$SUBMIT") || { echo "no task_id in: $SUBMIT" >&2; exit 1; }

# 2. Poll until the state is terminal
DELAY=5
for _ in $(seq 1 90); do
  RESP=$(curl -sS "$BASE/v1/vidu/video/$TASK" \
    -H "X-API-Key: $NUNCHUX_API_KEY" \
    -H "User-Agent: YourApp/1.0")
  STATE=$(jq -r '.state // ""' <<<"$RESP")
  case "$STATE" in success|failed) break ;; esac
  sleep "$DELAY"; DELAY=$(( DELAY * 2 > 30 ? 30 : DELAY * 2 ))
done

# 3. Check for failure
[ "$STATE" = "success" ] || { echo "task $STATE: $(jq -r '.error.message // "no error"' <<<"$RESP")" >&2; exit 1; }

# 4. Download
curl -sSL -o vidu.mp4 "$(jq -r '.creations[0].url' <<<"$RESP")"
```

```python Python theme={"system"}
# pip install requests
import os, time, requests

BASE = "https://api.nunchux.com"
HEADERS = {
    "X-API-Key": os.environ["NUNCHUX_API_KEY"],
    "User-Agent": "YourApp/1.0",
}

# 1. Submit
submit = requests.post(
    f"{BASE}/v1/vidu/video/img2video",
    headers=HEADERS,
    json={
        "model": "viduq4-preview",
        "images": ["https://example.com/frame.jpg"],
        "prompt": "the astronaut waves and the camera rises",
        "duration": 5,
        "resolution": "720p",
        "audio": False,
    },
)
if not submit.ok:
    raise RuntimeError(f"submit failed: HTTP {submit.status_code} {submit.text}")
task = submit.json()["task_id"]

# 2. Poll until the state is terminal
delay = 5
for _ in range(90):
    data = requests.get(f"{BASE}/v1/vidu/video/{task}", headers=HEADERS).json()
    state = data.get("state", "")
    if state in ("success", "failed"):
        break
    time.sleep(delay)
    delay = min(delay * 2, 30)
else:
    raise TimeoutError("Vidu task did not reach a terminal state")

# 3. Check for failure
if state != "success":
    raise RuntimeError(f"Vidu task {state}: {data.get('error')}")

# 4. Download
with requests.get(data["creations"][0]["url"], stream=True, timeout=300) as r:
    r.raise_for_status()
    with open("vidu.mp4", "wb") as f:
        for chunk in r.iter_content(1 << 14):
            f.write(chunk)
```

To carry subjects into a new scene, submit to `/v1/vidu/video/reference2video` instead, with up to 15 images and a prompt. The body below replaces the submit body in the example. It leaves `audio` out, so the clip has sound and the surcharge applies.

```json Reference-to-video theme={"system"}
{
  "model": "viduq4-preview",
  "images": [
    "https://example.com/character.jpg",
    "https://example.com/product.jpg",
    "https://example.com/kitchen.jpg"
  ],
  "prompt": "In the kitchen from the third image, the character from the first image picks up the product from the second image and says, 'This one is my favorite.'",
  "duration": 5,
  "resolution": "720p",
  "aspect_ratio": "9:16"
}
```

## Tips

* Send `audio: false` unless you want sound. Sound is on by default and costs extra on every task.
* Draft at 540p or 720p. 2K and 4K cost several times more per second, so settle the prompt at a lower tier first.
* Name each subject in the prompt by the image that shows it, and keep the reference images consistent in style.
* Poll every 5 seconds at first, then back off to 30 seconds. Polls are free and never use a concurrency slot.

## Errors and limits

A 200 status on submit means that Vidu accepted the task. It does not mean that the task succeeded. A task that fails later ends with `state` set to `failed`, and `error.message` explains why. Read it before you resubmit.

A failed submit returns an HTTP error status. See [Error codes](https://docs.nunchux.ai/errors.md). These request shapes are rejected with 400 before any charge:

| Code | Cause |
| - | - |
| `not_priced` | The path is `text2video` or `start-end2video`. The model does not offer them. |
| `invalid_duration` | The duration is not a whole number from 3 to 16. |
| `invalid_resolution` | The resolution is not `540p`, `720p`, `1080p`, `2K` or `4K`. |
| `invalid_images` | The image count does not fit the task, or an image is not an `https` URL or a base64 `data:image/...` URI. |
| `invalid_prompt` | `reference2video` without a prompt. |
| `invalid_request` | The body carries a field this route does not accept, such as `sounds`. |
| `callback_url_not_supported` | The body carries `callback_url`. Poll the task instead. |
| `off_peak_not_supported` | The body sets `off_peak` to `true`. |

A 5xx status or a timeout on submit does not tell you whether the task was created. Never resubmit blindly. If the submit returned a `task_id`, poll it. If it returned nothing, check your credits before you try again.

Vidu bills per second of output at the chosen resolution. Sound adds a fixed surcharge per task on both image-to-video and reference-to-video. Nunchux holds the charge at submit, with the surcharge unless you sent `audio: false`, and settles it to the credits that Vidu reports when the task ends. A failed task is refunded. See the [pricing page](https://nunchux.ai/pricing) for the rates.

Every submit counts toward the simultaneous-jobs cap of your plan for six minutes, however fast the task finishes. A task that ends sooner does not give its slot back early, so a burst of short tasks can return 429 `concurrent_limit_exceeded`. Wait and retry with backoff. Separately, at most five Vidu tasks per account can be in progress at once. A sixth returns 429 with the code `too_many_open_tasks`. A task stops counting toward the five when Nunchux settles it, which can be a few seconds after the poll first shows a terminal status, so a submit sent at once can still return 429. See [Rate limits](https://docs.nunchux.ai/rate-limits.md), [Credits & pricing](https://docs.nunchux.ai/credits-pricing.md) and the [Partner models overview](https://docs.nunchux.ai/partner-models/overview.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.