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

# 快速入门：文本生成视频

> 仅通过文本提示词生成 MiniMax H3 视频。

用文字描述镜头，提交到 `POST /v1/videos`，再轮询视频直至就绪。本流程无需源图片或视频，使用 **H3 视频生成**的文本模式，即经 Viggle 优化的 MiniMax H3 能力。角色加动作的[视频重混](/zh/v1/guides/quickstart-mix)使用另一套流程。所有生成视频均自带**原生音频**。

设置 `VIGGLE_API_KEY` 后即可运行下方示例，无需其他输入素材。

## 准备工作

* 在 [Viggle 控制台](https://portal.viggle.ai/keys)创建 API 密钥，并导出为 `VIGGLE_API_KEY`。
* 非空文本提示词。
* `quality` 选项：`low` 生成更快，适合迭代；`high` 提供更高的保真度。两者均按 **\$0.01/秒**计费。

## 生成并轮询视频

<CodeGroup>
  ```bash cURL theme={null}
  #!/usr/bin/env bash
  set -euo pipefail

  response=$(curl -s "https://apis.viggle.ai/v1/videos" \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F prompt="a paper airplane gliding through a sunlit office" \
    -F quality=low \
    -F duration_s=5)
  video_id=$(echo "$response" | jq -r '.id')

  for _ in $(seq 1 60); do
    video=$(curl -s "https://apis.viggle.ai/v1/videos/$video_id" \
      -H "Authorization: Bearer $VIGGLE_API_KEY")
    status=$(echo "$video" | jq -r '.status')

    case "$status" in
      ready)
        echo "$video" | jq -r '.video_url'
        exit 0
        ;;
      failed|cancelled)
        echo "Video $status: $(echo "$video" | jq -c '.error')" >&2
        exit 1
        ;;
    esac
    sleep 5
  done

  echo "Timed out waiting for video $video_id" >&2
  exit 1
  ```

  ```javascript Node theme={null}
  const baseUrl = "https://apis.viggle.ai/v1";
  const headers = { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` };
  const TERMINAL = new Set(["ready", "failed", "cancelled"]);

  async function createVideo() {
    const form = new FormData();
    form.append("prompt", "a paper airplane gliding through a sunlit office");
    form.append("quality", "low");
    form.append("duration_s", "5");

    const response = await fetch(`${baseUrl}/videos`, {
      method: "POST",
      headers,
      body: form,
    });
    if (!response.ok) {
      const body = await response.json().catch(() => ({}));
      throw new Error(`HTTP ${response.status}: ${JSON.stringify(body.error ?? body)}`);
    }
    return response.json();
  }

  async function waitForVideo(id) {
    for (let attempt = 0; attempt < 60; attempt++) {
      const response = await fetch(`${baseUrl}/videos/${id}`, { headers });
      if (!response.ok) {
        const body = await response.json().catch(() => ({}));
        throw new Error(`HTTP ${response.status}: ${JSON.stringify(body.error ?? body)}`);
      }
      const video = await response.json();
      if (TERMINAL.has(video.status)) return video;
      await new Promise((resolve) => setTimeout(resolve, 5000));
    }
    throw new Error(`Timed out waiting for video ${id}`);
  }

  const created = await createVideo();
  const video = await waitForVideo(created.id);

  if (video.status !== "ready") {
    throw new Error(`Video ${video.status}: ${JSON.stringify(video.error)}`);
  }
  console.log(video.video_url);
  ```

  ```python Python theme={null}
  import os
  import time
  import requests

  base_url = "https://apis.viggle.ai/v1"
  headers = {"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"}
  TERMINAL_STATES = {"ready", "failed", "cancelled"}

  response = requests.post(
      f"{base_url}/videos",
      headers=headers,
      data={
          "prompt": "a paper airplane gliding through a sunlit office",
          "quality": "low",
          "duration_s": 5,
      },
  )
  response.raise_for_status()
  video = response.json()

  for _ in range(60):
      if video["status"] in TERMINAL_STATES:
          break
      time.sleep(5)
      response = requests.get(f"{base_url}/videos/{video['id']}", headers=headers)
      response.raise_for_status()
      video = response.json()
  else:
      raise TimeoutError(f"Timed out waiting for video {video['id']}")

  if video["status"] != "ready":
      error = video.get("error") or {}
      raise RuntimeError(f"{video['status']}: {error.get('code')} {error.get('message')}")

  print(video["video_url"])
  ```
</CodeGroup>

## 响应

```json theme={null}
{
  "id": "vid_3f2a9c",
  "status": "queued",
  "progress": null,
  "created_at": "2026-08-24T09:12:03Z"
}
```

创建请求立即返回 `status: "queued"`，此时还没有 `video_url`，仅表示请求已受理。每 3–5 秒轮询 `GET /v1/videos/{video_id}`，直到 `status` 为 `ready`、`failed` 或 `cancelled`。就绪后读取 `video_url`：签名链接有效期为 1 小时，每次读取都会重新签名。`duration_s`（3–15，默认 `5`）、`resolution`（默认 `768p`）和 `aspect_ratio`（默认 `16:9`）均可省略；`prompt` 和 `quality` 必填。

费用见[计费与保留期限](/zh/v1/pricing#h3-video)：H3 视频按生成时长收取 \$0.01/秒，与 `quality`、`resolution` 和 `aspect_ratio` 无关。

<Card title="从文本生成视频" icon="text" href="/zh/v1/api-reference/videos/create-from-text">
  查看全部字段和完整响应结构。
</Card>
