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 data

All 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 ──▶ FastAPI

Use 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.
  • HTMLResponse and PlainTextResponse return text formats.
  • RedirectResponse sends visitors to another address.
  • FileResponse serves downloads from disk.
  • StreamingResponse sends large or live data in chunks.