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 .