إنتقل إلى المحتوى الرئيسي

API Documentation with Swagger/OpenAPI

Theory 45 min

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 DocumentationWith Documentation
Developers email you every time they want to use the APISelf-service — developers read the docs
Integration takes days/weeksIntegration takes hours
Bugs caused by incorrect assumptionsClear contracts prevent misunderstandings
Nobody uses your API (even if the model is great)Adoption is frictionless
You spend 50% of your time answering questionsYou spend that time improving the model
The "Menu" Analogy

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

AspectFastAPIFlask-RESTX
Setup effortZero (auto-generated)Moderate (manual model definitions)
Schema sourcePydantic modelsapi.model() definitions
ValidationAutomatic from schemaOptional (validate=True)
ExamplesIn Pydantic ConfigIn fields or @api.doc()
UISwagger + ReDocSwagger only
OpenAPI version3.12.0 (Swagger)
MaintenanceSchemas = validation = docs (single source)Schemas and validation can diverge
Single Source of Truth

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

StrategyExampleProsCons
URL Path/api/v1/predictClear, easy to routeURL changes for every version
Query Parameter/predict?version=1URL stays the sameEasy to forget
HeaderAccept: application/vnd.api.v1+jsonClean URLsLess discoverable
Subdomainv1.api.example.comClean separationDNS/infra complexity
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

AudienceWhat They Need
Frontend developersEndpoint URLs, request/response examples, error codes
Data scientistsInput feature descriptions, model version info, output interpretation
DevOps engineersHealth endpoints, authentication, rate limits, deployment notes
Product managersHigh-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

The Golden Rule

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

TopicKey Takeaway
OpenAPIIndustry standard for describing REST APIs
Swagger UIInteractive documentation generated from OpenAPI spec
FastAPI docsAuto-generated from Pydantic models — zero extra effort
Flask docsRequires Flask-RESTX extension — manual model definitions
VersioningURL path versioning (/api/v1/) is simplest and most common
Client SDKsGenerate clients in any language from your OpenAPI spec
Best practiceSingle source of truth — code = validation = docs
Swagger / OpenAPI Quick Reference
ToolURL
FastAPI Swagger UIhttp://localhost:8000/docs
FastAPI ReDochttp://localhost:8000/redoc
FastAPI OpenAPI JSONhttp://localhost:8000/openapi.json
Flask-RESTX Swaggerhttp://localhost:5000/docs
OpenAPI Generatorhttps://openapi-generator.tech/
Swagger Editorhttps://editor.swagger.io/
OpenAPI Spechttps://spec.openapis.org/oas/latest.html