> ## 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.

# Text-to-video

> Generate a video clip from a text prompt with LTX. The clip is in the response.

# Text-to-video

Send a text prompt, and the response contains the generated video clip. The request is synchronous. There is no task to poll. A clip is 1 to 8 seconds long, at 1080p or 720p, with an optional soundtrack.

## Models

| Model | Model ID | Use it for |
| - | - | - |
| LTX 2.5 | `nunchux-ltx-2.5-video` | New work. The newer release, with the same controls as LTX 2.3. |
| LTX 2.3 | `nunchux-ltx-2.3-video` | Continuity with clips that you already made with LTX 2.3. |

Both models also serve [Image-to-video](/nunchux-optimized/image-to-video). See [Choosing a model](/nunchux-optimized/overview#choosing-a-model).

## Endpoint

```text theme={"system"}
POST https://api.nunchux.ai/v1/video/generations
```

The path `/v1/videos/generations` is an alias. It gives the same result and the same billing.

## Request

Every request must include your API key and a JSON content type. See [Authentication](/authentication).

<ParamField header="X-API-Key" type="string" required>
  Your API key.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

<ParamField body="model" type="string" required>
  The model that generates the clip.

  Options: `nunchux-ltx-2.5-video`, `nunchux-ltx-2.3-video`
</ParamField>

<ParamField body="prompt" type="string" required>
  A text description of the clip. Describe the shot: the subject, the camera movement, the light, and what changes during the clip.
</ParamField>

<ParamField body="width" type="integer" default={1920}>
  Output width in pixels. Send it together with `height`. The size selects the tier, 1080p or 720p. 1080p costs more per second.

  Options: `1920` (1080p), `1280` (720p)
</ParamField>

<ParamField body="height" type="integer" default={1080}>
  Output height in pixels. Send it together with `width`.

  Options: `1080` (1080p), `720` (720p)
</ParamField>

<ParamField body="duration" type="number" default={6}>
  The clip length in seconds. Range: 1 to 8. The model renders at 24 frames per second, so the length of the clip can differ from your value by a fraction of a second. The response gives the length of the clip.
</ParamField>

<ParamField body="extra" type="object">
  Model options.

  <Expandable title="properties">
    <ParamField body="audio" type="boolean" default={false}>
      When `true`, the model generates a soundtrack with the picture. Describe the sound in the prompt.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="seed" type="integer">
  The random seed. When you do not send a seed, the model selects one and returns it in the response. The same seed with the same parameters gives the same result.
</ParamField>

<ParamField body="n" type="integer" default={1}>
  The number of clips to generate. Only 1 is supported.
</ParamField>

<ParamField body="response_format" type="string" default="b64_json">
  The format of the returned clip.

  Options: `b64_json`, `url`
</ParamField>

<Note>
  Unsupported fields

  LTX does not accept `steps`, `guidance_scale`, `negative_prompt` or `num_frames`. A request that contains one of them returns a 400.
</Note>

A minimal request body:

```json theme={"system"}
{
  "model": "nunchux-ltx-2.3-video",
  "prompt": "A golden retriever running on a beach at sunset, cinematic",
  "width": 1280,
  "height": 720,
  "duration": 6,
  "extra": { "audio": false },
  "response_format": "b64_json"
}
```

## Response

The response is JSON. The `data` array contains one MP4 clip, as base64 data or as a URL.

<ResponseField name="created" type="integer" required>
  The Unix timestamp of the generation.
</ResponseField>

<ResponseField name="model" type="string" required>
  The model that generated the clip.
</ResponseField>

<ResponseField name="tier" type="string" required>
  Always `radical_speed`. This field does not show the LTX tier. The `width` and `height` of the clip show it.
</ResponseField>

<ResponseField name="seed" type="integer" required>
  The seed of the clip. Send it again to get the same result.
</ResponseField>

<ResponseField name="data" type="array" required>
  The generated clip.

  <Expandable title="Item properties">
    <ResponseField name="b64_json" type="string">
      The MP4 clip as base64 data. Returned when `response_format` is `b64_json`.
    </ResponseField>

    <ResponseField name="url" type="string">
      The URL of the MP4 clip. Returned when `response_format` is `url`.
    </ResponseField>

    <ResponseField name="width" type="integer">
      The width of the clip in pixels.
    </ResponseField>

    <ResponseField name="height" type="integer">
      The height of the clip in pixels.
    </ResponseField>

    <ResponseField name="duration" type="number">
      The length of the clip in seconds.
    </ResponseField>

    <ResponseField name="frames" type="integer">
      The number of frames in the clip.
    </ResponseField>

    <ResponseField name="frame_rate" type="integer">
      The frame rate of the clip.
    </ResponseField>
  </Expandable>
</ResponseField>

```json theme={"system"}
{
  "created": 1234567890,
  "model": "nunchux-ltx-2.3-video",
  "tier": "radical_speed",
  "seed": 1873420651,
  "data": [
    {
      "b64_json": "AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQ...",
      "width": 1280,
      "height": 720,
      "duration": 6.0417,
      "frames": 145,
      "frame_rate": 24
    }
  ]
}
```

A base64 clip is large. A 6 second 1080p clip can be more than 10 MB of base64 data. Use `response_format: "url"` when you do not need the data in the response.

## Example

A clip takes some time to render. Set the timeout of your HTTP client to 120 seconds.

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST https://api.nunchux.ai/v1/video/generations \
    --max-time 120 \
    -H "Content-Type: application/json" \
    -H "X-API-Key: $NUNCHUX_API_KEY" \
    -d '{
      "model": "nunchux-ltx-2.3-video",
      "prompt": "A golden retriever running on a beach at sunset, cinematic",
      "width": 1280,
      "height": 720,
      "duration": 6,
      "extra": {"audio": false},
      "response_format": "b64_json"
    }' | jq -r '.data[0].b64_json // error(.error.message // .detail // tostring)' | base64 -d > output.mp4
  ```

  ```python Python theme={"system"}
  import os
  import base64
  import requests

  response = requests.post(
      "https://api.nunchux.ai/v1/video/generations",
      headers={
          "Content-Type": "application/json",
          "X-API-Key": os.environ["NUNCHUX_API_KEY"],
      },
      json={
          "model": "nunchux-ltx-2.3-video",
          "prompt": "A golden retriever running on a beach at sunset, cinematic",
          "width": 1280,
          "height": 720,
          "duration": 6,
          "extra": {"audio": False},
          "response_format": "b64_json",
      },
      timeout=120,
  )
  response.raise_for_status()

  # Decode the base64 clip and save it. Change output.mp4 to any file name.
  clip_b64 = response.json()["data"][0]["b64_json"]
  with open("output.mp4", "wb") as f:
      f.write(base64.b64decode(clip_b64))
  ```

  ```javascript JavaScript theme={"system"}
  import { writeFile } from "node:fs/promises";

  const response = await fetch("https://api.nunchux.ai/v1/video/generations", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": process.env.NUNCHUX_API_KEY,
    },
    body: JSON.stringify({
      model: "nunchux-ltx-2.3-video",
      prompt: "A golden retriever running on a beach at sunset, cinematic",
      width: 1280,
      height: 720,
      duration: 6,
      extra: { audio: false },
      response_format: "b64_json",
    }),
    signal: AbortSignal.timeout(120_000),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  const data = await response.json();

  // Decode the base64 clip and save it. Change output.mp4 to any file name.
  await writeFile("output.mp4", Buffer.from(data.data[0].b64_json, "base64"));
  ```
</CodeGroup>

## Tips

* Write the shot, not only the subject. Include the camera movement, the light, and what changes during the clip.
* When `extra.audio` is `true`, describe the sound in the prompt. For example: rain on a window, a crowd, or one piano line.
* Start at 1080p and 6 seconds. Use 720p for faster and lower-cost previews.
* Keep the `seed` when you like a result. Then change one parameter at a time.

## Errors and limits

| Status | Code | Cause |
| - | - | - |
| 400 | none | `model` is missing or not valid, or a size value is not valid. The body is a plain `detail` string. |
| 400 | `invalid_request` | The request sets `tier` to a value other than `radical_speed`. On LTX, `width` and `height` select the tier. Do not send the `tier` field. |
| 400 | `unsupported_response_format` | `response_format` is not `b64_json` or `url`. |
| 400 | `engine_error` | The model cannot render the request, for example a size, a duration or a field that it does not accept. |
| 422 | `request_too_large` | The requested size is larger than 1920x1080. `details` gives the requested value and the maximum. |
| 502 | `output_too_large` | The clip is too large to return. You are not charged. Request a shorter clip or a smaller size. |
| 504 | none | The clip did not complete in 120 seconds. Send the request again. |

A clip is billed per second of output video, at the rate of its tier: 1080p costs more per second than 720p. See the [pricing page](https://nunchux.ai/pricing) for current rates. Nunchux charges only for a successful clip. A request that fails is never billed.

Every request counts toward the requests-per-minute cap of your plan. A request also holds one simultaneous-jobs slot until the response returns. See [Rate limits](/rate-limits). For authentication and credit errors, see [Error codes](/errors).


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