10 Common Mistakes in REST API Design and How to Avoid Them

10 Common Mistakes in REST API Design and How to Avoid Them

by | Jun 23, 2026 | Uncategorized | 0 comments

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
API code developer computer

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.

API code developer computer

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.

API code developer computer

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:

  1. Write an OpenAPI specification first. Define your endpoints, request/response schemas, and error formats before writing any code.
  2. Review the spec with consumers. Get feedback from the people who will actually use the API.
  3. Mock the API. Use tools like Prism or Stoplight to create a mock server from your spec so consumers can test against it early.
  4. Implement and test. Build the API to match the agreed-upon specification.
  5. 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.