Async jobs
A video or a song takes seconds to minutes to render, so creating one answers 202 at once with a job. Poll the job, listen to a song as it renders, or have a webhook tell you when it is done.
A job's life
Every job moves through the same states, on /v1/videos and /v1/music alike. The last four are final: a job in one of them never changes again.
While a job waits, queue_position counts the jobs ahead of it for the same model; while it renders, progress estimates how far along it is, from 0 to 100. A render that fails before it starts, because the model was busy or out of reach, goes back to pending and is tried again, up to three times; a refusal from the model fails the job with its message.
On /v1/videos, OpenAI's SDKs see queued for pending, and read cancelled and expired as failed with the reason as the error's code, since their videos.poll() knows no other states.
Create a job
The create call checks the request before it answers: a malformed field, or a control the model does not have, is a 400 right away, never a job that fails later. Any other field the model does not read is listed in the job's warnings. It answers 202 with the job and its polling_url.
The models that render this way, with their prices:
POST /v1/music can also wait for the song: send Prefer: wait=30 (at most 60 seconds) and it answers 200 with the finished job if it ends in time, or 202 with the job as it stands.
Poll it
Fetch the job at its polling_url until it reaches a final state. While it is still going, every answer carries openai-poll-after-ms: how long to wait before asking again, worked out from the job's place in the queue and its progress, between 2 and 10 seconds. OpenAI's SDKs read it for you.
# Create the job; it answers 202 with the job at once
id=$(curl -s https://api.vultrinference.com/v1/videos \
-H "Authorization: Bearer $VULTR_INFERENCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "minimax-h3", "prompt": "A paper boat drifting down a rain-soaked street at dusk", "duration": 4}' | jq -r .id)
# Read it at its polling_url; while it runs, openai-poll-after-ms says how long to wait before reading again
curl -i https://api.vultrinference.com/v1/videos/$id \
-H "Authorization: Bearer $VULTR_INFERENCE_API_KEY"import os
from openai import OpenAI
client = OpenAI(base_url="https://api.vultrinference.com/v1", api_key=os.environ["VULTR_INFERENCE_API_KEY"])
# Creates the job, then polls at the pace the API asks for (openai-poll-after-ms)
video = client.videos.create_and_poll(
model="minimax-h3",
prompt="A paper boat drifting down a rain-soaked street at dusk",
seconds="4",
)
print(video.status) # completed, or failed with video.errorimport OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.vultrinference.com/v1", apiKey: process.env.VULTR_INFERENCE_API_KEY });
let video = await client.videos.create({
model: "minimax-h3",
prompt: "A paper boat drifting down a rain-soaked street at dusk",
seconds: "4",
});
// Poll at the pace the API asks for: openai-poll-after-ms, on every answer while the job runs
for (let wait = 5000; video.status === "queued" || video.status === "in_progress"; ) {
await new Promise((resolve) => setTimeout(resolve, wait));
const { data, response } = await client.videos.retrieve(video.id).withResponse();
video = data;
wait = Number(response.headers.get("openai-poll-after-ms") ?? wait);
}
console.log(video.status); // completed, or failed with video.errorHear a song as it renders
A song on a model that streams has a stream_url while it is pending or in_progress. GET /v1/music/{id}/stream answers audio/wav and plays the song from its first byte as it is made, then follows it until it ends. A listener who connects late, even after the song completed, still hears it from the start for 10 minutes; after that the stream answers 409 and the song is at its content URL.
The stream ends when the song does but cannot say why, so read the job afterwards: a stream that ends early belongs to a job that failed or was cancelled.
Or get a webhook
Send callback_url (a public https URL) with the create call and it is called when the job reaches a final state, so there is nothing to poll. Each delivery is signed with your webhook secret and carries the job; answer it within 10 seconds or it is sent again. Webhooks has the events, how to verify a delivery, and a receiver to start from.
Cancel or download
DELETE on a job that is still pending or in_progress cancels it at once, and it is not billed. On a finished job it deletes the job and its outputs.
A completed job lists its files in unsigned_urls; fetch them with your API key before expires_at, after which they are deleted and the job turns expired.