FastAPI Cheatsheet
Responses and Status Codes
Use this FastAPI reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
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}]