Status Codes & Responses

Difficulty: Beginner

HTTP status codes are three-digit numbers that indicate the result of an HTTP request. They are grouped into five categories: 1xx (informational), 2xx (success), 3xx (redirection), 4xx (client error), and 5xx (server error). Using the correct status code is essential for building RESTful APIs that clients can interpret correctly.

The most common success codes are 200 OK (general success, the default), 201 Created (a new resource was created), 204 No Content (success with no response body, often used for DELETE), and 202 Accepted (request received but processing is deferred). FastAPI returns 200 by default; to change it, pass status_code to the route decorator.

Common client error codes include 400 Bad Request (generic client error), 401 Unauthorized (authentication required), 403 Forbidden (authenticated but not allowed), 404 Not Found (resource does not exist), 409 Conflict (resource already exists or state conflict), and 422 Unprocessable Entity (validation error, FastAPI's default for invalid input).

Server error codes indicate something went wrong on the server side. 500 Internal Server Error is the generic server error. FastAPI returns 500 when an unhandled exception occurs in your handler. In production, you should have error handlers to catch exceptions and return appropriate responses.

FastAPI provides the starlette.status module with constants for all HTTP status codes, making your code more readable. Instead of writing status_code=201, you can write status_code=status.HTTP_201_CREATED. This makes the intent clear and prevents typos in status code numbers.

For more control over responses, FastAPI offers several response classes. JSONResponse is the default and returns JSON data. PlainTextResponse returns plain text. HTMLResponse returns HTML content. RedirectResponse sends a redirect. StreamingResponse streams data (useful for file downloads). FileResponse sends a file from disk. You can also use the generic Response class for complete control over the response body, headers, and content type.

Custom response headers can be added by using the Response parameter in your handler function. FastAPI injects a Response object that you can modify to add headers, set cookies, or change the status code dynamically. You can also return response objects directly for full control.

Code examples

Setting Status Codes

from fastapi import FastAPI, HTTPException
from starlette import status

app = FastAPI()

@app.post("/users", status_code=status.HTTP_201_CREATED)
def create_user():
    return {"id": 1, "name": "Alice"}

@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user(user_id: int):
    # 204 means no content is returned
    pass

@app.get("/users/{user_id}")
def get_user(user_id: int):
    if user_id > 100:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found",
        )
    return {"id": user_id, "name": "Alice"}

Use the status module constants for readable status codes. POST returns 201 for creation. DELETE returns 204 for successful deletion without a body. HTTPException is raised for error status codes.

Custom Response Headers

from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse

app = FastAPI()

# Method 1: Using the Response parameter
@app.get("/items/{item_id}")
def get_item(item_id: int, response: Response):
    response.headers["X-Item-Id"] = str(item_id)
    response.headers["Cache-Control"] = "max-age=300"
    return {"id": item_id, "name": "Widget"}

# Method 2: Returning a Response object directly
@app.get("/custom")
def custom_response():
    return JSONResponse(
        content={"message": "Custom response"},
        status_code=200,
        headers={
            "X-Custom-Header": "custom-value",
            "X-Request-Id": "abc-123",
        },
    )

There are two ways to set custom headers. Using the Response parameter lets you add headers while still returning data normally. Returning a JSONResponse directly gives you complete control over the response.

Error Response Patterns

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse

app = FastAPI()

# Standard HTTPException
@app.get("/items/{item_id}")
def get_item(item_id: int):
    if item_id < 1:
        raise HTTPException(
            status_code=400,
            detail="Item ID must be positive",
            headers={"X-Error": "invalid-id"},
        )
    return {"id": item_id}

# Custom exception class
class ItemNotFoundError(Exception):
    def __init__(self, item_id: int):
        self.item_id = item_id

@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request: Request, exc: ItemNotFoundError):
    return JSONResponse(
        status_code=404,
        content={
            "error": "item_not_found",
            "message": f"Item {exc.item_id} does not exist",
        },
    )

@app.get("/products/{product_id}")
def get_product(product_id: int):
    raise ItemNotFoundError(item_id=product_id)

HTTPException is the standard way to raise errors. For custom error formats, define exception classes and register exception handlers with @app.exception_handler(). This lets you standardize error response formats across your entire API.

Key points

Concepts covered

HTTP Status Codes, status Module, JSONResponse, Custom Headers, Response Classes