Why REST API Design Mistakes Cost More Than You Think
REST APIs power nearly every modern application. They connect frontends to backends, services to services, and businesses to partners. Yet even experienced developers fall into the same traps over and over again.
The problem? Most REST API design mistakes don’t break anything immediately. They create friction that compounds over time: confused consumers, painful migrations, broken integrations, and support tickets that never stop coming.
In this guide, we cover the 10 most frequent REST API design mistakes we see in real-world projects, and we show you exactly how to fix each one with practical code examples. Whether you are building your first API or refactoring an existing one, this list will save you hours of headaches.
1. Inconsistent Naming Conventions
The Mistake
Mixing naming styles across your endpoints is one of the most common REST API design mistakes. You end up with a chaotic surface that confuses every developer who touches it.
GET /getUsers
GET /fetch-orders
POST /CreateProduct
GET /user_profile
Four endpoints, four different conventions. This makes the API unpredictable and hard to learn.
The Fix
Use lowercase, plural nouns with hyphens (kebab-case) for all resource paths. Never use verbs in the URL since the HTTP method already describes the action.
GET /users
GET /orders
POST /products
GET /user-profiles
Rules to follow:
- Always use plural nouns for collections:
/users, not/user - Use hyphens to separate words:
/order-items, not/orderItems - Keep everything lowercase
- Let HTTP methods (GET, POST, PUT, DELETE) express the action

2. Poor Error Response Format
The Mistake
Returning plain text errors, inconsistent structures, or worse, a 200 OK status with an error message buried in the body.
// Bad: 200 OK with error in body
{
"success": false,
"message": "Something went wrong"
}
// Also bad: plain string
"User not found"
The Fix
Adopt a consistent error response format across your entire API. Use proper HTTP status codes and return structured error objects. The RFC 7807 Problem Details standard is an excellent foundation.
// 404 Not Found
{
"type": "https://api.boxsoftware.net/errors/resource-not-found",
"title": "Resource Not Found",
"status": 404,
"detail": "No user found with ID 4523.",
"instance": "/users/4523"
}
// 422 Unprocessable Entity
{
"type": "https://api.boxsoftware.net/errors/validation-error",
"title": "Validation Error",
"status": 422,
"detail": "One or more fields failed validation.",
"errors": [
{
"field": "email",
"message": "Must be a valid email address."
},
{
"field": "age",
"message": "Must be a positive integer."
}
]
}
Error messages should be clear, concise, and actionable. The developer reading them should immediately know what went wrong and how to fix it.
3. Missing or Broken Pagination
The Mistake
Returning every record in a single response. This might work during development with 15 test records, but in production with 500,000 rows, it will crush your server and your client.
GET /orders
// Returns all 500,000 orders in one response
The Fix
Implement pagination from day one. Cursor-based pagination is more performant and stable than offset-based for large datasets, but offset-based is simpler to start with.
Offset-based Pagination
GET /orders?page=2&per_page=25
{
"data": [ ... ],
"pagination": {
"current_page": 2,
"per_page": 25,
"total_items": 4830,
"total_pages": 194,
"next": "/orders?page=3&per_page=25",
"previous": "/orders?page=1&per_page=25"
}
}
Cursor-based Pagination
GET /orders?cursor=eyJpZCI6MTAwfQ==&limit=25
{
"data": [ ... ],
"pagination": {
"next_cursor": "eyJpZCI6MTI1fQ==",
"has_more": true
}
}
Also consider adding:
- Sorting:
?sort=created_at&order=desc - Filtering:
?status=active&created_after=2026-01-01 - Field selection:
?fields=id,name,email
4. Ignoring API Versioning
The Mistake
Shipping your API without any versioning strategy. The moment you need to introduce a breaking change, you have two options: break every existing client, or hack around the problem with ugly workarounds.
GET /users // Version? What version?
The Fix
Add versioning on day one. It takes minutes to set up and saves you from painful migrations later. URI-based versioning is the most common and easiest to understand.
GET /api/v1/users
GET /api/v2/users
Here is a quick comparison of the most popular versioning strategies:
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URI Path | /api/v1/users |
Simple, visible, easy to route | URL changes between versions |
| Header | Accept: application/vnd.api+json;version=1 |
Clean URLs | Harder to test and discover |
| Query Param | /users?version=1 |
Easy to implement | Easy to forget, clutters params |
Important: Do not create a new version for every minor change. Reserve new versions for breaking changes. Document what changed between versions, and give consumers a clear deprecation timeline.

