HomeProjectsBlogContact

REST API Design Best Practices You Won't Regret Later

Changing an API after clients depend on it is painful. The design decisions worth getting right early: resource naming, status codes, error format, pagination, versioning and idempotency.

REST API Design Best Practices You Won't Regret Later

An API is a promise. Once a mobile app, a partner or even your own frontend depends on it, every change becomes a negotiation. That's why a little care upfront pays off for years. These are the REST API design practices that age well.

Name resources, not actions

URLs identify things. HTTP methods say what to do with them.

Do Avoid
GET /users GET /getUsers
POST /users POST /createUser
GET /users/42 GET /user?id=42
PATCH /users/42 POST /updateUser
DELETE /users/42 GET /deleteUser/42
GET /users/42/orders GET /getOrdersForUser?userId=42

Guidelines:

  • Use plural nouns for collections: /users, /orders.
  • Nest only one level for clear ownership: /users/42/orders is fine; /users/42/orders/7/items/3/notes is a sign you need flatter resources.
  • Use kebab-case or lowercase consistently: /order-items, not /OrderItems.
  • For actions that don't fit CRUD, a sub-resource is acceptable: POST /orders/7/cancel. Just be consistent.

Use HTTP methods for what they mean

  • GET reads and never changes data. Crawlers, prefetchers and caches assume this.
  • POST creates, or triggers a process.
  • PUT replaces a resource entirely.
  • PATCH updates part of a resource.
  • DELETE removes it.

GET, PUT and DELETE should be idempotent: calling them twice has the same effect as calling them once. That property makes retries safe.

Return status codes that mean something

You don't need all of them. You need these, used consistently:

Code When
200 OK Successful read or update
201 Created Resource created (include its location or the resource)
204 No Content Success with nothing to return, e.g. delete
400 Bad Request Malformed request or validation failure
401 Unauthorized Not authenticated
403 Forbidden Authenticated, but not allowed
404 Not Found Resource doesn't exist (or the user shouldn't know it exists)
409 Conflict State conflict, like a duplicate email
422 Unprocessable Content Valid syntax, failed business rules (some teams use 400 for this)
429 Too Many Requests Rate limit hit (rate limiting guide)
500 Internal Server Error Something broke on your side

Never return 200 with { "error": "..." } in the body. Clients, monitoring tools and caches all rely on status codes.

Use one error format everywhere

Pick a shape and never deviate. Clients can then handle errors with one piece of code. There's even a standard for this, Problem Details for HTTP APIs (RFC 9457):

{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation failed",
  "status": 400,
  "detail": "One or more fields are invalid.",
  "errors": [
    { "field": "email", "message": "Must be a valid email address" }
  ]
}

Include field-level details for validation errors so frontends can show messages next to the right inputs. Never include stack traces or SQL errors in production responses.

Paginate every list from day one

An endpoint that returns "all orders" works until there are 200,000 orders. Adding pagination later breaks clients, so add it now.

Offset pagination (?page=3&limit=20) is simple and supports jumping to page 30. It gets slow on huge tables and can skip or repeat items when data changes between requests.

Cursor pagination (?limit=20&after=eyJpZCI6MTAwfQ) returns an opaque cursor pointing to the last item. It's stable and fast at any depth. Ideal for feeds and infinite scroll.

Either way, set a sensible default and a maximum limit, and return what clients need to fetch the next page.

Filtering, sorting and field selection

Keep query parameters predictable:

GET /orders?status=paid&created_after=2026-01-01&sort=-created_at&limit=20
  • Filters as plain parameters.
  • sort with a - prefix for descending.
  • Optionally, fields=id,total,status to keep responses small.

Only allow sorting and filtering on indexed columns, or you've built a very convenient way to make your database slow (indexing explained).

Version from the start

Even if you never expect breaking changes, leave room for them:

  • URL versioning (/v1/users) is explicit and easy to route and cache.
  • Header versioning keeps URLs clean but is less visible.

More important than the mechanism: avoid breaking changes. Adding fields is safe. Removing or renaming fields, changing types or changing meaning is breaking. When you must, run both versions for a while and announce a deprecation timeline.

Make retries safe with idempotency keys

Networks fail. A client sends "create payment," the response is lost, and it retries. Without protection, you charge twice.

Accept an Idempotency-Key header on important POST requests. Store the key with the result; if the same key arrives again, return the stored result instead of repeating the action. Payment providers use this pattern for exactly this reason.

Small details that matter

  • Dates in ISO 8601, in UTC: 2026-03-14T09:30:00Z. (Why dates go wrong.)
  • Consistent casing in JSON: pick camelCase or snake_case and stick with it.
  • IDs as strings in JSON if they might exceed JavaScript's safe integer range.
  • No sensitive fields in responses by default. Never serialise the whole user row with its password hash.
  • Document it with OpenAPI. Many frameworks generate the spec from code, and it gives you interactive docs and client generation for free.

FAQ

What are REST API best practices?

Use nouns for resources and HTTP methods for actions, return meaningful status codes, use one consistent error format, paginate lists, version your API, and document it with OpenAPI.

Should I use PUT or PATCH for updates?

Use PATCH for partial updates, which is what most edit forms do. Use PUT when the client sends the complete resource to replace it.

How should I version a REST API?

URL versioning like /v1/ is the simplest and most common. Whatever you choose, prefer additive, non-breaking changes so new versions are rarely needed.

Keep reading

More from the workbench

All articles →