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

# On-Demand Rendering

> Render a video in one API call -- no preprocessing required

Send a character image and driving video directly. No preprocessing, no stored assets -- one API call to start rendering.

## Prerequisites

* A Viggle API key ([get one here](https://portal.viggle.ai/keys))
* A reference image (PNG or JPG)
* A driving video (MP4)

## Workflow

<Steps>
  <Step title="Render" icon="upload">
    Upload `ref_image` and `driving_video` to start a job. **1 credit/sec** of output.

    <CodeGroup>
      ```python Python theme={null}
      import requests
      API_KEY, BASE = "YOUR_API_KEY", "https://apis.viggle.ai"
      headers = {"Authorization": f"Bearer {API_KEY}"}

      job = requests.post(f"{BASE}/api/render", headers=headers, files={
          "ref_image": open("character.png", "rb"),
          "driving_video": open("dance.mp4", "rb"),
      }).json()
      print(f"Job ID: {job['job_id']}")
      ```

      ```javascript JavaScript theme={null}
      const API_KEY = "YOUR_API_KEY", BASE = "https://apis.viggle.ai";
      const headers = { Authorization: `Bearer ${API_KEY}` };
      const form = new FormData();
      form.append("ref_image", imageFile);
      form.append("driving_video", videoFile);
      const job = await fetch(`${BASE}/api/render`, {
        method: "POST", headers, body: form
      }).then(r => r.json());
      ```

      ```bash cURL theme={null}
      curl -X POST "https://apis.viggle.ai/api/render" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -F "ref_image=@character.png" -F "driving_video=@dance.mp4"
      ```
    </CodeGroup>
  </Step>

  <Step title="Poll for completion" icon="clock">
    Poll every 3-5 seconds. No auth required for the status endpoint.

    <CodeGroup>
      ```python Python theme={null}
      import time
      while True:
          status = requests.get(f"{BASE}/api/render/{job['job_id']}").json()
          if status["status"] == "complete": break
          elif status["status"] == "failed": raise Exception(status.get("error_message"))
          time.sleep(3)
      ```

      ```javascript JavaScript theme={null}
      const sleep = (ms) => new Promise(r => setTimeout(r, ms));
      while (true) {
        const s = await fetch(`${BASE}/api/render/${job.job_id}`).then(r => r.json());
        if (s.status === "complete") break;
        if (s.status === "failed") throw new Error(s.error_message);
        await sleep(3000);
      }
      ```

      ```bash cURL theme={null}
      # Poll until status is "complete" (no auth required)
      curl "https://apis.viggle.ai/api/render/JOB_ID"
      # Repeat every 5 seconds until: "status": "complete"
      ```
    </CodeGroup>
  </Step>

  <Step title="Download" icon="download">
    When the job is complete, use the `cdn_url` from the status response to download the video.

    <CodeGroup>
      ```python Python theme={null}
      cdn_url = status["cdn_url"]
      video = requests.get(cdn_url)
      open("output.mp4", "wb").write(video.content)
      ```

      ```javascript JavaScript theme={null}
      const cdnUrl = s.cdn_url;
      const video = await fetch(cdnUrl);
      require("fs").writeFileSync("output.mp4", Buffer.from(await video.arrayBuffer()));
      ```

      ```bash cURL theme={null}
      # Use the cdn_url value from the status response
      curl -o output.mp4 "CDN_URL_FROM_STATUS_RESPONSE"
      ```
    </CodeGroup>
  </Step>
</Steps>

## Render options

Pass additional fields alongside `ref_image` and `driving_video`:

<CodeGroup>
  ```python Python theme={null}
  job = requests.post(f"{BASE}/api/render", headers=headers, files={
      "ref_image": open("character.png", "rb"),
      "driving_video": open("dance.mp4", "rb"),
  }, data={
      "background_mode": "solid",
      "bg_color": "0,255,0",
  }).json()
  ```

  ```javascript JavaScript theme={null}
  const form = new FormData();
  form.append("ref_image", imageFile);
  form.append("driving_video", videoFile);
  form.append("background_mode", "solid");
  form.append("bg_color", "0,255,0");

  const job = await fetch(`${BASE}/api/render`, {
    method: "POST", headers, body: form
  }).then(r => r.json());
  ```

  ```bash cURL theme={null}
  curl -X POST "https://apis.viggle.ai/api/render" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "ref_image=@character.png" -F "driving_video=@dance.mp4" \
    -F "background_mode=solid" -F "bg_color=0,255,0"
  ```
</CodeGroup>

See [Render Options](/render-options) for all available parameters.

### Selecting a model

`V3_Preview` is the default. Pass `model=V4_Preview` to use the V4 model:

```bash theme={null}
curl -X POST "https://apis.viggle.ai/api/render" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "ref_image=@character.png" -F "driving_video=@dance.mp4" \
  -F "model=V4_Preview"
```

### Using URL inputs

Pass URLs instead of uploading files. The server fetches the content:

```bash theme={null}
curl -X POST "https://apis.viggle.ai/api/render" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d "ref_image_url=https://example.com/character.png" \
  -d "driving_video_url=https://example.com/dance.mp4"
```

## On-Demand vs Preprocessing

|              | On-Demand                    | Preprocessing                  |
| ------------ | ---------------------------- | ------------------------------ |
| **Setup**    | None -- send files directly  | Create character + scene first |
| **Speed**    | Includes preprocessing time  | \~3x faster                    |
| **Reuse**    | No stored assets             | Reuse across renders           |
| **Best for** | One-off renders, prototyping | Production, repeated renders   |

## What's next?

<CardGroup cols={2}>
  <Card title="Preprocessing Guide" icon="rocket" href="/guides/quickstart">
    Pre-create assets for 3x faster renders
  </Card>

  <Card title="Import Templates" icon="download" href="/guides/import-templates">
    Use pre-made templates from viggle.ai
  </Card>

  <Card title="Render Options" icon="gauge" href="/render-options">
    Background mode and more
  </Card>
</CardGroup>