5. Using Wrong HTTP Status Codes
The Mistake
Returning 200 OK for everything, or using status codes incorrectly. A 200 on a failed validation, a 500 when a resource is simply missing, or a 201 for an update.
The Fix
Use HTTP status codes as they were intended. Here are the ones you will use most often:
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST that creates a resource |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Malformed request syntax |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but not allowed |
| 404 | Not Found | Resource does not exist |
| 409 | Conflict | Duplicate resource or state conflict |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server failure |
6. Not Handling Idempotency
The Mistake
Allowing the same POST request to create duplicate resources when the client retries due to a network timeout. This leads to duplicate orders, double charges, or repeated notifications.
The Fix
Implement idempotency keys for non-idempotent operations. The client sends a unique key with the request, and the server uses it to detect duplicates.
POST /api/v1/payments
Idempotency-Key: 8a3b91c4-f7e2-4d1a-b5c8-9e0f12345678
Content-Type: application/json
{
"amount": 99.99,
"currency": "USD",
"customer_id": "cust_12345"
}
On the server side (Node.js example):
app.post('/api/v1/payments', async (req, res) => {
const idempotencyKey = req.headers['idempotency-key'];
if (!idempotencyKey) {
return res.status(400).json({
title: 'Missing Idempotency Key',
detail: 'The Idempotency-Key header is required for this endpoint.'
});
}
const existing = await db.findPaymentByIdempotencyKey(idempotencyKey);
if (existing) {
return res.status(200).json(existing);
}
const payment = await db.createPayment(req.body, idempotencyKey);
return res.status(201).json(payment);
});
Remember: GET, PUT, and DELETE are naturally idempotent. POST is the one that needs extra attention.
7. Exposing Internal Implementation Details
The Mistake
Leaking database column names, internal IDs, stack traces, or technology-specific structures through your API. This is both a security risk and a coupling nightmare.
// Bad: exposes database internals
{
"user_tbl_id": 4523,
"pwd_hash": "$2b$10$abcdef...",
"mysql_created_ts": "2026-03-15 10:30:00",
"_hibernate_version": 3
}
The Fix
Design your API response as a contract that is independent of your implementation. Use a mapping layer (DTO / serializer / presenter) between your internal models and your API responses.
// Good: clean, intentional response
{
"id": "usr_4523",
"name": "Jane Doe",
"email": "[email protected]",
"created_at": "2026-03-15T10:30:00Z"
}
Never expose:
- Password hashes or internal tokens
- Database column names or auto-increment IDs
- Stack traces in production error responses
- ORM-specific metadata
8. Ignoring HATEOAS and Discoverability
The Mistake
Forcing API consumers to hardcode URLs and guess the relationships between resources. This makes your API fragile and difficult to navigate.
The Fix
Include relevant links in your responses so clients can discover related actions and resources. You do not need to implement full HATEOAS to benefit from this. Even simple links help tremendously.
{
"id": "ord_7891",
"status": "shipped",
"total": 149.99,
"links": {
"self": "/api/v1/orders/ord_7891",
"customer": "/api/v1/customers/cust_123",
"items": "/api/v1/orders/ord_7891/items",
"cancel": "/api/v1/orders/ord_7891/cancel"
}
}
This reduces guesswork for consumers and makes your API more self-documenting.

