Skip to main content
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:
EndpointUse it whenInputsNotes
POST /videos/image-to-videoYou have one image and want to bring it to lifeimageFast, affordable, audio included. Output matches the reference image’s aspect ratio.
POST /videos/framesYou have a start and end frame and want a planned A-to-B motionstartImage + 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 or prior generations.

Animate a single image

curl -X POST http://localhost:3001/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

curl -X POST http://localhost:3001/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:
{ "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):
curl "http://localhost:3001/api/v1/videos/6b2c1d4e-...?wait=true" \
  -H "x-api-key: $DAFTY_API_KEY"
{
  "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 for the loop and POST /jobs/{id}/cancel to abort an in-flight clip.
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.

Limits & best practices

  • One clip per request. For alternates, submit separate requests, and use Idempotency keys 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.
  • 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.
There are no webhooks in v1 - completion is via polling / ?wait=true.