FastAPI Custom Response Types
FastAPI sends JSON by default. Many projects need other formats too: a web page, a downloadable file, a redirect, or a live stream of data. Response classes let you pick the format. Imagine a shop that wraps each product differently: a gift box for presents, a paper bag for snacks, a crate for furniture. The product stays the same while the packaging changes.
Available Response Classes
Class Sends Typical use
───────────────── ─────────────────────── ───────────────────────
JSONResponse JSON data default API output
HTMLResponse HTML page simple web pages
PlainTextResponse plain text health checks, logs
RedirectResponse redirect to another URL moved pages, login flow
FileResponse file from disk downloads, images, PDFs
StreamingResponse data in small chunks large files, live dataAll classes live in fastapi.responses.
HTML Responses
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/welcome", response_class=HTMLResponse)
def welcome():
return "<h1>Welcome to my site</h1><p>Built with FastAPI.</p>"The response_class setting changes the content type to text/html. The browser renders the page, and the interactive docs show the correct media type.
Plain Text Responses
from fastapi.responses import PlainTextResponse
@app.get("/ping", response_class=PlainTextResponse)
def ping():
return "pong"Redirect Responses
A redirect tells the browser to visit a different address.
from fastapi.responses import RedirectResponse
@app.get("/old-page")
def old_page():
return RedirectResponse(url="/new-page", status_code=301)
Browser ── GET /old-page ──▶ FastAPI
Browser ◀── 301, go to /new-page ── FastAPI
Browser ── GET /new-page ──▶ FastAPIUse 301 for permanent moves and 307 for temporary ones.
File Responses
Return a file from disk with a single line. FastAPI handles the headers and the transfer.
from fastapi.responses import FileResponse
@app.get("/download/report")
def download_report():
return FileResponse(
path="files/report.pdf",
filename="monthly-report.pdf",
media_type="application/pdf",
)The filename value sets the name that appears in the browser's save dialog.
Streaming Responses
A streaming response sends data piece by piece. The client starts receiving content before the server finishes producing it.
from fastapi.responses import StreamingResponse
def number_generator():
for i in range(1, 6):
yield f"Line {i}\n"
@app.get("/stream")
def stream():
return StreamingResponse(number_generator(), media_type="text/plain")
Server ── "Line 1" ──▶ Client
Server ── "Line 2" ──▶ Client
Server ── "Line 3" ──▶ Client
...pieces arrive one after another...Streaming keeps memory low for large files and powers live text from AI models.
Custom Status Codes and Headers
Any response object accepts a status code and headers.
from fastapi.responses import JSONResponse
@app.post("/items")
def create_item():
return JSONResponse(
content={"message": "Created"},
status_code=201,
headers={"X-Request-Source": "api"},
)Changing the Default Response Class
Set a default for the whole app when every route returns the same format.
from fastapi.responses import HTMLResponse
app = FastAPI(default_response_class=HTMLResponse)Common Mistakes
Returning a Dictionary with HTMLResponse
An HTML route must return a string. A dictionary produces a confusing error.
Wrong File Path
A missing file raises a server error. Check that the path exists before returning a FileResponse.
Unescaped User Input in HTML
Never place raw user text inside an HTML string. Template engines escape text automatically, which keeps pages safe.
Choosing a Format in Practice
Ask one question to pick the right response class: what will the receiver do with the data? A mobile app parses JSON. A browser tab displays HTML. A person who clicks a download button expects a file with a sensible name. A monitoring tool that checks server health reads a short plain text answer.
Many projects mix formats in one app. The routes under /api return JSON, a few routes under /pages return HTML, and /download routes return files. The interactive docs show the media type for each route, so readers see the difference at a glance.
Streaming deserves special attention. A client that downloads a large report from a normal response waits for the entire file in memory before the first byte leaves the server. A streaming response starts the transfer immediately and keeps memory use flat, which protects the server during busy hours.
Test each response type from a browser and from the docs page. Download links, redirects, and HTML pages behave differently in each place, and a quick check catches wrong media types early.
Key Points
- JSON is the default; response classes switch the format.
HTMLResponseandPlainTextResponsereturn text formats.RedirectResponsesends visitors to another address.FileResponseserves downloads from disk.StreamingResponsesends large or live data in chunks.
