> For the complete documentation index, see [llms.txt](https://bountyv.gitbook.io/vdocs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bountyv.gitbook.io/vdocs/image-api/images-img2video.md).

# Image-to-Video

Animate a still with self-hosted Wan 2.2 (or the external Seedance 2.0 engine, SFW-only). Asynchronous — submit returns a `generation_id` immediately and charges up front; poll the dedicated status endpoint until the clip is ready.

```
POST /api/v1/images/img2video
```

Async submit + poll (see [Polling](#polling) below). Failed jobs are refunded automatically.

## Authentication

```
Authorization: Bearer sk_live_...
```

See [Authentication](/vdocs/authentication.md).

## Request

**Content-Type:** `multipart/form-data` — see [Uploading files](/vdocs/uploading-files.md).

| Field             | Type    | Required | Default  | Description                                                                                                                                             |
| ----------------- | ------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image`           | file    | ✅        | —        | The still to animate. **Repeatable on `seedance`** (or `image[]`) — up to 9 references. See [Multiple reference images](#multiple-reference-images)     |
| `prompt`          | string  | ✅        | —        | Motion description                                                                                                                                      |
| `mode`            | string  | —        | `"nsfw"` | `"sfw"` gates on the `video` product and rejects explicit prompts; `"nsfw"` gates on `nsfw`. `seedance` is always treated as SFW — it has no adult lane |
| `engine`          | string  | —        | `"wan"`  | `"wan"` (self-hosted Wan 2.2) or `"seedance"` (external BytePlus ModelArk Dreamina Seedance 2.0, SFW-only)                                              |
| `tier`            | string  | —        | `"pro"`  | `seedance` only — `"pro"` \| `"fast"`                                                                                                                   |
| `duration`        | integer | —        | `5`      | Seconds, `2`–`8`                                                                                                                                        |
| `ratio`           | string  | —        | `"9:16"` | `"9:16"` \| `"16:9"` \| `"1:1"`                                                                                                                         |
| `workflow`        | string  | —        | —        | `"subwan"` enables the act-LoRA lane (`wan` only)                                                                                                       |
| `act`             | string  | —        | —        | SubWan act key (`subwan`/`wan` only)                                                                                                                    |
| `negative_prompt` | string  | —        | —        | Extra negatives (`wan` only)                                                                                                                            |
| `seed`            | integer | —        | —        | Reproducibility (`wan` only)                                                                                                                            |
| `steps`           | integer | —        | —        | Diffusion steps (`wan` only)                                                                                                                            |

### Multiple reference images

Seedance 2.0 builds a clip from several stills at once — extra angles of a subject, a wardrobe piece, a location. Repeat the `image` field (or use `image[]`); order is preserved and the prompt addresses them as "Image 1", "Image 2", and so on.

| Engine                             | Max `image` files |
| ---------------------------------- | ----------------- |
| `seedance` (BytePlus Seedance 2.0) | 9                 |
| `wan` (self-hosted Wan 2.2)        | 1                 |

Seedance also caps reference *videos* and *audio* at 3 each, with 12 files total across all three; those inputs aren't exposed on this endpoint yet. Sending more images than the engine accepts returns `400` with a `max_reference_images` field.

### Lanes

Text-driven i2v by default. Pass `workflow=subwan` with an `act` key to use the act-LoRA lane. Motion-transfer modes (driving-video) are not exposed on this surface.

## Response — submit

```json
{
  "generation_id": "gJ8nP3kQ2m",
  "status": "pending",
  "reference_images": 1,
  "max_reference_images": 1,
  "poll_url": "/api/v1/images/img2video/gJ8nP3kQ2m",
  "charged_cents": 99,
  "balance_cents": 4701
}
```

HTTP status: `202 Accepted`.

| Field                  | Type    | Description                                |
| ---------------------- | ------- | ------------------------------------------ |
| `generation_id`        | string  | Job identifier — use it to poll            |
| `status`               | string  | `"pending"` on submit                      |
| `reference_images`     | integer | How many `image` files were used           |
| `max_reference_images` | integer | The engine's ceiling                       |
| `poll_url`             | string  | Path to poll for this job                  |
| `charged_cents`        | integer | Amount deducted from your balance up front |
| `balance_cents`        | integer | Remaining balance after the charge         |

## Polling

```
GET /api/v1/images/img2video/{generation_id}
```

No body — just the `Authorization` header. Poll every 5–10 seconds until `status` is no longer `"pending"`.

| Status field value | Meaning                                                                              |
| ------------------ | ------------------------------------------------------------------------------------ |
| `pending`          | Still running                                                                        |
| `completed`        | `video` holds a signed, time-limited URL to the finished clip                        |
| `failed`           | `error` holds the failure reason; the up-front charge is refunded (`refunded_cents`) |

```json
{ "status": "pending" }
```

```json
{ "status": "completed", "video": "https://.../generations/<id>.mp4?token=...", "generation_id": "gJ8nP3kQ2m" }
```

```json
{ "status": "failed", "error": "Generation failed", "refunded_cents": 99 }
```

## Example

```bash
# 1. submit
curl -X POST https://substance-api.com/api/v1/images/img2video \
  -H "Authorization: Bearer sk_live_..." \
  -F "image=@still.png" \
  -F "prompt=slow camera push, subject turns to face viewer"
# -> { "generation_id": "gJ8nP3kQ2m", "status": "pending", ... }

# 2. poll
curl https://substance-api.com/api/v1/images/img2video/gJ8nP3kQ2m \
  -H "Authorization: Bearer sk_live_..."
```

## Errors

| Status | When                                                                                                                                                                         |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Missing `image` or `prompt`, or the prompt is rejected by the safety filter (submit)                                                                                         |
| `401`  | Missing/invalid API key (submit or poll)                                                                                                                                     |
| `402`  | Insufficient credits (submit)                                                                                                                                                |
| `403`  | Required entitlement not enabled — `video` for `mode: "sfw"`, `nsfw` for `mode: "nsfw"` (submit), or you're polling a `generation_id` that belongs to another account (poll) |
| `404`  | Unknown `generation_id` (poll)                                                                                                                                               |
| `503`  | The base pipeline, or the `seedance` engine specifically, is temporarily disabled (submit)                                                                                   |
| `502`  | Submit failed upstream (submit)                                                                                                                                              |

A job can also resolve to `status: "failed"` on a successful poll request (HTTP `200`) — that's a mid-pipeline failure rather than an HTTP error; see the [Polling](#polling) table above. See [Errors](/vdocs/errors.md) for the general error format.

## Pricing

Billed per call, up front, and refunded in full if the job fails. The rate varies by `engine`:

| Engine                             | Price                               |
| ---------------------------------- | ----------------------------------- |
| `wan` (self-hosted Wan 2.2)        | $0.99                               |
| `seedance` (external Seedance 2.0) | from $1.59 — scales with `duration` |

Seedance bills per output **second** upstream, so its price scales with `duration` rather than staying flat; `wan` is a fixed-cost render and does not. The exact amount deducted is returned as `charged_cents` on submit. If your balance can't cover the selected engine the call returns `402` with `required_cents` and the `engine` that was priced, before the job is submitted. See [Credits & pricing](/vdocs/account/credits.md) for the general model.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://bountyv.gitbook.io/vdocs/image-api/images-img2video.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
