FastAPI Cheatsheet
Responses and Status Codes
Use this FastAPI reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Default Behavior
Return any dict, list, Pydantic model, or primitive — FastAPI serializes it to JSON with Content-Type: application/json and 200 OK.
@app.get("/items/{id}") def get_item(id: int): return {"id": id, "name": "Widget"} # → 200 JSON
Setting Status Codes
from fastapi import status @app.post("/items", status_code=201) def create_item(item: Item): return item # Using the status constants (preferred for readability): @app.post("/items", status_code=status.HTTP_201_CREATED) def create_item(item: Item): return item @app.delete("/items/{id}", status_code=status.HTTP_204_NO_CONTENT) def delete_item(id: int): return None # body must be empty for 204
Common Status Code Constants
| Constant | Code | Meaning |
|---|---|---|
HTTP_200_OK | 200 | Default success |
HTTP_201_CREATED | 201 | Resource created |
HTTP_202_ACCEPTED | 202 | Accepted (async) |
HTTP_204_NO_CONTENT | 204 | Success, no body |
HTTP_301_MOVED_PERMANENTLY | 301 | Redirect permanent |
HTTP_302_FOUND | 302 | Redirect temporary |
HTTP_304_NOT_MODIFIED | 304 | Cache valid |
HTTP_400_BAD_REQUEST | 400 | Client error |
HTTP_401_UNAUTHORIZED | 401 | Auth required |
HTTP_403_FORBIDDEN | 403 | Not allowed |
HTTP_404_NOT_FOUND | 404 | Not found |
HTTP_405_METHOD_NOT_ALLOWED | 405 | Wrong method |
HTTP_409_CONFLICT | 409 | State conflict |
HTTP_422_UNPROCESSABLE_ENTITY | 422 | Validation error |
HTTP_429_TOO_MANY_REQUESTS | 429 | Rate limited |
HTTP_500_INTERNAL_SERVER_ERROR | 500 | Server error |
Dynamic Status Codes (Response parameter)
from fastapi import Response @app.get("/items/{id}") def get_or_create(id: int, response: Response): if db.exists(id): return db.get(id) else: response.status_code = 201 return db.create(id)
Response Model
from pydantic import BaseModel class ItemIn(BaseModel): name: str price: float secret_key: str class ItemOut(BaseModel): name: str price: float @app.post("/items", response_model=ItemOut, status_code=201) def create(item: ItemIn): return item # secret_key is stripped — only ItemOut fields returned
response_model Options
@app.get("/items/{id}", response_model=ItemOut, response_model_exclude_unset=True, # skip fields not explicitly set response_model_exclude_none=True, # skip None fields response_model_exclude_defaults=True, # skip fields at their default value response_model_include={"name", "price"}, # whitelist response_model_exclude={"secret"}, # blacklist ) def get_item(id: int): ...
Response Classes
from fastapi.responses import ( JSONResponse, HTMLResponse, PlainTextResponse, RedirectResponse, StreamingResponse, FileResponse, Response, )
JSONResponse
from fastapi.responses import JSONResponse @app.get("/custom") def custom(): return JSONResponse( content={"message": "OK"}, status_code=200, headers={"X-Custom": "value"}, media_type="application/json", )
HTMLResponse
@app.get("/page", response_class=HTMLResponse) def page(): return "<html><body><h1>Hello</h1></body></html>"
PlainTextResponse
@app.get("/text", response_class=PlainTextResponse) def text(): return "plain string"
RedirectResponse
from fastapi.responses import RedirectResponse @app.get("/old-path") def redirect(): return RedirectResponse(url="/new-path", status_code=301)
StreamingResponse
from fastapi.responses import StreamingResponse def csv_generator(): yield "id,name\n" for item in db.iter_all(): yield f"{item.id},{item.name}\n" @app.get("/export.csv") def export(): return StreamingResponse( csv_generator(), media_type="text/csv", headers={"Content-Disposition": "attachment; filename=export.csv"}, ) # Async generator: async def async_gen(): for chunk in large_dataset: yield chunk @app.get("/stream") async def stream(): return StreamingResponse(async_gen(), media_type="application/octet-stream")
FileResponse
from fastapi.responses import FileResponse @app.get("/download") def download(): return FileResponse( path="report.pdf", filename="report.pdf", # Content-Disposition filename media_type="application/pdf", background=None, # optional BackgroundTask )
Raw Response (no serialization)
from fastapi import Response @app.get("/raw") def raw(): return Response( content=b"binary data", media_type="application/octet-stream", status_code=200, headers={"X-Custom": "val"}, )
Setting Response Headers
from fastapi import Response @app.get("/items/{id}") def get_item(id: int, response: Response): response.headers["X-Item-ID"] = str(id) response.headers["Cache-Control"] = "max-age=3600" return {"id": id}
Background Tasks
from fastapi import BackgroundTasks def send_email(email: str, message: str): ... # runs after response is sent @app.post("/notify") def notify(email: str, bg: BackgroundTasks): bg.add_task(send_email, email=email, message="Welcome!") return {"status": "queued"} # Multiple tasks: bg.add_task(task_one, arg1) bg.add_task(task_two, arg1, kwarg=val)
Documenting Multiple Responses
@app.get( "/items/{id}", response_model=ItemOut, responses={ 200: {"description": "Item found", "model": ItemOut}, 404: {"description": "Item not found"}, 403: { "description": "Forbidden", "content": { "application/json": { "example": {"detail": "Not enough permissions"} } }, }, }, ) def get_item(id: int): ...
ORJSONResponse (faster JSON)
# pip install orjson from fastapi.responses import ORJSONResponse app = FastAPI(default_response_class=ORJSONResponse) # or per-route: @app.get("/items", response_class=ORJSONResponse) def list_items(): return [{"id": 1}]