API Documentation with Swagger/OpenAPI
Why Document Your API?
An ML model API without documentation is like a restaurant without a menu — no one knows what they can order or how.
API documentation answers these questions for every consumer:
- What endpoints are available?
- What data do I need to send?
- What will I get back?
- What errors might occur?
- How do I authenticate?
The Real Cost of Poor Documentation
| Without Documentation | With Documentation |
|---|---|
| Developers email you every time they want to use the API | Self-service — developers read the docs |
| Integration takes days/weeks | Integration takes hours |
| Bugs caused by incorrect assumptions | Clear contracts prevent misunderstandings |
| Nobody uses your API (even if the model is great) | Adoption is frictionless |
| You spend 50% of your time answering questions | You spend that time improving the model |
A restaurant menu is the original API documentation:
- Endpoint = Dish category (appetizers, mains, desserts)
- Request = Your order (size, modifications, sides)
- Response = The dish delivered
- Status code = Waiter feedback ("coming right up!" vs "we're out of that")
- Examples = Photos of the dishes
OpenAPI Specification
The OpenAPI Specification (formerly Swagger Specification) is the industry standard for describing REST APIs. It's a machine-readable format (YAML or JSON) that fully describes your API.
Structure of an OpenAPI Document
OpenAPI YAML Example
openapi: 3.1.0
info:
title: ML Prediction API
description: Loan approval prediction service
version: 1.0.0
contact:
name: ML Team
email: ml-team@example.com
servers:
- url: http://localhost:8000
description: Development
- url: https://api.production.com
description: Production
paths:
/api/v1/predict:
post:
summary: Get a loan approval prediction
description: >
Submit loan application features and receive
an approval or denial prediction with confidence score.
tags:
- Predictions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PredictionInput'
example:
age: 35
income: 55000.0
credit_score: 720
employment_years: 8.5
loan_amount: 25000.0
responses:
'200':
description: Successful prediction
content:
application/json:
schema:
$ref: '#/components/schemas/PredictionOutput'
'422':
description: Validation error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal server error
/health:
get:
summary: Health check
tags:
- System
responses:
'200':
description: Service health status
components:
schemas:
PredictionInput:
type: object
required:
- age
- income
- credit_score
- employment_years
- loan_amount
properties:
age:
type: integer
minimum: 18
maximum: 120
description: Applicant age in years
income:
type: number
minimum: 0
description: Annual income in USD
credit_score:
type: integer
minimum: 300
maximum: 850
description: Credit score (FICO)
employment_years:
type: number
minimum: 0
description: Years of employment
loan_amount:
type: number
minimum: 0
description: Requested loan amount
PredictionOutput:
type: object
properties:
prediction:
type: string
enum: [approved, denied]
probability:
type: number
minimum: 0
maximum: 1
model_version:
type: string
timestamp:
type: string
format: date-time
ErrorResponse:
type: object
properties:
error_code:
type: string
message:
type: string
details:
type: array
items:
type: string
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
security:
- ApiKeyAuth: []
Swagger UI
Swagger UI renders your OpenAPI specification as an interactive web page where developers can:
- Browse all endpoints
- See request/response schemas
- Try out requests directly from the browser
- View example payloads
Swagger UI Layout
Auto-Generated Docs in FastAPI
FastAPI generates complete OpenAPI documentation automatically from your code. No additional configuration needed.
How FastAPI Generates Docs
View FastAPI Auto-Documentation Flow
Enhancing Auto-Docs
from fastapi import FastAPI, Query, Path, Body
from pydantic import BaseModel, Field
app = FastAPI(
title="Loan Prediction API",
description="""
## ML-Powered Loan Approval Predictions
This API serves a machine learning model that predicts
whether a loan application will be **approved** or **denied**.
### Features
- Real-time predictions with confidence scores
- Batch prediction support
- Model version tracking
### Authentication
All endpoints require an API key via the `X-API-Key` header.
""",
version="2.0.0",
contact={
"name": "ML Engineering Team",
"email": "ml-team@example.com",
},
license_info={
"name": "MIT",
"url": "https://opensource.org/licenses/MIT",
},
openapi_tags=[
{
"name": "Predictions",
"description": "Submit features and receive ML predictions",
},
{
"name": "Models",
"description": "Manage deployed ML models",
},
{
"name": "System",
"description": "Health checks and service status",
},
],
)
Adding Examples to Schemas
class PredictionInput(BaseModel):
age: int = Field(..., ge=18, le=120, description="Applicant age")
income: float = Field(..., gt=0, description="Annual income (USD)")
credit_score: int = Field(..., ge=300, le=850, description="FICO score")
employment_years: float = Field(..., ge=0, description="Years employed")
loan_amount: float = Field(..., gt=0, description="Loan amount (USD)")
class Config:
json_schema_extra = {
"examples": [
{
"summary": "Strong applicant",
"description": "High income, excellent credit",
"value": {
"age": 42,
"income": 95000,
"credit_score": 780,
"employment_years": 15,
"loan_amount": 30000,
},
},
{
"summary": "Average applicant",
"description": "Moderate income and credit",
"value": {
"age": 28,
"income": 45000,
"credit_score": 650,
"employment_years": 3,
"loan_amount": 15000,
},
},
{
"summary": "Risky applicant",
"description": "Low credit score, new employment",
"value": {
"age": 22,
"income": 28000,
"credit_score": 520,
"employment_years": 0.5,
"loan_amount": 20000,
},
},
]
}
Documenting Response Codes
from fastapi import HTTPException
from fastapi.responses import JSONResponse
@app.post(
"/api/v1/predict",
response_model=PredictionOutput,
responses={
200: {
"description": "Successful prediction",
"content": {
"application/json": {
"example": {
"prediction": "approved",
"probability": 0.87,
"model_version": "v2.1",
"timestamp": "2026-02-23T14:30:00Z",
}
}
},
},
422: {
"description": "Validation error — invalid input features",
"content": {
"application/json": {
"example": {
"detail": [
{
"loc": ["body", "credit_score"],
"msg": "ensure this value is >= 300",
"type": "value_error",
}
]
}
}
},
},
503: {
"description": "Model not loaded or unavailable",
},
},
summary="Get loan approval prediction",
description="Submit loan features and receive prediction with confidence.",
tags=["Predictions"],
)
def predict(input_data: PredictionInput):
...
Documentation in Flask with Flask-RESTX
Flask requires extensions for Swagger documentation. Flask-RESTX is the most popular choice.
Setting Up Flask-RESTX
from flask import Flask
from flask_restx import Api, Resource, fields
app = Flask(__name__)
api = Api(
app,
version="1.0",
title="Loan Prediction API",
description="ML-powered loan approval predictions",
doc="/docs",
authorizations={
"apikey": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key",
}
},
)
Comparison: FastAPI vs Flask-RESTX Documentation
| Aspect | FastAPI | Flask-RESTX |
|---|---|---|
| Setup effort | Zero (auto-generated) | Moderate (manual model definitions) |
| Schema source | Pydantic models | api.model() definitions |
| Validation | Automatic from schema | Optional (validate=True) |
| Examples | In Pydantic Config | In fields or @api.doc() |
| UI | Swagger + ReDoc | Swagger only |
| OpenAPI version | 3.1 | 2.0 (Swagger) |
| Maintenance | Schemas = validation = docs (single source) | Schemas and validation can diverge |
With FastAPI, your Pydantic models are simultaneously your validation rules and your documentation schemas. With Flask-RESTX, you define API models separately from your validation logic, which can lead to drift.
API Versioning
As your model evolves, you need to version your API so existing clients continue working while new clients use the latest version.
Versioning Strategies
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URL Path | /api/v1/predict | Clear, easy to route | URL changes for every version |
| Query Parameter | /predict?version=1 | URL stays the same | Easy to forget |
| Header | Accept: application/vnd.api.v1+json | Clean URLs | Less discoverable |
| Subdomain | v1.api.example.com | Clean separation | DNS/infra complexity |
URL Path Versioning (Recommended)
from fastapi import APIRouter
v1_router = APIRouter(prefix="/api/v1", tags=["v1"])
v2_router = APIRouter(prefix="/api/v2", tags=["v2"])
@v1_router.post("/predict")
def predict_v1(data: PredictionInputV1):
"""Uses model v1 — original feature set."""
result = ml_service_v1.predict(data.model_dump())
return result
@v2_router.post("/predict")
def predict_v2(data: PredictionInputV2):
"""Uses model v2 — expanded feature set with new fields."""
result = ml_service_v2.predict(data.model_dump())
return result
app.include_router(v1_router)
app.include_router(v2_router)
Generating Client SDKs
One of the most powerful features of OpenAPI is automatic client generation. From your OpenAPI spec, you can generate client libraries in any language.
Using openapi-generator
# Install
npm install @openapitools/openapi-generator-cli -g
# Generate Python client
openapi-generator-cli generate \
-i http://localhost:8000/openapi.json \
-g python \
-o ./generated-client/python
# Generate JavaScript client
openapi-generator-cli generate \
-i http://localhost:8000/openapi.json \
-g javascript \
-o ./generated-client/javascript
# Generate TypeScript client
openapi-generator-cli generate \
-i http://localhost:8000/openapi.json \
-g typescript-axios \
-o ./generated-client/typescript
View Documentation Generation Flow
Using the Generated Client
from prediction_api_client import PredictionApi, PredictionInput
client = PredictionApi(base_url="http://localhost:8000")
result = client.predict(PredictionInput(
age=35,
income=55000,
credit_score=720,
employment_years=8,
loan_amount=25000,
))
print(f"Prediction: {result.prediction}")
print(f"Confidence: {result.probability:.2%}")
Best Practices for API Documentation
1. Write for Your Audience
| Audience | What They Need |
|---|---|
| Frontend developers | Endpoint URLs, request/response examples, error codes |
| Data scientists | Input feature descriptions, model version info, output interpretation |
| DevOps engineers | Health endpoints, authentication, rate limits, deployment notes |
| Product managers | High-level API capabilities, limitations |
2. Documentation Checklist
- Every endpoint has a summary and description
- All request parameters are documented with types and constraints
- Response schemas include all fields with descriptions
- Error responses are documented with example payloads
- Authentication method is clearly explained
- Rate limits are documented
- At least 2 examples per endpoint (success + error)
- Model version and feature descriptions are included
- Changelog is maintained for API changes
- Getting started guide exists for new users
3. Keep Docs in Sync
If your documentation is generated from your code (FastAPI approach), it can never be out of date. If it's written separately, it will be out of date eventually.
Summary
| Topic | Key Takeaway |
|---|---|
| OpenAPI | Industry standard for describing REST APIs |
| Swagger UI | Interactive documentation generated from OpenAPI spec |
| FastAPI docs | Auto-generated from Pydantic models — zero extra effort |
| Flask docs | Requires Flask-RESTX extension — manual model definitions |
| Versioning | URL path versioning (/api/v1/) is simplest and most common |
| Client SDKs | Generate clients in any language from your OpenAPI spec |
| Best practice | Single source of truth — code = validation = docs |
Swagger / OpenAPI Quick Reference
| Tool | URL |
|---|---|
| FastAPI Swagger UI | http://localhost:8000/docs |
| FastAPI ReDoc | http://localhost:8000/redoc |
| FastAPI OpenAPI JSON | http://localhost:8000/openapi.json |
| Flask-RESTX Swagger | http://localhost:5000/docs |
| OpenAPI Generator | https://openapi-generator.tech/ |
| Swagger Editor | https://editor.swagger.io/ |
| OpenAPI Spec | https://spec.openapis.org/oas/latest.html |