REST API Design Best Practices for 2026
APIRESTBest Practices

REST API Design Best Practices for 2026

APIs Are Forever

A public API is a contract. Once consumers build on top of it, breaking changes become extremely costly — they break other people's production systems. This is why API design decisions made early in a project have outsized impact. The principles in this guide are designed for longevity: APIs that you can evolve without breaking consumers.

Resource Naming: Use Nouns, Not Verbs

REST resources are nouns — they represent things, not actions. Use the HTTP method to express the action:

GET    /users              — list users
POST   /users              — create a user
GET    /users/{id}         — get a specific user
PUT    /users/{id}         — replace a user
PATCH  /users/{id}         — partial update a user
DELETE /users/{id}         — delete a user

Avoid verb-based URLs like /getUser, /createUser, /deleteUser. These are RPC-style, not REST-style. The exception: complex operations that do not map to CRUD naturally can use action sub-resources: POST /orders/{id}/cancel, POST /users/{id}/activate.

Use plural nouns for collection resources (/users, not /user). This creates consistency — you never need to think about whether to pluralize: collections are always plural, individual items are /users/{id}.

URL Structure and Hierarchy

Represent relationships in the URL hierarchy, but avoid deep nesting:

GET /users/{userId}/orders              ← orders for a specific user
GET /users/{userId}/orders/{orderId}    ← a specific order for a user

// Avoid going deeper than 3 levels — it becomes unwieldy:
GET /users/{userId}/orders/{orderId}/items/{itemId}/reviews   ← too deep
// Better: treat reviews as a top-level resource
GET /reviews?orderId={orderId}&itemId={itemId}

Query parameters are for filtering, sorting, and pagination — not for identifying resources. Resource identity belongs in the path.

Versioning: Do It from Day One

Every public API should be versioned from the first day it is deployed. Unversioned APIs have no way to introduce breaking changes without impacting all consumers.

URL versioning (most common): /api/v1/users. The version is visible, easily routable, and works with every HTTP client including curl. This is the recommended approach for most APIs.

Header versioning: Accept: application/vnd.myapi.v1+json. Cleaner URLs but harder to test in browsers and requires clients to set headers explicitly.

Never make breaking changes to an existing version. A breaking change is: removing a field, changing a field's type, changing a field's name, changing an endpoint's URL, or changing the semantics of an existing value. Additive changes (new optional fields, new endpoints) are non-breaking and can be deployed without a version bump.

Maintain old versions with a documented sunset policy — give consumers at minimum 6–12 months notice before deprecating a version.

Consistent Error Responses

Error responses should follow a consistent schema across all endpoints. Inconsistent errors are one of the most common API usability complaints:

{
  "error": {
    "status": 422,
    "code": "VALIDATION_FAILED",
    "message": "The request body failed validation.",
    "details": [
      {
        "field": "email",
        "code": "INVALID_FORMAT",
        "message": "Must be a valid email address"
      },
      {
        "field": "age",
        "code": "TOO_SMALL",
        "message": "Must be at least 18",
        "minimum": 18
      }
    ],
    "requestId": "req_01HZXYZ"
  }
}

The code field is machine-readable — clients can switch on it. The message is human-readable — for developers reading logs. The requestId correlates with server logs for debugging.

Never return 200 OK with an error in the body. Use the correct HTTP status code — see our HTTP Status Codes guide for the full reference.

Pagination

Never return unlimited results. Collections can grow unbounded — return everything and you risk timeouts, out-of-memory errors, and enormous responses for large datasets.

Cursor-based pagination (recommended for large or real-time datasets): instead of page numbers, use an opaque cursor that points to a position in the dataset. More efficient for databases and handles insertions/deletions correctly:

GET /users?limit=20&after=cursor_abc123

{
  "data": [...],
  "pagination": {
    "hasNextPage": true,
    "endCursor": "cursor_xyz789",
    "limit": 20
  }
}

Offset-based pagination (simpler, for paginated UIs with page numbers): returns results by page number. Simpler to implement, but breaks under concurrent insertions — items can appear twice or be skipped when the dataset changes between pages.

GET /users?page=2&perPage=20

{
  "data": [...],
  "pagination": {
    "page": 2,
    "perPage": 20,
    "total": 1847,
    "totalPages": 93
  }
}

Filtering, Sorting, and Searching

Design a consistent query parameter convention:

// Filtering
GET /users?status=active&planId=pro

// Sorting (prefix with - for descending)
GET /users?sort=-createdAt,email

// Search
GET /users?q=fredy

// Field selection (reduce payload size)
GET /users?fields=id,email,createdAt

// Combined
GET /users?status=active&sort=-createdAt&limit=20&after=cursor_abc

Response Envelopes

Use a consistent response envelope for all endpoints:

// Collection response
{
  "data": [...],
  "pagination": { ... },
  "meta": { "requestId": "req_01H", "timestamp": "2026-03-16T10:00:00Z" }
}

// Single resource response
{
  "data": { "id": "usr_123", "email": "fredy@example.com" },
  "meta": { "requestId": "req_01H" }
}

The envelope approach makes it easy to add metadata (pagination, rate limit info, deprecation warnings) without breaking existing consumers who are reading response.data.

Rate Limiting and Throttling

Always implement rate limiting on public APIs. Use standard headers to communicate limits:

X-RateLimit-Limit: 1000       // requests allowed per window
X-RateLimit-Remaining: 847    // requests left in current window
X-RateLimit-Reset: 1742123456 // Unix timestamp when window resets

Return 429 Too Many Requests when the limit is exceeded, with a Retry-After header indicating how many seconds to wait.

Documentation

An API without documentation is an API that will not be used. At minimum, document every endpoint with: URL and method, request body schema, response schema, possible error codes, and at least one example request and response. OpenAPI (Swagger) is the industry standard — it enables auto-generated client SDKs and interactive documentation. Tools like Swagger UI, Redoc, and Scalar can render beautiful documentation from an OpenAPI spec file.

Format Your Responses During Development

Use PureFormatter's JSON Formatter to make API responses readable during development and debugging. Copy a raw API response, paste it, and immediately see the structure. Also useful for validating that your response schema matches your documentation.

Fredy
Written by
Fredy
Senior Developer & Technical Writer

Fredy is a full-stack developer with 8+ years of experience building web applications. He writes about developer tools, best practices, and the craft of clean code.