Skip to navigation

Async Jobs

Submit video generation as background jobs and poll for results

The async API (V2) lets you submit a generation request and poll for its response. The result is downloadable using a URL available when the job completes. This is the recommended approach for production workloads, as it avoids the connection timeouts that can affect long-running sync requests.

Lifecycle

  1. Submit. POST to an async endpoint (/v2/{endpoint}) with your generation parameters. The response is 202 Accepted with a job ID.
  2. Poll. GET /v2/{endpoint}/{id} every few seconds until the status field is completed or failed.
  3. Download. When status is completed, the result object contains one or more URLs pointing to the generated output files.

Status values

StatusMeaning
pendingJob is queued.
processingGeneration is running.
completedJob finished successfully. result contains output URLs.
failedJob failed. error describes why.

completed and failed are terminal — stop polling when you see either.

Result format

The result object on a completed job is a map of output labels to URLs. The available keys depend on the endpoint — most video endpoints return video_url, while video-to-video-hdr returns exr_frames_url. Check each endpoint’s reference for the exact shape.

{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"created_at": "2026-01-15T10:00:00.000Z",
"completed_at": "2026-01-15T10:02:30.000Z",
"result": {
"video_url": "https://storage.googleapis.com/example/video.mp4"
}
}

Polling guidance

We recommend waiting at least 5 seconds between polls. Choose a slightly different delay each time to spread out requests when polling multiple jobs.

  • Stop polling when the status is completed or failed.
  • Retry transient 5xx responses and network failures on polling and download GET requests with exponential backoff. The examples below omit retry logic for brevity.
  • A 404 means the job doesn’t exist or has expired (see retention below).

Retention

Job status is available for up to 24 hours after the job reaches a terminal state, and can be removed sooner. Once it’s removed, GET /v2/{endpoint}/{id} returns 404.

Output URLs expire independently of job status. Download or re-host outputs as soon as you observe completed, rather than using these URLs as permanent storage.

Error handling

Errors appear in two places but use the same { type, message } shape:

  • HTTP errors on POST or GET requests — validation, auth, rate limits, etc.
  • Job failures inside the status response when a job fails during processing.

Your error handling logic can use the same type checks (e.g., content_filtered_error, insufficient_funds_error) regardless of where the error appears. See Error Handling for the full list.

Example

A complete submit → poll → download loop. The cURL version uses jq to read fields out of the JSON responses:

import random
import requests
import time
api_key = "YOUR_API_KEY"
headers = {"Authorization": f"Bearer {api_key}"}
# Submit
submit_response = requests.post(
"https://api.ltx.io/v2/text-to-video",
headers={**headers, "Content-Type": "application/json"},
json={
"prompt": "A majestic eagle soaring through clouds at sunset",
"model": "ltx-2-5-pro",
"duration": 8,
"resolution": "1920x1080",
},
)
submit_response.raise_for_status()
job = submit_response.json()
# Poll
while True:
time.sleep(random.uniform(5, 6))
poll_response = requests.get(
f"https://api.ltx.io/v2/text-to-video/{job['id']}",
headers=headers,
)
poll_response.raise_for_status()
status = poll_response.json()
if status["status"] in ("completed", "failed"):
break
if status["status"] == "failed":
raise RuntimeError(status["error"]["message"])
# Download
video = requests.get(status["result"]["video_url"])
video.raise_for_status()
with open("video.mp4", "wb") as f:
f.write(video.content)