What Makes a Good REST API?
A good API is predictable, consistent, and self-explanatory. Developers shouldn't need to read documentation to guess what GET /users/123 does. Here are the practices that separate professional APIs from confusing ones.
1. Use Nouns for Resources, Not Verbs
Resources are things, not actions. The HTTP method expresses the action.
✓ GET /articles
✓ GET /articles/42
✓ POST /articles
✓ PUT /articles/42
✓ DELETE /articles/42
✗ GET /getArticles
✗ POST /createArticle
✗ GET /deleteArticle?id=42
2. Use Correct HTTP Methods
| Method | Meaning | Idempotent? |
|---|---|---|
| GET | Retrieve resource | Yes |
| POST | Create resource | No |
| PUT | Replace entire resource | Yes |
| PATCH | Partial update | No |
| DELETE | Delete resource | Yes |
Use PATCH for partial updates, not PUT. PUT replaces the entire resource.
3. Return Correct HTTP Status Codes
200 OK — successful GET, PATCH, PUT
201 Created — successful POST (include Location header)
204 No Content — successful DELETE
400 Bad Request — invalid input (include error details)
401 Unauthorized — missing/invalid auth token
403 Forbidden — authenticated but not authorized
404 Not Found — resource doesn't exist
409 Conflict — e.g., email already taken
422 Unprocessable — validation errors
429 Too Many Requests — rate limited
500 Internal Server Error — unexpected server error
4. Consistent Error Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address"
}
]
}
}
Always return errors in the same shape so clients can handle them generically.
5. API Versioning
Include the version in the URL path:
/api/v1/users
/api/v2/users
Never break existing clients. When you need breaking changes, create a new version. Maintain v1 until clients migrate.
6. Pagination for Collections
GET /articles?page=2&limit=20
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 347,
"totalPages": 18,
"hasNext": true,
"hasPrev": true
}
}
For large datasets, cursor-based pagination is more efficient:
GET /articles?cursor=eyJpZCI6MTAwfQ&limit=20
{
"data": [...],
"nextCursor": "eyJpZCI6MTIwfQ",
"hasMore": true
}
7. Filtering, Sorting, and Field Selection
GET /articles?status=published&author=alex
GET /articles?sort=created_at:desc
GET /articles?fields=id,title,author
8. Nested Resources for Relationships
GET /users/42/orders
GET /users/42/orders/99
POST /users/42/orders
Don't nest more than two levels deep — it gets unwieldy.
9. Request/Response Naming Conventions
Stick to one convention throughout. camelCase for JSON (JavaScript convention):
{
"userId": 42,
"firstName": "Alex",
"createdAt": "2025-04-01T10:00:00Z"
}
Use ISO 8601 for all dates.
10. Authentication Headers
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Never pass tokens in query strings (they appear in server logs).
11. Rate Limiting Response Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 43
X-RateLimit-Reset: 1714953600
Retry-After: 30
Conclusion
Good API design is about empathy for the developer using your API. Consistent naming, correct status codes, predictable error formats, and thoughtful versioning make the difference between an API people love and one they dread. Write the API you'd want to consume.