APIs are the backbone of modern software. Whether you’re building a mobile app, a web platform, or microservices, APIs define how systems communicate.
API design is the process of structuring endpoints, requests, and responses so developers can interact with your system clearly, predictably, and efficiently.
Good API design is not just about making things work. It directly impacts:
- Scalability — Can your API grow without breaking clients?
- Maintainability — Can your team evolve it safely?
- Developer Experience (DX) — Can others understand and use it quickly?
Poor API design leads to:
- Confusing integrations
- Frequent breaking changes
- Difficult debugging
- Security risks
- Slow development cycles
In this blog, we’ll explore common API design mistakes, how to avoid them, and practical best practices with real-world examples.
Common API Design Mistakes (With Examples)
1. Poor or Inconsistent Naming Conventions
The Mistake: Mixing plural/singular terms or using unclear names like GET /getUser or GET /user_data.
Why It’s a Problem:
- Hard to remember endpoints
- Confusing for developers
- Increases documentation dependency
The Solution: Use consistent, plural, resource-based naming.
Bad Example
GET /getUser
GET /usersList
GET /user_data
Better Example
GET /users
GET /users/{id}
Rule: Use consistent, plural, resource-based naming.
2. Using Verbs Instead of Nouns in REST Endpoints
The Mistake: Designing endpoints like actions (e.g., POST /createUser or DELETE /removeUser/123).
Why It’s a Problem: REST is resource-oriented. HTTP methods already define actions.
The Solution: Let HTTP verbs describe the action and URLs describe the resource.
Bad Example
POST /createUser
GET /fetchOrders
DELETE /removeUser/123
Better Example
POST /users
GET /orders
DELETE /users/123
Let HTTP verbs describe the action, and URLs describe the resource.
3. Inconsistent HTTP Status Codes
The Mistake: Returning 200 OK for everything, including errors.
Why It’s a Problem: Clients rely on status codes for logic. Wrong codes break error handling.
The Solution: Return semantic status codes like 404 Not Found for missing resources or 400 Bad Request for client errors.
Bad Example
200 OK
{
"error": "User not found"
}
Better Example
404 Not Found
{
"error": {
"code": "USER_NOT_FOUND",
"message": "User not found"
}
}
4. Ignoring Proper Error Handling
The Mistake: Returning raw messages, stack traces, or inconsistent error formats.
Why It’s a Problem:
- Hard to debug
- Security risk
- No structured handling on client
The Solution: Use a structured error object.
Bad Example
"Something went wrong"
Better Example
{
"error": {
"code": "INVALID_INPUT",
"message": "Email is required",
"details": {
"field": "email"
}
}
}
5. Overloading Single Endpoints
The Mistake: One endpoint is doing too many things based on flags or conditions.
Why It’s a Problem:
- Hard to maintain
- Difficult to test
- Breaks REST principles
Bad Example
POST /users?action=create|update|delete
Better Example
POST /users
PUT /users/{id}
DELETE /users/{id}
6. Not Versioning APIs
The Mistake: No versioning, causing existing apps to break when the API changes.
Why It’s a Problem: Existing apps break when API changes.
The Solution: Include the version in the URL (e.g., GET /v1/users).
Bad Example
GET /users
Better Example
GET /v1/users
GET /v2/users
7. Exposing Internal Database Structure
The Mistake: Returning raw database schema or internal field names.
Why It’s a Problem:
- Tight coupling with DB
- Security risks
- Hard to refactor later
Bad Example
{
"usr_tbl_id": 10,
"usr_tbl_nm": "Aman"
}
Better Example
{
"id": 10,
"name": "Aman"
}
8. Inconsistent Request & Response Formats
The Mistake: Different formats for different endpoints.
Why It’s a Problem: Clients must handle multiple patterns.
Bad Example
// Endpoint 1
{ "data": {...} }
// Endpoint 2
{ "user": {...} }
Better Example
{
"data": {...},
"meta": {...}
}
9. No Pagination, Filtering, or Sorting
The Mistake: Returning huge datasets without control.
Why It’s a Problem:
- Slow responses
- Memory issues
- Poor UX
Bad Example
GET /orders
Better Example
GET /orders?page=1&limit=20&sort=created_at&status=completed
10. Weak or Missing Authentication & Authorization
The Mistake:
- No authentication
- Role not validated
- Exposed sensitive data
Why It’s a Problem: Security vulnerabilities.
Bad Example
GET /admin/users
(No auth required)
Better Example
GET /admin/users
Authorization: Bearer
Advanced API Design Best Practices
1. RESTful Resource Naming
Principle
Use nouns, plural, and hierarchical structure.
Example
GET /users
GET /users/123
GET /users/123/orders
Improves clarity and predictability.
2. Proper Use of HTTP Methods
Example
POST /users
PUT /users/123
PATCH /users/123
DELETE /users/123
3. Standardized Error Response Format
Example
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request",
"details": {
"email": "Email is required"
}
}
}
Makes debugging easier.
4. Meaningful HTTP Status Codes
5. API Versioning Strategies
The version number is baked directly into the URI. When you make a breaking change (like renaming a field or changing the data structure), you increment the version.
- v1 (Current): GET /api/v1/users → Returns { “fullname”: “John Doe” }
- v2 (New): GET /api/v2/users → Returns { “first_name”: “John”, “last_name”: “Doe” }
This allows both versions to live side-by-side during a transition period.
6. Pagination, Filtering & Sorting
Don’t return huge datasets at once; it’s slow and memory-intensive.
- Example: GET /users?page=2&limit=10&sort=name&status=active.
- Response Tip: Include metadata in your response so the client knows the total number of items and pages.
Response Example
{
"data": [...],
"meta": {
"page": 2,
"limit": 10,
"total": 120
}
}
7. Idempotency & Safe Operations
Idempotent: Same request = same result.
PUT & DELETE must be idempotent.
Example
PUT /users/123
Sending twice should not create duplicates.
8. Security & Rate Limiting
- Authentication: Secure endpoints using JWT or OAuth2.
- Rate Limiting: Protect your server from DoS attacks and heavy load by limiting the number of requests a client can make in a given timeframe.
9. Documentation-First Approach
Use tools like OpenAPI/Swagger or Postman collections. This ensures clear contracts and easier collaboration between teams. Additionally, consider Automated Contract Testing (using tools like Prism) to ensure your API stays consistent with its documentation.
Benefits:
- Better planning
- Clear contracts
- Easier collaboration
10. PATCH vs. PUT (Partial Updates)
While PUT is used to replace an entire resource, PATCH should be used for partial updates.
- Example: If you only want to update a user’s email, a PATCH request would only contain the email field in the body, whereas a PUT request would require the entire user object.
11. Proper Use of Security Headers
- The Mistake: Focusing only on the body of the API and ignoring the HTTP headers that protect the transaction.
- The Solution: Always implement essential security headers to prevent common web vulnerabilities.
- Key Headers to Include:
- Strict-Transport-Security (HSTS): Forces HTTPS.
- Content-Type: application/json: Explicitly tells the client how to parse the data.
- X-Content-Type-Options: nosniff: Prevents the browser from “guessing” the content type.
Well-Designed API vs Poorly Designed API
Poor API
POST /createUser
GET /getUserData?id=10
POST /updateUser
Response
{
"status": "ok",
"msg": "done"
}
Problems:
- Verb-based
- No status codes
- No structure
- No versioning
- Hard to scale
Well-Designed API
POST /v1/users
GET /v1/users/10
PATCH /v1/users/10
Response
{
"data": {
"id": 10,
"name": "Aman"
}
}
Error
{
"error": {
"code": "USER_NOT_FOUND",
"message": "User not found"
}
}
Why better:
- RESTful
- Consistent
- Versioned
- Structured
- Scalable
Checklist for Designing a Good API
Conclusion
Good API design is not about making endpoints work — it’s about making them predictable, scalable, and easy for humans to use.
Avoid common mistakes like inconsistent naming, weak error handling, and lack of versioning. Follow proven best practices such as RESTful resource design, proper HTTP usage, structured responses, and strong security.
A well-designed API:
- Reduces bugs
- Improves developer experience
- Enables safe scaling
- Makes maintenance easier
