How FastAPI Generates the OpenAPI Spec
Every route you define in FastAPI produces an OpenAPI (formerly Swagger) specification automatically. The framework inspects:
- The path and HTTP method from the decorator
- Path parameters from the function signature
- Query parameters from keyword arguments with defaults
- Request body from Pydantic model type hints
- Response model from
response_modelor return type annotations
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/items/{item_id}")
def read_item(
item_id: int = Path(ge=1),
q: str | None = Query(default=None, max_length=50)
) -> Item:
return Item(name="Widget", price=9.99)
FastAPI converts this entire route into an OpenAPI operation object:
/items/{item_id}:
get:
parameters:
- name: item_id
in: path
schema: { type: integer, minimum: 1 }
- name: q
in: query
schema: { type: string, maxLength: 50, nullable: true }
responses:
"200":
content:
application/json:
schema: { $ref: "#/components/schemas/Item" }
Swagger UI (/docs)
By default FastAPI exposes an interactive Swagger UI at /docs. You can send requests, view responses, and see schemas - all from the browser:
| Feature | Behaviour |
|---|---|
| Try it out | Sends real HTTP requests to your running server |
| Schema viewer | Expands models, enums, and nested objects |
| Auth support | API keys, Bearer tokens, OAuth2 flows |
| Code samples | Auto-generated cURL, Python, JavaScript, etc. |
# Swagger UI is enabled by default - visit http://localhost:8000/docs
app = FastAPI()
ReDoc (/redoc)
FastAPI also provides ReDoc, an alternative documentation UI focused on readability. It renders the entire spec in a clean, three-panel layout:
# ReDoc is also enabled by default - visit http://localhost:8000/redoc
app = FastAPI()
| Doc UI | URL | Best for |
|---|---|---|
| Swagger UI | /docs | Interactive testing, development |
| ReDoc | /redoc | Reference docs, sharing with teams |
Customizing Metadata
You can control what appears in the generated docs by passing metadata to the FastAPI constructor:
app = FastAPI(
title="Shop API",
description="RESTful API for the online shop - product catalog, cart, checkout",
version="2.1.0",
summary="Online Shop Backend",
terms_of_service="https://example.com/terms",
contact={
"name": "API Support",
"url": "https://example.com/support",
"email": "api@example.com",
},
license_info={
"name": "MIT",
"url": "https://opensource.org/licenses/MIT",
},
)
This metadata is injected into the OpenAPI root object:
openapi: 3.1.0
info:
title: Shop API
description: RESTful API for the online shop...
version: 2.1.0
contact:
name: API Support
email: api@example.com
license:
name: MIT
Server and External Docs
app = FastAPI(
servers=[
{"url": "https://api.example.com/v2", "description": "Production"},
{"url": "https://staging.example.com", "description": "Staging"},
],
docs_url="/docs",
redoc_url="/redoc",
)
Organising Endpoints with Tags
Use the tags parameter to group endpoints in the docs:
from fastapi import APIRouter
router_items = APIRouter(prefix="/items", tags=["Items"])
router_users = APIRouter(prefix="/users", tags=["Users"])
@router_items.get("/")
def list_items():
return [{"id": 1, "name": "Widget"}]
@router_items.post("/")
def create_item():
return {"id": 2, "name": "Gadget"}
@router_users.get("/")
def list_users():
return [{"id": 1, "name": "Alice"}]
Swagger UI groups these under collapsible sections:
┌─ Items ──────────────────────────┐
│ GET /items │
│ POST /items │
└───────────────────────────────────┘
┌─ Users ──────────────────────────┐
│ GET /users │
└───────────────────────────────────┘
You can also add a description for each tag:
tags_metadata = [
{
"name": "Items",
"description": "Product catalogue - create, read, update, delete items",
"externalDocs": {
"description": "Inventory guide",
"url": "https://example.com/inventory",
},
},
{
"name": "Users",
"description": "User accounts and profile management",
},
]
app = FastAPI(openapi_tags=tags_metadata)
Disabling Docs in Production
You should disable or restrict documentation in production to avoid leaking API schema details:
import os
app = FastAPI(
docs_url="/docs" if os.getenv("ENV") == "development" else None,
redoc_url="/redoc" if os.getenv("ENV") == "development" else None,
openapi_url="/openapi.json" if os.getenv("ENV") == "development" else None,
)
Setting these to None removes the corresponding routes entirely:
| Parameter | Effect when None |
|---|---|
docs_url=None | /docs returns 404 |
redoc_url=None | /redoc returns 404 |
openapi_url=None | OpenAPI JSON spec is not generated |
Conditional OpenAPI via Dependency
For more granular control, override the OpenAPI function:
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if os.getenv("ENV") == "production":
return None
return get_openapi(
title="Shop API",
version="2.1.0",
routes=app.routes,
)
app.openapi = custom_openapi
The Generated OpenAPI JSON
Access the raw spec at /openapi.json:
curl http://localhost:8000/openapi.json | jq '.info'
{
"title": "Shop API",
"version": "2.1.0",
"description": "RESTful API for the online shop..."
}
You can use this JSON with any OpenAPI-compatible tool - Postman, Insomnia, openapi-generator, or API gateways.
Customising the OpenAPI Schema
Override the entire spec generation for advanced cases:
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom API",
version="3.0.0",
description="This is a custom OpenAPI schema",
routes=app.routes,
)
openapi_schema["info"]["x-logo"] = {
"url": "https://example.com/logo.png",
"altText": "Company logo",
}
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
Summary
┌──────────────┐ ┌──────────────────┐
│ FastAPI App │─────►│ /openapi.json │
│ (type hints)│ │ (OpenAPI 3.1) │
└──────┬───────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ /docs │ │ /redoc │
│ (Swagger UI)│ │ (ReDoc) │
└──────────────┘ └──────────────────┘
Key Takeaways
- FastAPI generates an OpenAPI 3.1 specification automatically from Python type hints, path decorators, and Pydantic models
- Two documentation UIs are built-in: Swagger UI at
/docs(interactive) and ReDoc at/redoc(reference-style) - Use the
FastAPI()constructor metadata (title,description,version,contact,license_info) to customise the generated docs - Tag endpoints with the
tagsparameter to group them in both Swagger UI and ReDoc - Always disable docs in production by setting
docs_url,redoc_url, andopenapi_urltoNone - The raw OpenAPI JSON at
/openapi.jsoncan be consumed by any OpenAPI-compatible tool