API Design Guide: Building APIs Your Users Will Love
A poorly designed API is a tax on every developer who uses it. A well-designed API feels natural, is self-documenting, and scales without breaking. Here's how to build the latter.
REST API Principles
1. Use Nouns, Not Verbs
Bad:
```
GET /api/getUsers
GET /api/createUser
DELETE /api/deleteUserById?id=123
```
Good:
```
GET /api/users
POST /api/users
DELETE /api/users/123
```
The HTTP verb (GET, POST, DELETE, etc.) is your action. The URL is the resource.
2. HTTP Status Codes Matter
Don't use 200 for everything:
```
200 OK → Request succeeded
201 Created → Resource created
204 No Content → Success, no response body
400 Bad Request → Client error in request
401 Unauthorized → Authentication required
403 Forbidden → Authenticated but not allowed
404 Not Found → Resource doesn't exist
500 Server Error → Something broke on our side
```
Use the right status code. It tells clients what happened.
3. Consistent Response Format
Every response should follow the same structure:
```json
{
"success": true,
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"error": null
}
```
For errors:
```json
{
"success": false,
"data": null,
"error": {
"code": "INVALID_EMAIL",
"message": "Email is not valid"
}
}
```
Consistency makes client code predictable.
4. Pagination for Lists
Never return millions of records at once:
```
GET /api/users?page=1&limit=50
```
Response:
```json
{
"data": [/ 50 users /],
"pagination": {
"page": 1,
"limit": 50,
"total": 5000,
"pages": 100
}
}
```
Always include total count so clients know how many pages exist.
5. Filtering & Sorting
Let clients specify what data they want:
```
GET /api/users?status=active&sort=created_at:desc&limit=20
```
Key patterns:
- `?status=active` — Filter by status
Versioning Strategy
APIs change. Plan for it:
Option 1: URL Versioning (Simple)
```
/api/v1/users
/api/v2/users
```
Option 2: Header Versioning (Clean)
```
GET /api/users
Header: Accept: application/json;version=2
```
Best practice: Maintain two major versions. When you release v3, keep v2 alive for 12 months. Gives clients time to migrate.
Documentation is Part of the API
Your API is only as good as its documentation. Use OpenAPI/Swagger:
```yaml
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: List users
parameters:
- name: status
in: query
schema:
type: string
description: Filter by user status
responses:
'200':
description: List of users
```
Tools like Swagger UI auto-generate interactive documentation.
Authentication & Authorization
Don't Use Basic Auth in Production
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
Use JWT tokens or OAuth2. Key requirements:
Error Handling
Don't leak internals:
Bad:
```json
{
"error": "NullPointerException at line 42 in UserService.java"
}
```
Good:
```json
{
"error": {
"code": "USER_NOT_FOUND",
"message": "The specified user does not exist"
}
}
```
Users don't need stack traces. Give them enough to understand what went wrong.
Performance Considerations
Caching Headers
```
Cache-Control: public, max-age=3600
```
Tells clients "you can cache this for 1 hour". Reduces server load.
Compression
Use gzip compression for responses:
```
Content-Encoding: gzip
```
Reduces payload size by 70-90%.
Async Operations
For long operations, don't make clients wait:
```
POST /api/reports
→ 202 Accepted
→ Returns: { id: "report-123", status_url: "/api/reports/report-123/status" }
Client polls: GET /api/reports/report-123/status
→ { status: "processing" } or { status: "completed", data: ... }
```
API Security Checklist
✅ HTTPS everywhere
✅ Input validation (don't trust client input)
✅ Rate limiting
✅ Authentication required for sensitive endpoints
✅ Authorization (authenticated ≠ authorized)
✅ Avoid exposing sensitive data (passwords, PII)
✅ Log security events
✅ Update dependencies regularly
✅ Penetration testing
✅ Data encryption at rest
Deprecation & Lifecycle
Plan for sunset:
1. Announce deprecation 6 months before
2. Mark endpoints as deprecated in docs
3. Return deprecation header
4. Log which clients use old endpoints
5. Turn off after 12 months
```
Deprecation: true
Sunset: Sun, 01 Jan 2025 00:00:00 GMT
```
Testing Your API
Don't just test success cases:
```
✅ Valid requests work
❌ Invalid requests return 400
❌ Missing authentication returns 401
❌ Unauthorized access returns 403
❌ Missing resources return 404
❌ Rate limiting works
❌ XSS/SQL injection blocked
```
API Design Checklist
✅ Resource-oriented (nouns, not verbs)
✅ Correct HTTP status codes
✅ Consistent response format
✅ Pagination for lists
✅ Filtering/sorting support
✅ Versioning strategy
✅ OpenAPI documentation
✅ Authentication/Authorization
✅ Error handling with codes
✅ Caching headers
✅ Compression enabled
✅ Rate limiting
✅ Security hardened
✅ Deprecation plan
Real-World Example
At mznah, we built the MZ Listing API following these principles:
Result: Developers love using it. Integration partners rarely complain about the API itself.
Tools We Recommend
Key Takeaway
Your API is a product. Treat it like one. Design for your users, not just for your convenience. A good API scales, doesn't break, and feels natural to use.
Want help designing your API? Let's talk.