Kodnos
/
Software Engineering

REST API Design Mistakes I Keep Seeing (And How to Fix Them)

The same API design mistakes keep showing up in code reviews. Here are the 10 most common ones I see, with clear examples, proper HTTP status codes, and a pre-launch checklist you can use today.

A
admin
Apr 4, 2026 14 min read 25 views
REST API Design Mistakes I Keep Seeing (And How to Fix Them)

I have reviewed hundreds of REST APIs over the years — as a developer, as a code reviewer, and as someone who has had to integrate with third-party APIs that made me want to throw my laptop out the window. The same mistakes keep coming up, and the frustrating thing is that most of them are easy to fix once you know about them.

Here are the ones I see most often, with clear examples of what to do instead.

Mistake 1: Verbs in URLs

This is the most common mistake, especially from developers who are building their first API.

Bad:
POST   /api/createUser
GET    /api/getUsers
PUT    /api/updateUser/5
DELETE /api/deleteUser/5

Good: POST /api/users (create) GET /api/users (list) GET /api/users/5 (get one) PUT /api/users/5 (update) DELETE /api/users/5 (delete)

The HTTP method already tells you the action. POST creates, GET reads, PUT updates, DELETE deletes. The URL should describe the resource (what), not the operation (how). Think of URLs as nouns, not verbs.

This is not just a style preference — it is the foundation of REST. When URLs are resource-based, clients can predict the API structure. If I know there is a /api/users endpoint, I can guess that /api/posts and /api/comments work the same way. That predictability is incredibly valuable.

Mistake 2: Inconsistent Naming

I have seen APIs that mix naming conventions within the same project:

Bad (mixing conventions):
/api/blog-posts      (kebab-case)
/api/user_profiles   (snake_case)
/api/orderItems      (camelCase)
/api/ProductList     (PascalCase)

Good (consistent kebab-case): /api/blog-posts /api/user-profiles /api/order-items /api/products

Kebab-case is the most common convention for URLs because it is readable and URL-safe. But the specific convention matters less than consistency. Pick one and stick with it across your entire API.

Also, use plural nouns for collection endpoints: /api/users, not /api/user. The collection is plural even when you access a single item: /api/users/5.

Mistake 3: Not Using Proper Status Codes

I have seen APIs that return HTTP 200 for everything and put the real status in the response body. This is terrible for several reasons: HTTP clients, proxies, and monitoring tools all rely on status codes to understand what happened.

Bad:
HTTP 200
{
  "success": false,
  "error": "User not found"
}

Good: HTTP 404 { "message": "User not found", "code": "USER_NOT_FOUND" }

Here are the status codes you should know and use:

| Code | Meaning | When to Use | |------|---------|-------------| | 200 | OK | Successful GET, PUT | | 201 | Created | Successful POST (include Location header) | | 204 | No Content | Successful DELETE | | 400 | Bad Request | Validation errors, malformed input | | 401 | Unauthorized | Not authenticated (no token, expired token) | | 403 | Forbidden | Authenticated but not authorized | | 404 | Not Found | Resource does not exist | | 409 | Conflict | Duplicate resource, version conflict | | 422 | Unprocessable | Syntactically valid but semantically wrong | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Server Error | Unexpected server failure |

Pay special attention to the difference between 401 and 403. Getting this wrong confuses every frontend developer who integrates with your API.

Mistake 4: Deeply Nested URLs

Some APIs nest resources so deeply that URLs become unmanageable:

Bad:
/api/companies/5/departments/3/employees/42/projects/7/tasks/12/comments

Better: /api/tasks/12/comments or /api/comments?task_id=12

More than two levels of nesting makes your API hard to use, your routing code a mess, and your documentation confusing. When you are tempted to go deeper than two levels, ask yourself: can this sub-resource be accessed directly?

If comments belong to a task and you often need comments without knowing the full hierarchy, give comments their own top-level endpoint with query parameters for filtering.

Mistake 5: No Pagination

Returning all records from a collection endpoint is a ticking time bomb:

Bad:
GET /api/users → returns 50,000 users in one response

Good: GET /api/users?page=0&size=20

{ "content": [...20 users...], "totalElements": 50000, "totalPages": 2500, "number": 0, "size": 20, "first": true, "last": false }

Always paginate list endpoints. Set a reasonable default page size (20 is common) and a maximum (100 is reasonable). Your database, network, and frontend will thank you.

