Skip to content

Async API

The asynchronous polling-style API enables easy integration of long duration workflows, particularly video generation. Unlike the synchronous /v2/job endpoint which blocks until completion, the async API returns immediately with a job ID that you can poll to check progress and retrieve outputs.

Creates a new async job and returns immediately with the job metadata. The job will be processed in the background.

Request: Same as synchronous /v2/job - supports both application/json and multipart/form-data content types.

Response: 201 Created with the job JSON containing the job id.

GET /v2/job/async/:id/job.state.current Get Job State

Section titled “GET /v2/job/async/:id/job.state.current Get Job State”

Returns the current state of the job as plain text. Use this for efficient polling.

States:

  • processing - Job is currently being processed
  • processed - Job completed successfully
  • failed - Job encountered an error

GET /v2/job/async/:id/job.json Get Job Metadata

Section titled “GET /v2/job/async/:id/job.json Get Job Metadata”

Returns the full job JSON including configuration, timestamps, metrics, and error information (if failed).

GET /v2/job/async/:id/output List Output Files

Section titled “GET /v2/job/async/:id/output List Output Files”

Returns a list of output files produced by the job.

GET /v2/job/async/:id/output/:filename Download Output

Section titled “GET /v2/job/async/:id/output/:filename Download Output”

Downloads a specific output file (e.g., video.mp4).

CodeDescription
201Job created successfully (POST /v2/job/async)
200Request successful (GET endpoints)
400Bad request - invalid job configuration or job type doesn’t support async
401Unauthorized - missing or invalid authentication
404Job not found or expired
429Too many requests - exceeded async job limit or no capacity

Create the following main.py

main.py
import json
import os
import sys
import time
import requests
from requests.adapters import HTTPAdapter, Retry
prodia_token = os.getenv('PRODIA_TOKEN')
prodia_url = 'https://inference.prodia.com/v2/job/async'
session = requests.Session()
retries = Retry(allowed_methods=None, status_forcelist=Retry.RETRY_AFTER_STATUS_CODES)
session.mount('http://', HTTPAdapter(max_retries=retries))
session.mount('https://', HTTPAdapter(max_retries=retries))
session.headers.update({'Authorization': f"Bearer {prodia_token}"})
headers = {
'Accept': 'video/mp4',
}
job = json.loads('''{
"type": "inference.veo.fast.txt2vid.v1",
"config": {
"prompt": "Running in the woods with a dog.",
"aspect_ratio": "16:9",
"resolution": "720p",
"generate_audio": true
}
}''')
res = session.post(prodia_url, headers=headers, json=job)
print(f"Request ID: {res.headers['x-request-id']}")
print(f"Status: {res.status_code}")
if res.status_code != 201:
print(res.text)
sys.exit(1)
job = res.json()
job_id = job['id']
job_status = job['state']['current']
print(f"Job ID: {job_id}")
print(f"Job Status: {job_status}")
while job_status == 'processing':
time.sleep(1)
res = session.get(f"{prodia_url}/{job_id}/job.state.current")
if res.status_code != 200:
print(res.text)
sys.exit(1)
job_status = res.text
print(f"Job Status: {job_status}")
if job_status != 'processed':
res = session.get(f"{prodia_url}/{job_id}/job.json")
if res.status_code != 200:
print(res.text)
sys.exit(1)
print(res.json())
sys.exit(1)
res = session.get(f"{prodia_url}/{job_id}/output/video.mp4")
if res.status_code != 200:
print(res.text)
sys.exit(1)
with open('output.mp4', 'wb') as f:
f.write(res.content)
print('Output file: output.mp4')