9. No Rate Limiting or Throttling
The Mistake
Leaving your API wide open without any protection against abuse. A single misbehaving client or a bot can bring down your entire service.
The Fix
Implement rate limiting and communicate limits clearly through response headers.
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1743696000
When a client exceeds the limit:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"type": "https://api.boxsoftware.net/errors/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "You have exceeded 1000 requests per hour. Please retry after 60 seconds."
}
Most frameworks and API gateways offer built-in rate limiting middleware. Use it.
10. Treating API Design as an Afterthought
The Mistake
This is perhaps the most damaging REST API design mistake, and it happens before a single line of code is written. Teams jump straight into implementation, bolt on endpoints as features are built, and end up with an API that reflects their codebase rather than the needs of their consumers.
The Fix
Treat your API as a product. Design it before you build it. Here is a practical workflow:
- Write an OpenAPI specification first. Define your endpoints, request/response schemas, and error formats before writing any code.
- Review the spec with consumers. Get feedback from the people who will actually use the API.
- Mock the API. Use tools like Prism or Stoplight to create a mock server from your spec so consumers can test against it early.
- Implement and test. Build the API to match the agreed-upon specification.
- Document continuously. Keep your documentation in sync with your implementation. Automated docs from your OpenAPI spec make this easier.
An API designed up front is almost always cleaner, more consistent, and easier to evolve than one that grows organically.
Quick Reference Checklist
Before you ship your next REST API, run through this checklist:
| # | Mistake | Fix |
|---|---|---|
| 1 | Inconsistent naming | Lowercase plural nouns, kebab-case |
| 2 | Poor error responses | RFC 7807 structured errors |
| 3 | Missing pagination | Offset or cursor-based pagination from day one |
| 4 | No versioning | Start with /api/v1 |
| 5 | Wrong status codes | Use correct HTTP codes for each scenario |
| 6 | No idempotency | Idempotency keys for POST requests |
| 7 | Exposing internals | Use DTOs and clean response contracts |
| 8 | No discoverability | Add links to related resources |
| 9 | No rate limiting | Implement throttling with clear headers |
| 10 | Design as afterthought | API-first design with OpenAPI spec |
Frequently Asked Questions
What is the most common REST API design mistake?
Inconsistent naming and structure is the mistake we see most often. It seems minor, but it creates confusion for every developer who integrates with your API. Following a strict naming convention from the start eliminates an entire category of questions and bugs.
Should I use URI versioning or header versioning for my REST API?
For most teams, URI path versioning (e.g., /api/v1/) is the best choice. It is simple, visible, easy to test in a browser, and well understood by developers. Header-based versioning is cleaner in theory but adds friction in practice.
How should I format error responses in a REST API?
Use a consistent JSON structure for all errors. We recommend following the RFC 7807 Problem Details format, which includes a type, title, status code, and human-readable detail message. For validation errors, add an array of field-level error objects.
When should I implement pagination in my API?
Always. Even if your dataset is small today, it will grow. Adding pagination later is a breaking change for your consumers. Start with a simple offset-based approach and switch to cursor-based pagination if performance requires it.
What is idempotency and why does it matter for REST APIs?
Idempotency means that making the same request multiple times produces the same result. GET, PUT, and DELETE are naturally idempotent. POST is not. Without idempotency handling on POST endpoints, network retries can create duplicate resources, duplicate charges, or duplicate notifications.
How do I prevent breaking changes in my REST API?
Design with evolution in mind. Add new fields as optional, never remove or rename existing fields without a version bump, use API versioning from the start, and give consumers a clear deprecation timeline before retiring old versions.
Wrapping Up
Great REST API design is not about following academic rules. It is about reducing friction for the people who build on top of your API. Every mistake on this list creates real costs: wasted developer time, more support requests, and harder migrations down the road.
The good news is that every one of these REST API design mistakes is avoidable with a little upfront planning. Pick the ones that apply to your current project, fix them, and your API consumers will thank you.
At Box Software, we help teams design, build, and scale APIs that developers actually enjoy using. If you need help getting your API architecture right, get in touch.
