Last modified: Dec 02, 2025 By Alexander Williams

FastAPI Background Tasks: Run Async Jobs

Modern web apps need to be fast. Users expect instant responses.

But some operations take time. Sending emails or processing files can be slow.

You cannot make the user wait. FastAPI provides a brilliant solution.

It is called Background Tasks. They let you run code after sending a response.

What Are Background Tasks?

Background Tasks are functions that run after your endpoint returns.

The client gets an immediate response. The server handles the heavy work later.

This is perfect for non-critical, time-consuming jobs.

Think of email notifications, data cleanup, or image resizing.

They run in the same event loop as your main app. This is efficient.

Why Use Background Tasks?

Speed is the main reason. Your API stays responsive.

User experience improves dramatically. No more staring at loading screens.

They simplify error handling. A failed task does not break the main request.

You can queue multiple tasks easily. FastAPI manages the execution order.

They integrate seamlessly with async/await Python code.

Getting Started with BackgroundTasks

First, import BackgroundTasks from FastAPI. Then add it as a parameter.

Use the add_task method to queue your function. Let's see a basic example.


from fastapi import FastAPI, BackgroundTasks
import time

app = FastAPI()

def write_log(message: str):
    # Simulate a slow task
    time.sleep(2)
    with open("log.txt", mode="a") as log:
        log.write(f"{message}\n")
    print(f"Log written: {message}")

@app.post("/send-notification/")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    # Add the slow task to run in the background
    background_tasks.add_task(write_log, f"Notification sent to {email}")
    # Return response immediately
    return {"message": "Notification queued", "email": email}
    

In this code, the write_log function simulates slow work.

The endpoint adds it as a background task. It then returns a JSON response instantly.

The server writes to the log file after the response is sent.

Example: Sending a Welcome Email

Let's build a practical user registration endpoint. It uses background tasks.

We will simulate sending a welcome email. This is a classic use case.


from fastapi import FastAPI, BackgroundTasks, HTTPException
from pydantic import BaseModel, EmailStr

app = FastAPI()

# A simple Pydantic model for request validation
class UserRegister(BaseModel):
    username: str
    email: EmailStr

def send_welcome_email(email: str, username: str):
    # Simulate email sending delay
    time.sleep(3)
    print(f"Email sent to {email}. Welcome, {username}!")
    # In reality, you would call an email service API here

@app.post("/register/")
async def register_user(user: UserRegister, background_tasks: BackgroundTasks):
    # Add the email task to the background
    background_tasks.add_task(send_welcome_email, user.email, user.username)
    # Return success response right away
    return {
        "status": "success",
        "message": "User registered. Welcome email is being sent.",
        "user": user.username
    }
    

We use a Pydantic model for validation. This ensures correct data.

The send_welcome_email function is the background job.

The user gets an instant success message. The email sends in the background.

For robust validation, see our guide on FastAPI Validation with Pydantic Models.

Testing Your Background Tasks

Testing is crucial. Use FastAPI's TestClient from pytest.

You can verify the main response works. Mocking the background function is wise.

This keeps your tests fast and reliable. Learn more in our tutorial on Test FastAPI Endpoints with pytest and TestClient.

Important Considerations

Background tasks run in the same process. A crash can affect your app.

For critical jobs, use a proper task queue. Celery or RQ are good choices.

Tasks are not guaranteed to run if the server stops. Plan for failures.

They are ideal for fire-and-forget operations. Logging is your friend.

Always monitor your background tasks. Ensure they complete successfully.

Advanced Pattern: Chaining Tasks

You can add multiple tasks. They will run sequentially after the response.

This is useful for multi-step processes. Here is an example.


def step_one(data: str):
    time.sleep(1)
    print(f"Step one completed for: {data}")

def step_two(data: str):
    time.sleep(1)
    print(f"Step two completed for: {data}")

@app.post("/process/")
async def process_data(item: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(step_one, item)
    background_tasks.add_task(step_two, item)
    return {"status": "processing", "item": item}
    

Both tasks will run. They execute in the order they were added.

Remember, they run after the response is on its way to the client.

When Not to Use Background Tasks

Do not use them for operations the client needs immediately.

They are not for real-time updates. For that, consider FastAPI WebSockets.

Avoid long-running tasks that block the event loop. Use a separate worker.

If you need task persistence, use a dedicated queue system.

For heavy production loads, plan your deployment. Our guide on Deploy FastAPI with Docker for Production can help.

Conclusion

FastAPI Background Tasks are a powerful tool. They keep your API fast.

You can handle slow operations elegantly. The user experience remains top-notch.

Start with simple tasks like emails or logging. Understand the limits.

For complex workflows, graduate to task queues. FastAPI makes it easy to start.

Integrate them into your next project. Your users will thank you for the speed.