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

# 快速入门：Viggle Image

> 用文字描述生成 PNG 图片，可选参考图引导。

用文字描述画面，提交到 `POST /v1/images`，轮询任务直到 `ready`，再下载 PNG。一次请求用 `qwen` 模型生成 1–6 张图，可附带最多 8 张参考图引导生成。

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

## 准备工作

* 在 [Viggle 控制台](https://portal.viggle.ai/keys)创建 API 密钥，并导出为 `VIGGLE_API_KEY`。
* 一段文字描述，最多 4000 个字符。
* 积分：`1K` 每张 **1 积分（\$0.01）**，`2K` 每张 **2 积分（\$0.02）**。下方示例生成两张 `1K` 图片，共 2 积分。

## 生成、轮询并下载

脚本创建一个 16:9、两张图的任务，每 3 秒轮询一次，就绪后依次保存为 `image_0.png`、`image_1.png`。

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

  response=$(curl -s -X POST "https://apis.viggle.ai/v1/images" \
    -H "Authorization: Bearer $VIGGLE_API_KEY" \
    -F prompt="A watercolor fox reading a book under a maple tree" \
    -F aspect_ratio=16:9 \
    -F resolution=1K \
    -F n=2)
  image_id=$(echo "$response" | jq -r '.id')

  # 任务 15 分钟内未完成会自动失败。
  for _ in $(seq 1 300); do
    job=$(curl -s "https://apis.viggle.ai/v1/images/$image_id" \
      -H "Authorization: Bearer $VIGGLE_API_KEY")
    status=$(echo "$job" | jq -r '.status')

    case "$status" in
      ready)
        i=0
        for url in $(echo "$job" | jq -r '.images[].url'); do
          curl -s -o "image_$i.png" "$url"
          echo "saved image_$i.png"
          i=$((i + 1))
        done
        exit 0
        ;;
      failed)
        echo "Image job failed: $(echo "$job" | jq -c '.error')" >&2
        exit 1
        ;;
    esac
    sleep 3
  done

  echo "Timed out waiting for image job $image_id" >&2
  exit 1
  ```

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

  const baseUrl = "https://apis.viggle.ai/v1";
  const headers = { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` };

  async function api(path, init = {}) {
    const response = await fetch(`${baseUrl}${path}`, { ...init, headers });
    const body = await response.json().catch(() => ({}));
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${JSON.stringify(body.error ?? body)}`);
    }
    return body;
  }

  const form = new FormData();
  form.append("prompt", "A watercolor fox reading a book under a maple tree");
  form.append("aspect_ratio", "16:9");
  form.append("resolution", "1K");
  form.append("n", "2");

  let job = await api("/images", { method: "POST", body: form });

  // 任务 15 分钟内未完成会自动失败。
  while (job.status === "queued" || job.status === "processing") {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    job = await api(`/images/${job.id}`);
  }

  if (job.status !== "ready") {
    throw new Error(`Image job failed: ${JSON.stringify(job.error)}`);
  }

  for (const [i, image] of job.images.entries()) {
    const png = await fetch(image.url);
    await writeFile(`image_${i}.png`, Buffer.from(await png.arrayBuffer()));
    console.log(`saved image_${i}.png (${image.width}x${image.height}, seed ${image.seed})`);
  }
  ```

  ```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']}"}

  fields = {
      "prompt": "A watercolor fox reading a book under a maple tree",
      "aspect_ratio": "16:9",
      "resolution": "1K",
      "n": "2",
  }
  # 通过 files= 让 requests 以 multipart/form-data 发送，接口要求这种格式。
  response = requests.post(
      f"{base_url}/images",
      headers=headers,
      files={name: (None, value) for name, value in fields.items()},
  )
  response.raise_for_status()
  job = response.json()

  # 任务 15 分钟内未完成会自动失败。
  while job["status"] in ("queued", "processing"):
      time.sleep(3)
      response = requests.get(f"{base_url}/images/{job['id']}", headers=headers)
      response.raise_for_status()
      job = response.json()

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

  for i, image in enumerate(job["images"]):
      png = requests.get(image["url"])
      png.raise_for_status()
      with open(f"image_{i}.png", "wb") as f:
          f.write(png.content)
      print(f"saved image_{i}.png ({image['width']}x{image['height']}, seed {image['seed']})")
  ```

  ```typescript @viggle/sdk theme={null}
  import { Viggle } from "@viggle/sdk";

  const viggle = new Viggle({ apiKey: process.env.VIGGLE_API_KEY! });

  let job = await viggle.images.create({
    prompt: "A watercolor fox reading a book under a maple tree",
    aspectRatio: "16:9",
    resolution: "1K",
    n: 2,
  });
  while (job.status === "queued" || job.status === "processing") {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    job = await viggle.images.get(job.id);
  }
  console.log(job.images.map((image) => image.url));
  ```
</CodeGroup>

`@viggle/sdk` 示例需使用包含 `images` 资源的 SDK 版本。

## 响应

创建请求立即返回 `queued` 状态的任务：

```json theme={null}
{
  "id": "img_b16a190d-2d44-41a0-8056-ef68183c3b9e",
  "status": "queued",
  "model": "qwen",
  "progress": 0,
  "images": [],
  "created_at": "2026-10-12T08:20:00Z",
  "completed_at": null,
  "error": null
}
```

轮询 `GET /v1/images/{image_id}`，直到 `status` 为 `ready` 或 `failed`。`processing` 期间 `progress` 随每张完成而增加。`ready` 后 `images` 按请求顺序列出全部 `n` 张，每张包含 `url`、实际尺寸 `width` / `height`（像素）和实际使用的 `seed`。生产环境下 `1K` 两张约需 17–22 秒。

每个 `url` 都是签名链接，**1 小时**内有效，过期后再查询一次任务即可拿到新链接。生成的图片在任务结束 **24 小时**后删除，请在此之前下载。

## 添加参考图

用 `reference_image` 上传文件，用 `reference_image_url` 传公开的 `http(s)` 地址，两者可以混用，字段可以重复，合计最多 8 张：

```bash theme={null}
curl -X POST https://apis.viggle.ai/v1/images \
  -H "Authorization: Bearer $VIGGLE_API_KEY" \
  -F prompt="The same fox, now in winter, snow falling" \
  -F aspect_ratio=1:1 \
  -F resolution=2K \
  -F reference_image=@fox.png \
  -F reference_image_url=https://example.com/style.jpg
```

轮询和下载方式不变。带参考图时 `2K` 会变成较小的一档：本例为 1536 × 1536，而非文生图的 2048 × 2048，价格不变。`2K` 带参考图一张约需 37 秒。单张参考图不超过 24 MiB，支持的格式和其他限制见[生成图片](/zh/v1/api-reference/images/create#reference-image-limits)。

## 任务失败时

查看 `error.code`：`SERVICE_BUSY` 和 `TASK_FAILED` 可重试，用同样的参数重新创建即可；`INVALID_REQUEST` 表示模型拒绝了请求，例如参考图无法使用，需先调整输入。失败的任务全额退回积分。详见[查询图片](/zh/v1/api-reference/images/get#status-and-errors)。

<CardGroup cols={2}>
  <Card title="生成图片" icon="image" href="/zh/v1/api-reference/images/create">
    全部字段、输出尺寸和限制。
  </Card>

  <Card title="查询图片" icon="magnifying-glass" href="/zh/v1/api-reference/images/get">
    完整的 Image 对象、状态和错误码。
  </Card>
</CardGroup>


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