Cheatsheet for OpenAPI customization in FastAPI.
Title / version / contact
app = FastAPI(
title="MyAPI",
version="1.2.3",
description="What it does. Markdown supported.",
summary="Short summary",
terms_of_service="https://yourdomain.com/tos",
contact={"name": "Manvendra", "url": "https://rajpoot.dev/", "email": "..."},
license_info={"name": "MIT", "url": "https://opensource.org/licenses/MIT"},
)
Server URLs
app = FastAPI(
servers=[
{"url": "https://api.yourdomain.com", "description": "Production"},
{"url": "https://api.staging.yourdomain.com", "description": "Staging"},
],
)
Exclude routes from schema
@app.get("/internal", include_in_schema=False)
async def internal(): ...
Useful for /healthz, /metrics, internal admin.
Tags + metadata
tags_metadata = [
{"name": "users", "description": "User management"},
{"name": "admin", "description": "Admin only", "externalDocs": {"url": "https://docs..."}},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/users", tags=["users"])
async def list_(): ...
Per-route operation_id
@app.get("/users/{id}", operation_id="get_user_by_id")
Codegen tools use operation_id for method names. Stable IDs prevent SDK churn.
Override operation_id format
def custom_op_id(route: APIRoute):
return f"{route.tags[0]}_{route.name}" if route.tags else route.name
app = FastAPI()
# After routes registered:
for route in app.routes:
if isinstance(route, APIRoute):
route.operation_id = custom_op_id(route)
Multiple responses
@app.get(
"/users/{id}",
response_model=UserOut,
responses={
404: {"description": "Not found", "model": ErrorOut},
429: {"description": "Rate limited"},
},
)
async def get_(id: int): ...
Examples in body
class UserCreate(BaseModel):
email: EmailStr
age: int
@app.post("/users")
async def create(user: UserCreate = Body(
examples={
"happy": {"summary": "Happy path", "value": {"email": "[email protected]", "age": 30}},
"edge": {"summary": "Edge case", "value": {"email": "[email protected]", "age": 0}},
}
)): ...
Securityschemes (OAuth2)
oauth2 = OAuth2PasswordBearer(
tokenUrl="token",
scopes={"read": "...", "write": "..."},
)
Auto-shows in Swagger “Authorize.”
Custom Swagger UI / ReDoc
app = FastAPI(
docs_url=None, # disable default
redoc_url=None,
openapi_url="/openapi.json",
)
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
@app.get("/docs", include_in_schema=False)
async def custom_docs():
return get_swagger_ui_html(
openapi_url=app.openapi_url,
title=app.title + " | Swagger",
swagger_js_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
swagger_css_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
swagger_ui_parameters={
"persistAuthorization": True,
"tryItOutEnabled": True,
"displayRequestDuration": True,
},
)
Disable docs in prod
app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
For private APIs.
Customize the OpenAPI schema
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
schema = get_openapi(
title=app.title, version=app.version, routes=app.routes,
)
schema["info"]["x-logo"] = {"url": "https://yourdomain.com/logo.png"}
schema["components"]["securitySchemes"] = {...}
app.openapi_schema = schema
return schema
app.openapi = custom_openapi
Generate TS SDK
npx openapi-typescript-codegen --input http://localhost:8000/openapi.json --output sdk
Or openapi-fetch for runtime-typed clients.
Generate Python SDK
openapi-python-client generate --url http://localhost:8000/openapi.json
Useful for service-to-service clients.
Deprecating endpoints
@app.get("/v1/old", deprecated=True)
async def old(): ...
Swagger marks it. Combine with Deprecation and Sunset headers.
Read this next
If you want my OpenAPI customization examples, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .