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. The ?price=true query parameter is
also supported (see Query Parameters).
Response: 201 Created with the full job JSON. The response includes the
server-generated id field that you need for polling:
{ "type": "inference.veo.fast.txt2vid.v1", "id": "c83d7027-240a-484a-aeba-96014e568711", "created_at": "2025-01-01T00:00:14.885Z", "updated_at": "2025-01-01T00:00:14.885Z", "expires_at": "2025-01-01T00:01:14.885Z", "state": { "current": "processing" }, "config": { "prompt": "Running in the woods with a dog.", "aspect_ratio": "16:9", "resolution": "720p", "generate_audio": true }}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).
DELETE /v2/job/async/:id Delete Job Resources
Section titled “DELETE /v2/job/async/:id Delete Job Resources”Deletes the async job’s stored input files, output files, and job metadata. Use the same API token that created the job.
The job must be in the processed or failed state. A job that is still
processing returns 409 Conflict; this endpoint does not cancel running jobs.
Response: 204 No Content with an empty body on success. The job’s metadata
and outputs are no longer available after deletion. A missing, expired, or
already deleted job returns 404 Not Found, as does a job created with a
different token.
Download any outputs you want to keep before deleting, and delete before the job expires.
curl -sSf -X DELETE \ -H "Authorization: Bearer $PRODIA_TOKEN" \ "https://inference.prodia.com/v2/job/async/$job_id"Job Expiry
Section titled “Job Expiry”Async job results are stored temporarily and expire after 1 hour. After
expiry, all GET endpoints for that job will return 404 Not Found. Clients
should download outputs promptly after the job completes.
The expires_at field in the job result indicates the exact expiry time.
Status Codes
Section titled “Status Codes”| Code | Description |
|---|---|
201 |
Job created successfully (POST /v2/job/async) |
200 |
Request successful (GET endpoints) |
204 |
Job resources deleted successfully (DELETE) |
400 |
Bad request - invalid job configuration or job type doesn’t support async |
401 |
Unauthorized - missing or invalid authentication |
404 |
Job not found, expired, deleted, or created with a different token |
409 |
Job is still processing and cannot be deleted yet |
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');}
main().catch((error) => { console.error(error); process.exitCode = 1;});