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

# Generating video

> Animate a single image, or transition between two frames - when to use which, and how to poll for the clip.

The Dafty API generates short video clips from your images. Like image generation,
it's asynchronous: you create a **job** and poll it until it returns a video URL.
Each request produces a single clip.

There are two endpoints, split by what you're trying to do:

| Endpoint                      | Use it when                                                         | Inputs                                    | Notes                                                                                |
| ----------------------------- | ------------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------ |
| `POST /videos/image-to-video` | You have **one image** and want to bring it to life                 | `image`                                   | Fast, affordable, audio included. Output matches the reference image's aspect ratio. |
| `POST /videos/frames`         | You have a **start and end frame** and want a planned A-to-B motion | `startImage` + `endImage` (both required) | Slower and pricier. Supports an `aspectRatio` knob.                                  |

If you only have one image, use **image-to-video**. Reach for **frames** only when
you have both ends of the shot and want the motion to land on a specific final frame.

Frame inputs accept the same references as everywhere else: an `https` URL we ingest
(SSRF-guarded, then re-hosted), or the id/url of one of your [uploads](/uploads) or
prior generations.

## Animate a single image

```bash theme={null}
curl -X POST https://app.dafty.ai/api/v1/videos/image-to-video \
  -H "x-api-key: $DAFTY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "5c1e8b34-2a9f-4d61-8e07-3b2c1a0f9d4e",
    "prompt": "Slow push-in as the logo shimmers, soft ambient hum",
    "duration": 6,
    "resolution": "720p"
  }'
```

Only `image` and `prompt` are required. `duration` (1-15s, default 6), `resolution`
(`480p|720p`, default `720p`), and `audio` (default `true`) have sensible defaults;
`negativePrompt` is optional.

**The output video matches the reference image's aspect ratio.** To set the aspect
ratio explicitly, use [`/videos/frames`](#transition-between-two-frames).

## Transition between two frames

```bash theme={null}
curl -X POST https://app.dafty.ai/api/v1/videos/frames \
  -H "x-api-key: $DAFTY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "startImage": "5c1e8b34-2a9f-4d61-8e07-3b2c1a0f9d4e",
    "endImage": "4d9c2b1a-7e6f-4a8b-9c2d-1f0e8a7b6c54",
    "prompt": "Smooth dolly from the wide shot to the close-up",
    "duration": 6,
    "aspectRatio": "16:9",
    "resolution": "720p"
  }'
```

`startImage`, `endImage`, and `prompt` are required. `duration` here is 4-15s
(default 6). `aspectRatio` (`16:9|9:16|4:3|3:4|1:1|21:9|auto`, default `auto` which
matches the start frame), `resolution`, `audio`, and `negativePrompt` are optional.

## Poll for the result

Both endpoints return `202 Accepted` with a `video.generate` job:

```json theme={null}
{ "data": { "id": "6b2c1d4e-...", "type": "video.generate", "status": "pending", "createdAt": "..." } }
```

Poll it with `?wait=true` to long-poll until it's terminal (up to \~55s; video takes
longer than images, so expect a few waits):

```bash theme={null}
curl "https://app.dafty.ai/api/v1/videos/6b2c1d4e-...?wait=true" \
  -H "x-api-key: $DAFTY_API_KEY"
```

```json theme={null}
{
  "data": {
    "id": "6b2c1d4e-...",
    "type": "video.generate",
    "status": "completed",
    "result": {
      "video": {
        "id": "6b2c1d4e-...",
        "url": "https://cdn.../original.mp4",
        "durationSeconds": 6,
        "thumbnailUrl": "https://cdn.../thumbnail.jpg",
        "width": 1280,
        "height": 720
      }
    }
  }
}
```

Read the clip from `data.result.video.url`. `GET /videos/{id}` only resolves video
jobs; `GET /jobs/{id}` works for any job. See [Polling & wait](/polling-and-wait) for
the loop and `POST /jobs/{id}/cancel` to abort an in-flight clip.

<Note>
  `thumbnailUrl` is a poster image produced just after the video, so it can be `null`
  on the first `completed` read - re-fetch the job a moment later if you need it.
</Note>

## Limits & best practices

* **One clip per request.** For alternates, submit separate requests, and use
  [Idempotency keys](/idempotency) so a retried create doesn't double-charge.
* **Rate limit:** video creation is capped at **5 requests/minute per key** (tighter
  than images). Honor `Retry-After` on `429` - see [Rate limits](/rate-limits).
* **Concurrency:** video runs on a small dedicated pool, so clips may queue behind
  your other in-flight videos before they start `processing`. This is normal; keep
  polling.
* **Credits:** video costs more than images and scales with duration and resolution.
  A create returns `402` with code `INSUFFICIENT_CREDITS` (including how many CU were
  needed) if your balance can't cover it.

<Note>
  There are no webhooks in v1 - completion is via polling / `?wait=true`.
</Note>
