AI Tools Compared

API documentation generated from code comments tends to be incomplete — developers document the “what” but skip the “why”, error cases, and usage examples. AI tools have moved the state of the art from “generate docstrings” to “generate complete API reference pages with examples, error tables, and usage patterns.”

Tools Compared

What Complete API Documentation Includes

For a REST endpoint:

## POST /api/v1/payments

### Authentication
Bearer token required. Token must have `payments:write` scope.

### Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| amount | integer | Yes | 1 - 10,000,000 | Amount in cents |
| currency | string | Yes | ISO 4217 | 3-letter currency code |
| source | string | Yes | Stripe token | Payment source token |

### Response (201 Created)
```json
{
  "id": "pay_1234abc",
  "amount": 2999,
  "currency": "USD",
  "status": "pending",
  "created_at": "2026-03-21T14:22:00Z"
}

Error Codes

| Status | Code | Description | |—|—|—| | 400 | invalid_amount | Amount is zero or negative | | 402 | card_declined | Card declined by issuer | | 422 | source_expired | Payment token is expired |


Most teams have the request body section. The error codes table and usage example are where documentation breaks down.

## Mintlify Writer

Mintlify Writer is a VS Code extension that generates docstrings and documentation from function code.

```python
# Before: undocumented FastAPI endpoint
@router.post("/payments")
async def create_payment(
    payload: PaymentCreateRequest,
    current_user: User = Depends(get_authenticated_user),
    db: Session = Depends(get_db),
) -> PaymentResponse:
    if payload.amount <= 0:
        raise HTTPException(status_code=400, detail="invalid_amount")
    charge = stripe.charge.create(
        amount=payload.amount,
        currency=payload.currency,
        source=payload.source,
    )
    payment = Payment.create(db, charge, current_user.id)
    return PaymentResponse.from_orm(payment)

Mintlify Writer generates:

    """
    Create a new payment transaction.

    Processes a payment charge through Stripe using the provided payment source.

    Args:
        payload: Payment creation data including amount (in cents), currency, and source token.
        current_user: Authenticated user making the request.
        db: Database session (injected by dependency).

    Returns:
        PaymentResponse: Created payment record with transaction ID and status.

    Raises:
        HTTPException (400): If amount is zero or negative.
        HTTPException (402): If Stripe declines the charge.
    """

The docstring is accurate and useful. Weakness: doesn’t document the request body fields individually, and misses error codes not visible from the function body.

Speakeasy

Speakeasy starts from OpenAPI specs and generates SDKs, reference docs, and usage guides.

pip install speakeasy-cli
speakeasy generate openapi --source ./app/main.py --out openapi.yaml
speakeasy generate docs --schema openapi.yaml --out ./docs

Speakeasy’s OpenAPI generation from FastAPI is strong. The generated spec includes all routes with request/response schemas. The main differentiator: it generates SDK code (Python, TypeScript, Go, Java) from the spec, with authentication handling and error types built in.

Limitation: the generated OpenAPI spec often misses error details that aren’t in the Pydantic model (exceptions thrown deep in the stack).

Swimm

Swimm focuses on documentation that stays current. It links doc sections to specific code locations and alerts you when the code changes without a doc update.

# Webhook Processing

Webhooks are verified using HMAC-SHA256 before processing. The verification
key is read from the `WEBHOOK_SECRET` environment variable.

<!-- Swimm links to: src/webhooks/processor.ts:processWebhook -->
The [`processWebhook` function](../src/webhooks/processor.ts) validates
the signature before dispatching to the appropriate handler.

When processWebhook is renamed or its signature changes, Swimm marks the documentation as “out of sync” and requires a review before the PR can merge. Swimm is about keeping existing documentation accurate — not generating new documentation from scratch.

Using Claude for One-Off API Documentation

For generating complete documentation for undocumented APIs, a structured Claude prompt produces thorough output:

Generate complete API reference documentation for this endpoint.
Include: description, authentication requirements, all request body fields
with types/constraints, response schema with example, all possible error codes
with causes, and a curl example.

Here is the FastAPI route code:
[paste route function]

Here is the Pydantic request model:
[paste PaymentCreateRequest model]

Here are all the custom exceptions this endpoint can raise:
[paste exception classes]

With all models and exceptions provided, Claude generates documentation that includes every error code and request field constraint — including the ones not visible from the route function alone.

Example Claude error table output:

