Genace

AI video API webhook vs polling: which one to use and when

A render takes minutes, so something has to bridge the gap between “submitted” and “here is your file”. There are four mechanisms, and they are not interchangeable — each survives a different failure.

Long poll

curl "https://genace.ai/api/v1/jobs/$JOB_ID?wait=30" \
  -H "Authorization: Bearer $GENACE_API_KEY"

Blocks server-side for up to wait seconds and returns whatever state it reached. wait=0 returns immediately.

Use it when the caller is a script or a CLI that can just sit there.

Fails when your runtime has a hard request timeout shorter than the render. Serverless functions are the usual culprit.

SSE

curl -N "https://genace.ai/api/v1/jobs/$JOB_ID/stream" \
  -H "Authorization: Bearer $GENACE_API_KEY"

Holds a connection open and pushes an event each time the state moves. The stream polls upstream on your behalf.

Use it when a human is watching — it is what a progress bar wants.

Fails when nobody is watching. A dropped connection means the events go nowhere, and mobile networks drop connections.

Webhook

You supply a URL; we POST to it when the job reaches a terminal state.

Use it when the work is server-side and there is somewhere durable to receive the result. Nothing has to stay connected, which is the whole point.

Fails when your endpoint is down at the moment we call it, or is not publicly reachable. Local development is the common trap here.

The cron backstop

Jobs that nobody is watching still get advanced on a schedule. This is not something you call — it is what makes the other three safe to abandon.

Why it matters for agents specifically: the session ends, the user closes the tab, the process exits. Without a backstop, that job is stuck in running forever, holding credits it will never release. With one, it reaches a terminal state and — if it failed — refunds.

Picking

Your caller isUse
A script or CLIlong poll
A UI with a progress barSSE
A server-side pipelinewebhook
An agent tool callreturn the job id, collect elsewhere

That last row is the one people get wrong. A tool that blocks for four minutes is dead weight in the agent’s transcript, and the render outlives the session anyway.

All four have the same failure semantics

Whichever you use, a terminal state of failed is a successful HTTP response describing a failure. It will not throw. Check state, not the status code — see job state failed and UPSTREAM_FAIL.

Failed and canceled jobs refund their credits in full, regardless of how you found out.

Models covered on this page

Where these facts come from

  • codebase: src/ai/jobs/http.ts — pollUntilTerminal and createJobSseStream
  • codebase: src/app/api/v1/jobs/[id]/stream/route.ts — SSE endpoint
  • codebase: src/app/api/cron/poll/route.ts — the scheduled backstop