FastAPI long running task: return 202 with a job id
Don't hold a FastAPI request open for a long task: return 202 with a job id. For an AI video job, Sume does the work and a webhook brings the result.

For a long running task in FastAPI, don't keep the request open until the work ends. Accept the request, answer 202 Accepted with a job id, run the work outside the request, and let the client check a status route or receive a webhook. When the long task is an AI video on Sume's API, the work already runs on Sume's side: your endpoint submits the job, stores its id, and returns, with no loop in BackgroundTasks or a Celery worker waiting for the video.
FastAPI facts come from its Background Tasks, Response Status Code, and Concurrency and async / await pages; Sume facts come from Video Generation, Jobs and results, and Webhooks. All were read on 2026-09-27. Sume has no FastAPI package: the endpoint makes a plain HTTPS call with Requests.
Should I use BackgroundTasks or Celery for a long task?
It depends on where the work runs. FastAPI's docs describe BackgroundTasks as tasks run after returning a response, and even suggest answering 202 Accepted and processing a slow file in the background. For heavy computation that doesn't need the same process, they point to bigger tools like Celery, which tend to need a message or job queue manager such as RabbitMQ or Redis but can run tasks in multiple processes and servers.
A Sume video fits neither. Its docs say video generation typically takes 30 seconds to several minutes, all of it on Sume's side. A poll loop in BackgroundTasks would sit inside your web app for that long, and a Celery worker would only move the wait somewhere else.
| Option | Where the work runs | Fits |
|---|---|---|
BackgroundTasks | In the FastAPI app, after the response is sent | Small tasks, such as an email notification, or work that needs the app's own variables and objects |
| Celery with a queue | Worker processes, possibly on other servers, fed by RabbitMQ or Redis | Heavy computation you run yourself |
An async API such as POST /v1/videos | On Sume's side; the submit returns a job id and polling URL at once | Work you only start and collect |
How do I start the video job from a FastAPI endpoint?
Submit, store, return. The endpoint sets its own 202 with the decorator's status_code parameter. It is a plain def, as FastAPI's docs advise when a library that calls an API doesn't support await, and FastAPI runs such path operations in an external threadpool instead of blocking the server. Sume answers the submit with 202 and a bare object: id, polling_url, status: "pending", and model.
Idempotency-Keycomes from your record, not from the request: resending the same key with the same body returns the original job instead of a second paid one.callback_urlmust be a public HTTPS URL; Sume POSTs to it once the job reaches a terminal state. Localhost and private-network URLs are rejected.- Requests' docs point to
json=when you need theapplication/jsonContent-Type, and warn that a call with notimeoutcan hang indefinitely. SUME_API_KEYstays in the server's environment, never in frontend JavaScript.
import os, requests
from fastapi import FastAPI
app = FastAPI()
@app.post("/orders/{order_id}/video", status_code=202)
def start_video(order_id: str): # plain def: runs in FastAPI's threadpool
if job := db.find_video_job(order_id): # your table: already submitted
return job
r = requests.post(
"https://api.sume.com/v1/videos",
headers={
"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
"Idempotency-Key": f"order-{order_id}-video-v1", # from the record
},
json={
"model": "sume/auto",
"prompt": db.video_prompt(order_id), # same record, same body
"callback_url": "https://example.com/hooks/sume",
},
timeout=30,
)
r.raise_for_status()
job = r.json() # id, polling_url, status: "pending"
db.save_video_job(order_id, job["id"], job["polling_url"])
return {"job_id": job["id"], "status": job["status"]}What if the request times out or the server crashes?
Nothing is lost while the job id or the key survives. A submit is accepted the moment Sume has a durable job id, and the docs say to store that id so your integration can recover work after process restarts. A 2xx means the job exists and paid work is in flight, not that it finished.
- Crashed before saving the id: resubmit with the same
Idempotency-Keyand the same body, and the replay returns the original job. A different body under the same key is409 idempotency_conflict, so build the prompt from the record too. - Your own call timed out: a client-side timeout does not cancel the job. It keeps running and still bills, so read it back instead of submitting a new paid request.
- Your worker restarted: the stored job id lets any process pick the job up again at
GET /v1/jobs/{id}/status.
How does the finished video get back to my app?
Through callback_url. Sume sends terminal job events only, job.completed, job.failed, or job.canceled, signed with HMAC-SHA256 over <timestamp>.<raw_body>, and a job.completed payload lists artifacts with media.sume.com URLs. Verify the raw body before parsing it; Python webhook HMAC verification in FastAPI and Django has that route.
- Store the event durably, then answer any
2xx. Each attempt gets 10 seconds, and Sume makes up to 10. - Use
job_idas the idempotency key on your side, so a repeated delivery updates one row. - Keep polling as the backup for deliveries that never arrive. Text-to-video API in Python covers the poll loop and the download.
Sources
Related posts
More in Integrations
- Gemini CLI MCP server: add Sume's hosted MCP
Add Sume's hosted MCP server to Gemini CLI with httpUrl and an API-key header read from your environment, then allowlist and confirm its tools.
- GitHub Actions: generate a release video with a Sume Format
Start a Sume Format run when a GitHub release is published, pass the notes as input, poll until the video is ready, and attach it to the release.
- GitHub Copilot coding agent MCP: add Sume's hosted server
Copilot's coding agent reads MCP servers from repository settings. Add Sume with an API key from a COPILOT_MCP_ secret and a tools allowlist.
- GitLab scheduled pipeline: run a nightly AI video job
Run a GitLab scheduled pipeline nightly, start the AI video with a date-based Idempotency-Key, and poll inside a job timeout above the run deadline.
Written by Sume