Good API documentation reduces support burden and improves developer adoption. Poor documentation leads to frustration and errors. Here's how to document APIs effectively.
OpenAPI Specification#
1# openapi.yaml
2openapi: 3.1.0
3info:
4 title: User API
5 version: 1.0.0
6 description: |
7 API for managing users in the system.
8
9 ## Authentication
10 All endpoints require Bearer token authentication.
11
12 ## Rate Limiting
13 API is limited to 100 requests per minute per API key.
14
15servers:
16 - url: https://api.example.com/v1
17 description: Production
18 - url: https://api-staging.example.com/v1
19 description: Staging
20
21paths:
22 /users:
23 get:
24 summary: List users
25 description: Returns a paginated list of users
26 operationId: listUsers
27 tags:
28 - Users
29 parameters:
30 - name: limit
31 in: query
32 schema:
33 type: integer
34 minimum: 1
35 maximum: 100
36 default: 20
37 description: Number of users to return
38 - name: cursor
39 in: query
40 schema:
41 type: string
42 description: Pagination cursor from previous response
43 responses:
44 '200':
45 description: Successful response
46 content:
47 application/json:
48 schema:
49 $ref: '#/components/schemas/UserList'
50 example:
51 data:
52 - id: "usr_123"
53 email: "jane@example.com"
54 name: "Jane Doe"
55 createdAt: "2024-01-15T10:30:00Z"
56 pagination:
57 hasMore: true
58 nextCursor: "eyJpZCI6InVzcl8xMjMifQ=="
59 '401':
60 $ref: '#/components/responses/Unauthorized'
61
62 post:
63 summary: Create user
64 operationId: createUser
65 tags:
66 - Users
67 requestBody:
68 required: true
69 content:
70 application/json:
71 schema:
72 $ref: '#/components/schemas/CreateUserInput'
73 examples:
74 basic:
75 summary: Basic user creation
76 value:
77 email: "jane@example.com"
78 name: "Jane Doe"
79 withRole:
80 summary: Create admin user
81 value:
82 email: "admin@example.com"
83 name: "Admin User"
84 role: "admin"
85 responses:
86 '201':
87 description: User created
88 content:
89 application/json:
90 schema:
91 $ref: '#/components/schemas/User'
92 '400':
93 $ref: '#/components/responses/ValidationError'
94
95components:
96 schemas:
97 User:
98 type: object
99 required:
100 - id
101 - email
102 - name
103 - createdAt
104 properties:
105 id:
106 type: string
107 example: "usr_123"
108 email:
109 type: string
110 format: email
111 name:
112 type: string
113 role:
114 type: string
115 enum: [user, admin]
116 default: user
117 createdAt:
118 type: string
119 format: date-time
120
121 CreateUserInput:
122 type: object
123 required:
124 - email
125 - name
126 properties:
127 email:
128 type: string
129 format: email
130 description: Must be unique
131 name:
132 type: string
133 minLength: 1
134 maxLength: 100
135 role:
136 type: string
137 enum: [user, admin]
138
139 Error:
140 type: object
141 properties:
142 error:
143 type: string
144 code:
145 type: string
146 details:
147 type: array
148 items:
149 type: object
150 properties:
151 field:
152 type: string
153 message:
154 type: string
155
156 responses:
157 Unauthorized:
158 description: Authentication required
159 content:
160 application/json:
161 schema:
162 $ref: '#/components/schemas/Error'
163 example:
164 error: "Unauthorized"
165 code: "AUTH_REQUIRED"
166
167 ValidationError:
168 description: Validation failed
169 content:
170 application/json:
171 schema:
172 $ref: '#/components/schemas/Error'
173 example:
174 error: "Validation failed"
175 code: "VALIDATION_ERROR"
176 details:
177 - field: "email"
178 message: "Must be a valid email"
179
180 securitySchemes:
181 bearerAuth:
182 type: http
183 scheme: bearer
184 bearerFormat: JWT
185
186security:
187 - bearerAuth: []Code Examples#
1// Include examples in multiple languages
2
3const examples = {
4 curl: `
5curl -X POST https://api.example.com/v1/users \\
6 -H "Authorization: Bearer YOUR_API_KEY" \\
7 -H "Content-Type: application/json" \\
8 -d '{
9 "email": "jane@example.com",
10 "name": "Jane Doe"
11 }'
12`,
13
14 javascript: `
15const response = await fetch('https://api.example.com/v1/users', {
16 method: 'POST',
17 headers: {
18 'Authorization': 'Bearer YOUR_API_KEY',
19 'Content-Type': 'application/json',
20 },
21 body: JSON.stringify({
22 email: 'jane@example.com',
23 name: 'Jane Doe',
24 }),
25});
26
27const user = await response.json();
28console.log(user);
29`,
30
31 python: `
32import requests
33
34response = requests.post(
35 'https://api.example.com/v1/users',
36 headers={'Authorization': 'Bearer YOUR_API_KEY'},
37 json={
38 'email': 'jane@example.com',
39 'name': 'Jane Doe',
40 }
41)
42
43user = response.json()
44print(user)
45`,
46
47 ruby: `
48require 'net/http'
49require 'json'
50
51uri = URI('https://api.example.com/v1/users')
52http = Net::HTTP.new(uri.host, uri.port)
53http.use_ssl = true
54
55request = Net::HTTP::Post.new(uri)
56request['Authorization'] = 'Bearer YOUR_API_KEY'
57request['Content-Type'] = 'application/json'
58request.body = {
59 email: 'jane@example.com',
60 name: 'Jane Doe'
61}.to_json
62
63response = http.request(request)
64user = JSON.parse(response.body)
65puts user
66`,
67};Interactive Documentation#
1// Generate interactive docs with Swagger UI or Redoc
2import swaggerUi from 'swagger-ui-express';
3import YAML from 'yamljs';
4
5const swaggerDocument = YAML.load('./openapi.yaml');
6
7app.use(
8 '/docs',
9 swaggerUi.serve,
10 swaggerUi.setup(swaggerDocument, {
11 customCss: '.swagger-ui .topbar { display: none }',
12 customSiteTitle: 'API Documentation',
13 swaggerOptions: {
14 persistAuthorization: true,
15 tryItOutEnabled: true,
16 },
17 })
18);
19
20// Or use Redoc for cleaner documentation
21import redoc from 'redoc-express';
22
23app.get(
24 '/docs',
25 redoc({
26 title: 'API Documentation',
27 specUrl: '/openapi.json',
28 nonce: '',
29 redocOptions: {
30 theme: {
31 colors: {
32 primary: { main: '#3b82f6' },
33 },
34 },
35 hideDownloadButton: false,
36 expandResponses: '200,201',
37 },
38 })
39);Error Documentation#
1# Document all error responses
2components:
3 responses:
4 BadRequest:
5 description: Invalid request
6 content:
7 application/json:
8 schema:
9 $ref: '#/components/schemas/Error'
10 examples:
11 validation:
12 summary: Validation error
13 value:
14 error: "Validation failed"
15 code: "VALIDATION_ERROR"
16 details:
17 - field: "email"
18 message: "Invalid email format"
19 missing_field:
20 summary: Missing required field
21 value:
22 error: "Missing required field: name"
23 code: "MISSING_FIELD"
24
25 NotFound:
26 description: Resource not found
27 content:
28 application/json:
29 example:
30 error: "User not found"
31 code: "NOT_FOUND"
32
33 RateLimited:
34 description: Rate limit exceeded
35 headers:
36 X-RateLimit-Limit:
37 description: Request limit per minute
38 schema:
39 type: integer
40 X-RateLimit-Remaining:
41 description: Remaining requests
42 schema:
43 type: integer
44 X-RateLimit-Reset:
45 description: Reset timestamp
46 schema:
47 type: integer
48 content:
49 application/json:
50 example:
51 error: "Rate limit exceeded"
52 code: "RATE_LIMITED"
53 retryAfter: 30Changelog#
1# API Changelog
2
3## v1.2.0 (2024-02-01)
4
5### Added
6- `GET /users/{id}/activity` - Get user activity history
7- `role` field to User object
8
9### Changed
10- Increased rate limit from 60 to 100 requests/minute
11
12### Deprecated
13- `GET /users/{id}/logs` - Use `/users/{id}/activity` instead
14
15## v1.1.0 (2024-01-15)
16
17### Added
18- Pagination support with cursor-based pagination
19- `filter` query parameter on `GET /users`
20
21### Fixed
22- Email validation now accepts plus signs
23
24## v1.0.0 (2024-01-01)
25
26Initial release with:
27- User CRUD operations
28- Authentication endpoints
29- Rate limitingSDK Generation#
1// Generate SDKs from OpenAPI spec
2// package.json script
3{
4 "scripts": {
5 "generate:sdk:ts": "openapi-typescript-codegen --input ./openapi.yaml --output ./sdk/typescript",
6 "generate:sdk:python": "openapi-python-client generate --path ./openapi.yaml --output-path ./sdk/python"
7 }
8}
9
10// Generated TypeScript client
11import { UserApi, Configuration } from '@yourcompany/api-client';
12
13const config = new Configuration({
14 basePath: 'https://api.example.com/v1',
15 headers: {
16 Authorization: `Bearer ${apiKey}`,
17 },
18});
19
20const api = new UserApi(config);
21
22// Fully typed!
23const users = await api.listUsers({ limit: 10 });
24const newUser = await api.createUser({
25 createUserInput: {
26 email: 'jane@example.com',
27 name: 'Jane Doe',
28 },
29});Best Practices#
Content:
✓ Start with quickstart guide
✓ Include authentication examples
✓ Document all error codes
✓ Provide code examples in multiple languages
Structure:
✓ Group endpoints logically
✓ Use consistent naming
✓ Include request/response examples
✓ Document pagination patterns
Maintenance:
✓ Keep docs in sync with code
✓ Version your API docs
✓ Maintain a changelog
✓ Test documentation accuracy
Conclusion#
Good API documentation is essential for developer adoption. Use OpenAPI for specification, provide interactive docs, include code examples, and document errors thoroughly. Keep documentation in sync with your API and maintain a changelog for updates.