For high-performance APIs, consider cursor-based pagination instead of offset-based. Offset pagination gets slower as you go deeper because the database still has to skip rows. Cursor pagination always performs the same.

Mistake 6: Exposing Internal Sequential IDs

This is both a design issue and a security issue:

Bad:
/api/users/1
/api/users/2
/api/users/3
(Attacker knows you have at least 3 users and can try 4, 5, 6...)

Better: /api/users/a1b2c3d4-e5f6-7890-abcd-ef1234567890

Sequential IDs reveal information: how many users you have, the rate of growth, and they make it trivial to enumerate all resources. Use UUIDs or create separate public-facing identifiers.

Mistake 7: Ignoring Versioning Until It Is Too Late

Someday, you will need to make a breaking change to your API. If you did not plan for versioning, you are stuck choosing between breaking all existing clients or maintaining ugly backward-compatible hacks forever.

URL versioning (most common, easiest to implement):
/api/v1/users
/api/v2/users

Header versioning (cleaner URLs, more complex): Accept: application/vnd.myapi.v2+json

URL-based versioning is simpler and more visible. I recommend it for most APIs. You might not need it on day one, but at least design your URL structure so that adding /v1/ later is not painful.

Mistake 8: Inconsistent Error Responses

Every error from your API should follow the same structure. Nothing is more frustrating for a frontend developer than getting different error formats from different endpoints.

JSON
{
  "status": 400,
  "message": "Validation failed",
  "timestamp": "2026-04-15T10:30:00Z",
  "path": "/api/users",
  "errors": [
    {
      "field": "email",
      "message": "must be a valid email address",
      "rejectedValue": "not-an-email"
    },
    {
      "field": "password",
      "message": "must be at least 8 characters",
      "rejectedValue": "abc"
    }
  ]
}

Create an error response class in your backend and use it consistently through a global exception handler. In Spring Boot, @ControllerAdvice makes this easy. Document your error format as prominently as your success responses.

Mistake 9: Not Rate Limiting Public Endpoints

Any public-facing API without rate limiting is an invitation for abuse. A single client could flood your server with thousands of requests per second, intentionally or accidentally.

Response headers for rate limiting:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1681539600

When exceeded: HTTP 429 Too Many Requests { "message": "Rate limit exceeded. Try again in 60 seconds." }

Rate limiting protects your server, ensures fair usage, and is expected by API consumers.

Mistake 10: Returning Too Much or Too Little Data

Bad: Returning everything
{
  "id": 1,
  "username": "alice",
  "email": "alice@example.com",
  "passwordHash": "$2a$10$...",     // NEVER!
  "internalNotes": "VIP customer",  // Should not be public
  "createdAt": "...",
  "updatedAt": "...",
  "lastLoginIp": "192.168.1.1"      // Privacy concern
}

Good: Returning what the client needs { "id": "a1b2c3", "username": "alice", "email": "alice@example.com", "createdAt": "2026-04-15T10:30:00Z" }

Use DTOs (Data Transfer Objects) to control exactly what gets returned. Never expose internal implementation details, sensitive data, or database entities directly.

The Pre-Launch Checklist

Before shipping an API, run through this:

  • URLs use nouns, not verbs
  • Consistent naming convention throughout
  • Proper HTTP methods (GET, POST, PUT, DELETE)
  • Correct HTTP status codes
  • Pagination on all list endpoints
  • Input validation with clear error messages
  • Consistent error response format
  • Authentication and authorization
  • Rate limiting on public endpoints
  • API documentation (OpenAPI/Swagger)
  • No sensitive data in responses
  • CORS configured correctly

The Bottom Line

Good API design is not about following rules blindly. It is about making your API predictable. When developers can guess how an endpoint works before reading the docs, when error messages tell them exactly what went wrong, when the naming is consistent and the structure is logical — that is a well-designed API.

The effort you put into API design pays off every single day, in fewer support questions, faster integrations, and fewer bugs. Take the time to get it right.

14 MinShare this article
Oğuzhan Berke Özdil
Author

Oğuzhan Berke Özdil

I have been connected to computers since childhood. On this website, I share what I learn and experience while trying to build a strong foundation in software. I completed my BSc in Computer Science at AGH University of Krakow and I am currently pursuing an MSc in Computer Science with a focus on AI & Data Analysis at the same university.