Status Code Cause
400 invalid_amount Amount field is zero, negative, or exceeds 10,000,000 cents
400 invalid_currency Currency is not a valid ISO 4217 code
402 card_declined Issuer declined the charge
402 insufficient_funds Card has insufficient balance
422 source_expired Stripe token has been used or is expired

Comparison: When to Use Each Tool

Tool Best For Cost
Mintlify Writer Docstring generation as you code Freemium
Speakeasy OpenAPI to SDKs + reference docs Paid
Swimm Keeping existing docs current Paid
Claude (direct) Complete one-off documentation API cost

The pragmatic workflow: generate an OpenAPI spec from FastAPI/Express automatically, use Claude to generate human-readable reference pages with error tables and examples, then use Swimm to prevent those pages from going stale.

Generating TypeScript Types from API Responses

A useful doc artifact is a TypeScript client library. Most teams hand-maintain this.

# Using Speakeasy to auto-generate SDKs from OpenAPI

speakeasy generate sdk \
  --schema openapi.yaml \
  --out ./client-sdk-ts \
  --lang typescript

This generates:

// auto/generated/client.ts
export interface PaymentCreateRequest {
  amount: number; // in cents
  currency: string; // ISO 4217 code
  sourceToken: string; // Stripe token
}

export interface PaymentResponse {
  id: string;
  amount: number;
  currency: string;
  status: "pending" | "completed" | "failed";
  createdAt: Date;
}

export class PaymentsClient {
  async createPayment(request: PaymentCreateRequest): Promise<PaymentResponse> {
    // implementation
  }
}

Type-safe client code generated from the same schema that documents the API. No manual type duplication.

Interactive Documentation with Examples

Tools like Mintlify or ReadTheDocs can render markdown docs with embedded examples:

## POST /api/v1/payments

Create a payment charge through our gateway.

### Example Request

cURL:
\`\`\`bash
curl -X POST https://api.example.com/api/v1/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2999,
    "currency": "USD",
    "source": "tok_4242424242424242"
  }'
\`\`\`

Python:
\`\`\`python
import requests

response = requests.post(
    "https://api.example.com/api/v1/payments",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "amount": 2999,
        "currency": "USD",
        "source": "tok_4242424242424242"
    }
)
payment = response.json()

Mintlify automatically renders code blocks with syntax highlighting and tabs for language selection.

Error Documentation Best Practices

The most useful error docs include:

  1. HTTP Status Code — What the client receives
  2. Error Code — Machine-readable identifier (e.g., “INSUFFICIENT_FUNDS”)
  3. User-Facing Message — What to show in UI
  4. Developer Notes — How to fix it in code
  5. Retry Strategy — Is this retryable? Exponential backoff?

Example:

### Error: 402 Payment Required

**Code:** `card_declined`

**Message (to show users):** "Your card was declined. Please try another payment method or contact your bank."

**Developer Notes:**
This error occurs when the payment processor (Stripe, etc.) declines the charge.
Common causes:
- Insufficient funds
- Card expired
- Card flagged for fraud
- Processing limits exceeded

**Retry Strategy:** Do NOT retry automatically. This error requires user intervention.
Inform the user and request a different payment method.

**Example Response:**
\`\`\`json
{
  "error": {
    "code": "card_declined",
    "message": "Your card was declined",
    "charge_id": "ch_1234abc",
    "processor_message": "Card declined - contact issuer"
  }
}
\`\`\`

Maintaining Docs During Refactors

Using Swimm or similar tools prevents docs from going stale during refactors.

Scenario: You rename an API endpoint from /api/v1/users to /api/v2/users.

Without doc linking:

With Swimm:

  1. Create a doc linked to the route definition:
    <!-- Swimm links to: src/routes/users.ts:getUsersHandler -->
    The [`/api/v2/users` endpoint](../src/routes/users.ts) lists all users...
    
  2. When the route file changes, Swimm marks the doc as “out of sync”

  3. PR merges are blocked until the doc is reviewed and updated

This forces synchronization — outdated docs can’t be merged.

Cost Breakdown for Documentation Tools

Tool Setup Monthly Per-Endpoint Cost
Mintlify Writer (free) 10 min $0 ~5 min docstring generation
Mintlify (paid) 30 min $29 Hosting, analytics
Speakeasy SDK generation 20 min $0 (SDK) / varies (paid) Automatic from OpenAPI
Swimm 60 min (initial docs) $49 Doc sync auditing
Claude (direct) 5 min per endpoint API cost (~$0.01 per endpoint) Most flexible

For a 50-endpoint API:

Built by theluckystrike — More at zovo.one