Published on 2026-02-22
Creating API Documentation in Markdown
How to write clear, complete, and maintainable API documentation using Markdown — covering endpoints, request/response schemas, code examples, and authentication.
API documentation is the face of your API. Good API docs turn a confusing interface into a joy to use — and bad ones turn a great API into an abandoned one. This guide covers how to write excellent API documentation in Markdown.
What Good API Docs Include
At a minimum, every API should document:
- Authentication — how to obtain and pass credentials
- Base URL — the root URL for all endpoints
- Endpoints — path, method, parameters, request body, response
- Error codes — what failure responses look like
- Rate limits — how many requests are allowed
- Code examples — working code in multiple languages
Document Structure
# My API Reference
## Overview
## Authentication
## Rate Limiting
## Endpoints
### Users
#### GET /users
#### POST /users
#### GET /users/{id}
#### PUT /users/{id}
#### DELETE /users/{id}
### Documents
#### GET /documents
...
## Error Reference
## Changelog
Documenting Authentication
## Authentication
All API requests require a bearer token in the `Authorization` header.
**Obtaining a token:**
```bash
curl -X POST https://api.example.com/auth/token \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "secret"}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1...",
"token_type": "bearer",
"expires_in": 3600
}
Using the token:
curl https://api.example.com/api/users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1..."
## Documenting an Endpoint
Use a consistent template for every endpoint:
```markdown
### GET /api/documents
Returns a paginated list of documents belonging to the authenticated user.
**Query Parameters**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | `1` | Page number |
| `per_page` | integer | `20` | Results per page (max: 100) |
| `is_public` | boolean | — | Filter by visibility |
| `search` | string | — | Full-text search query |
**Example Request**
```bash
curl "https://api.example.com/api/documents?page=1&per_page=10" \
-H "Authorization: Bearer {token}"
Example Response
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Q1 Report",
"is_public": false,
"created_at": "2026-01-15T09:00:00Z",
"updated_at": "2026-02-01T14:30:00Z"
}
],
"meta": {
"total": 42,
"page": 1,
"per_page": 10,
"total_pages": 5
}
}
Status Codes
| Code | Description |
|---|---|
200 |
Success |
401 |
Missing or invalid token |
422 |
Invalid query parameter |
## Documenting Request Bodies
For `POST` and `PUT` endpoints, document the request body schema:
```markdown
### POST /api/documents
Creates a new document.
**Request Body** (`application/json`)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | ✅ | Document title (max 255 chars) |
| `content` | string | ✅ | Markdown content |
| `is_public` | boolean | ❌ | Public visibility. Default: `false` |
| `tags` | string[] | ❌ | Array of tag strings |
Documenting Errors
Create a central error reference so readers don’t have to hunt:
## Error Reference
All errors follow this format:
```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description",
"details": [
{ "field": "title", "issue": "must not be empty" }
]
}
}
| HTTP Code | Error Code | Description |
|---|---|---|
400 |
VALIDATION_ERROR |
Request body failed validation |
401 |
UNAUTHORIZED |
Token missing or expired |
403 |
FORBIDDEN |
Token valid but lacks permission |
404 |
NOT_FOUND |
Resource does not exist |
429 |
RATE_LIMITED |
Too many requests |
500 |
INTERNAL_ERROR |
Server error — contact support |
## Multi-Language Code Examples
Provide examples in multiple languages. Tabbed examples help readers find their language quickly. A table of short snippets also works:
```markdown
**cURL**
```bash
curl -X POST https://api.example.com/api/documents \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"title": "My Doc", "content": "# Hello"}'
Python
import requests
response = requests.post(
"https://api.example.com/api/documents",
headers={"Authorization": f"Bearer {token}"},
json={"title": "My Doc", "content": "# Hello"}
)
print(response.json())
JavaScript
const response = await fetch('https://api.example.com/api/documents', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ title: 'My Doc', content: '# Hello' }),
});
const data = await response.json();
## Keeping API Docs Current
The most common problem with API documentation is drift — the docs say one thing and the API does another. Solutions:
- **Generate docs from code** — use tools like OpenAPI/Swagger or TypeDoc to auto-generate from annotations
- **Test your examples** — run curl examples as part of your CI to detect when they break
- **Version your docs** — keep separate documentation for each major API version
- **Add a changelog** — document every change in a `## Changelog` section so integrators know what changed