Backend
March 5, 2026·FastAPI, Redis, Celery, Async

Building High-Throughput Asynchronous Tasks with FastAPI and Redis Event Queues

AI inference endpoints have a specific shape problem: the work is heavy (seconds, not milliseconds) but the client still wants a fast response. Blocking a FastAPI request handler on a multi-second model call kills concurrency — every worker thread you have gets tied up waiting instead of accepting new requests.

The Pattern: Accept, Queue, Poll

from fastapi import FastAPI
from celery_app import process_image

app = FastAPI()

@app.post("/jobs")
async def create_job(payload: JobRequest):
    task = process_image.delay(payload.image_url)
    return {"job_id": task.id, "status": "queued"}

@app.get("/jobs/{job_id}")
async def get_job(job_id: str):
    result = process_image.AsyncResult(job_id)
    if result.ready():
        return {"status": "done", "result": result.result}
    return {"status": "pending"}

The request handler itself never touches the model. It hands the job to Celery, gets a task ID back immediately, and returns. The actual inference happens in a separate worker process pool that can scale independently of the API layer.

Why Redis as the Broker

Redis works well here for two reasons beyond being a message broker: it's also useful as a result backend (Celery can store task results in Redis directly), and it doubles as a cache for anything the pipeline computes repeatedly — embeddings, resized images, model outputs keyed by input hash.

# celery_app.py
from celery import Celery

celery_app = Celery(
    "worker",
    broker="redis://redis:6379/0",
    backend="redis://redis:6379/1",
)

Splitting broker (db 0) and result backend (db 1) keeps queue traffic and result storage from competing for the same keyspace under load.

Scaling the Workers, Not the API

Because the API layer and the worker pool are decoupled, you scale them independently: more Uvicorn workers for request throughput, more Celery workers (potentially on GPU-backed nodes) for inference throughput. This is the backbone of how the Memora AI clustering pipeline sustains hundreds of concurrent uploads without the API layer becoming the bottleneck.

Production deployments typically add a dead-letter queue and task result TTL on top of this base pattern — covered in a follow-up post.