AI Video
Run Image-to-Video Jobs on Fal.ai with the Queue API
Submit an image-to-video job to the Fal.ai queue, poll status, and download the clip when the request completes.
- Fal.ai
- Video
- API
- Next.js
On this page
Image-to-video models do not finish inside a single web request. A good still can take tens of seconds, and a cold queue can take longer. If your route handler waits with the browser connection open, the platform will time the request out and the user will click again. The Fal.ai queue API is built for that wait. You submit a job, you store the request id, and you ask for status until the result URL exists.
Call Fal from a server you control. The API key is a secret. The browser should send your own backend a prompt and a picture you already stored, then poll your backend. Your backend talks to queue.fal.run. Confirm the model id in the Fal dashboard before you hard-code one. Model ids change, and an id that worked in a tutorial can 404 after a version bump.
Submit the job
A queue submit is a POST to https://queue.fal.run/ followed by the model id. The Authorization header uses the word Key and your FAL_KEY. The body is the model's input. For image-to-video that usually means a prompt and an image URL Fal can fetch. A localhost path will fail. Upload the still to a host Fal can reach, or use Fal's own file upload, and pass that URL.
const MODEL = "fal-ai/ltx-video";
export async function submitClip(input: { prompt: string; imageUrl: string }) {
const response = await fetch("https://queue.fal.run/" + MODEL, {
method: "POST",
headers: {
Authorization: "Key " + process.env.FAL_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: input.prompt,
image_url: input.imageUrl,
}),
});
if (!response.ok) {
throw new Error("Fal submit failed");
}
return response.json() as Promise<{ request_id: string }>;
}
Store request_id next to the user and the project. If the process restarts, the job is still running on Fal and the id is the only handle you have. Also store the model id you submitted. Status URLs are easier to rebuild when you know both.
Poll status, then fetch the result
Status lives at https://queue.fal.run/{model}/requests/{request_id}/status. The result lives at the same path without /status. Poll every few seconds from a worker or from a route the browser calls, and stop on COMPLETED, on an error status, or after a deadline you chose. A page that polls forever will burn the user's battery and your function invocations.
export async function readStatus(requestId: string) {
const url =
"https://queue.fal.run/fal-ai/ltx-video/requests/" +
requestId +
"/status";
const response = await fetch(url, {
headers: { Authorization: "Key " + process.env.FAL_KEY },
});
if (!response.ok) {
throw new Error("Fal status failed");
}
return response.json() as Promise<{ status: string }>;
}
- 01
Validate the still
Check type and size before submit. A 40 MB original will waste a queue slot. Resize to the model's preferred width.
- 02
Submit once
Disable the button and persist the request id. A retry needs a new decision, not an accidental second POST.
- 03
Poll with a deadline
Every 4 seconds is enough for a short clip. Give up after a few minutes and show the request id so a person can look it up.
- 04
Copy the file you care about
When status is COMPLETED, download the video URL into your own bucket. Provider URLs expire.
Prompts that survive a short clip
One camera move, one subject, and the same lighting as the still. "Slow dolly in, the bottle stays centered, soft studio light" beats a paragraph of plot. Image-to-video is a motion pass on a picture you already approved. If the still is wrong, the clip will be wrong in motion. Fix the still first.
- Pros: the queue survives browser disconnects, and you can run several shots without blocking a page render.
- Cons: you must persist ids, handle expired result URLs, and pay for retries that were actually double submits.
- Pros: the same status loop works for more than one Fal model if you store the model id.
- Cons: input field names differ by model. image_url on one model is image on another. Read the schema.
Log the Fal request id and your project id. Do not log the API key or the full Authorization header. When a job fails, the useful question is whether the image URL was reachable from Fal's network. Open that URL in a private window. If you cannot, Fal cannot either.
Poll for a product, webhooks when you have a worker
Polling from the page that started the job is fine while you are the only user. It is a poor fit once ten people submit shots and close their laptops. A webhook from Fal, if the model and your plan support one, lets a worker mark the row complete and copy the file without a browser. Verify the signature the provider documents before you trust the body. A public URL that marks any request id complete will be found. If you stay with polling, do it from a scheduled job that reads unfinished rows, not from a room full of open tabs. Either way, the request id in your database is the source of truth. The tab is just a view of that row. When the copy into your bucket succeeds, store your URL and stop pointing the editor at the provider URL.
