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.
Endpoints
Section titled “Endpoints”POST /v2/job/async Create Async Job
Section titled “POST /v2/job/async Create Async Job”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 processedprocessed- Job completed successfullyfailed- 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).
Status Codes
Section titled “Status Codes”| Code | Description |
|---|---|
201 | Job created successfully (POST /v2/job/async) |
200 | Request successful (GET endpoints) |
400 | Bad request - invalid job configuration or job type doesn’t support async |
401 | Unauthorized - missing or invalid authentication |
404 | Job not found or expired |
429 | Too many requests - exceeded async job limit or no capacity |
Example
Section titled “Example”Create the following main.py
import jsonimport osimport sysimport time
import requestsfrom 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')Create the following main.js
const axios = require('axios');const axiosRetry = require('axios-retry').default;const fs = require('node:fs');
async function main() { axiosRetry(axios, { retries: 3, retryCondition: (error) => { return [413, 429, 503].includes(error.response.status); }, });
let job = { "type": "inference.veo.fast.txt2vid.v1", "config": { "prompt": "Running in the woods with a dog.", "aspect_ratio": "16:9", "resolution": "720p", "generate_audio": true } };
let res = await axios({ method: 'POST', url: 'https://inference.prodia.com/v2/job/async', headers: { 'Accept': 'video/mp4', 'Authorization': `Bearer ${process.env.PRODIA_TOKEN}`, }, data: job, });
job = res.data; let jobStatus = job.state.current; console.log(`Job ID: ${job.id}`); console.log(`Job Status: ${jobStatus}`);
while (jobStatus == 'processing') { await new Promise(resolve => setTimeout(resolve, 1000)); res = await axios({ method: 'GET', url: `https://inference.prodia.com/v2/job/async/${job.id}/job.state.current`, headers: { 'Authorization': `Bearer ${process.env.PRODIA_TOKEN}`, }, }); jobStatus = res.data; console.log(`Job Status: ${jobStatus}`) }
if (jobStatus != 'processed') { console.log(job); process.exit(1); }
res = await axios({ method: 'GET', url: `https://inference.prodia.com/v2/job/async/${job.id}/output/video.mp4`, headers: { 'Authorization': `Bearer ${process.env.PRODIA_TOKEN}`, }, responseType: 'arraybuffer', });
fs.writeFileSync('output.mp4', res.data); console.log('Output file: output.mp4');}
await main();