Ctrl + K
API20 min read

REST API Best Practices

A practical guide to designing maintainable REST APIs with consistent resources, HTTP methods, status codes, validation, error handling, pagination, security, caching, and documentation.

Published: 2026-10-05

A REST API can work correctly and still be difficult to use, maintain, or extend. Problems often appear when resource names are inconsistent, HTTP methods are used incorrectly, error responses have different shapes, pagination behaves differently between endpoints, or clients have to guess how an API is supposed to work.

REST API best practices are mostly about consistency and predictable behavior. A well-designed API gives clients clear resource URLs, appropriate HTTP methods, meaningful status codes, stable response structures, useful errors, and documented rules.

This guide covers practical REST API design principles that can be applied to new APIs as well as existing APIs that need to become easier to consume and maintain.

What Makes a REST API Well Designed?

A good REST API should make common operations predictable. Developers should be able to understand how to retrieve, create, update, and delete resources without learning a different convention for every endpoint.

AreaRecommended approach
ResourcesUse clear and consistent resource-oriented URLs
HTTP methodsUse methods according to their intended semantics
Status codesReturn status codes that describe the result
ResponsesKeep response structures predictable
ErrorsUse a consistent error format
ValidationValidate input at the API boundary
PaginationUse a documented and consistent pagination strategy
SecurityUse authentication, authorization, and transport security
DocumentationDocument endpoints, parameters, responses, and errors

Use Resource-Oriented URLs

REST APIs generally work with resources rather than actions. URLs should identify the resource being operated on, while the HTTP method describes the operation.

GET /users
GET /users/42
POST /users
PATCH /users/42
DELETE /users/42

This is usually clearer than putting actions directly into every URL. For example, an API does not normally need separate endpoints such as /getUsers, /createUser, and /deleteUser when the HTTP method already communicates the intended operation.

Less consistentResource-oriented
GET /getUsersGET /users
POST /createUserPOST /users
POST /deleteUser/42DELETE /users/42
POST /updateUser/42PATCH /users/42

Keep Resource Names Consistent

Choose a naming convention and use it throughout the API. Mixing singular and plural resource names makes an API harder to learn and increases the chance of client-side mistakes.

GET /users
GET /users/42
GET /users/42/orders
GET /orders/1001

Plural nouns are a common convention for collection resources. The important point is not that one naming style is universally mandatory, but that the same convention should be applied consistently.

Use HTTP Methods Correctly

HTTP methods already provide semantics that REST APIs can use to communicate intent. GET is generally used to retrieve representations, POST to create resources or perform operations that do not fit another method, PUT to replace a resource, PATCH to partially modify a resource, and DELETE to remove a resource.

MethodTypical useSafeIdempotent
GETRetrieve a resourceYesYes
POSTCreate or submit dataNoNo
PUTReplace a resourceNoYes
PATCHPartially modify a resourceNoNot inherently
DELETERemove a resourceNoYes

The distinction between PUT and PATCH is particularly important. PUT conventionally represents replacement of the target resource, while PATCH is intended for partial modifications. The exact implementation should be documented so clients know what fields are required and how omitted fields are treated.

Choose Status Codes Deliberately

HTTP status codes communicate the high-level result of a request. Returning the same status code for successful operations, validation failures, missing resources, and server errors makes it much harder for clients to respond correctly.

StatusTypical API meaning
200 OKRequest succeeded
201 CreatedA resource was successfully created
204 No ContentRequest succeeded without a response body
400 Bad RequestRequest is invalid or cannot be processed as submitted
401 UnauthorizedAuthentication is required or invalid
403 ForbiddenThe authenticated client is not allowed to perform the operation
404 Not FoundThe requested resource was not found
409 ConflictRequest conflicts with the current resource state
422 Unprocessable ContentRequest syntax is valid but the content cannot be processed
429 Too Many RequestsThe client exceeded a rate limit
500 Internal Server ErrorUnexpected server-side failure
💡 Do not choose status codes only because they sound approximately correct. Document the conditions under which each endpoint returns important success and error codes.

Return Consistent Response Structures

Clients become easier to implement when similar endpoints return data in predictable structures. Inconsistent response shapes force frontend and backend developers to add special cases for individual endpoints.

{
  "id": 42,
  "name": "Alice",
  "email": "[email protected]"
}

The exact JSON structure is a design decision, but fields should have stable meanings and types. If an endpoint returns an object in one situation and an array in another without a documented reason, clients have to handle unnecessary ambiguity.

Keep Error Responses Predictable

Error handling deserves the same consistency as successful responses. A client should be able to inspect an error response and determine what happened without parsing completely different formats for every endpoint.

{
  "error": {
    "code": "INVALID_EMAIL",
    "message": "The email address is invalid",
    "field": "email"
  }
}

An error response can contain a machine-readable code, a human-readable message, and additional information about the affected field or resource. The exact structure should be standardized across the API.

⚠️ Avoid returning internal stack traces, database errors, secret values, or implementation details in production API responses. Detailed diagnostic information should remain on the server side.

Validate Input at the API Boundary

Every API should validate data received from clients before using it. Validation should cover required fields, data types, allowed values, string lengths, numeric ranges, formats, and other business constraints that are appropriate for the endpoint.

{
  "name": "Alice",
  "email": "[email protected]",
  "age": 28
}

Validation should produce useful errors rather than simply returning a generic failure. If several fields are invalid, an API can return structured information describing which fields failed and why.

Client-side validation can improve user experience, but it should not replace server-side validation. The API must treat incoming data as untrusted regardless of whether another application already validated it.

Use Pagination for Large Collections

Returning thousands or millions of records from one endpoint can increase response size, memory usage, processing time, and network traffic. Collection endpoints should therefore use pagination when the result set can become large.

GET /users?page=2&limit=25

A paginated response can include the requested records together with metadata describing the result set. The exact pagination model can be page-based, offset-based, cursor-based, or another documented approach.

{
  "data": [
    {
      "id": 26,
      "name": "User 26"
    }
  ],
  "pagination": {
    "page": 2,
    "limit": 25,
    "total": 125
  }
}

Cursor-based pagination can be more suitable for large or frequently changing datasets because it does not depend on a fixed numeric offset. Whichever strategy is selected, clients should be able to understand how to request the next set of results.

Define Filtering, Sorting, and Search Consistently

Collection endpoints often need filtering and sorting in addition to pagination. Consistent query parameter conventions make these features easier to discover and use.

GET /products?category=books&sort=price&order=asc
GET /users?status=active&sort=createdAt&order=desc

Avoid giving every endpoint a completely different query parameter vocabulary unless there is a strong reason. If one endpoint uses sort and another uses orderBy for the same concept, clients have to learn unnecessary differences.

Avoid Uncontrolled Response Sizes

Pagination is one way to control response size, but APIs can also use field selection or expansion rules when resources contain large amounts of data. Returning every possible field for every request can waste bandwidth and processing time.

If an API supports field selection, the syntax and behavior should be documented clearly. The server should also enforce sensible limits so that one request cannot unexpectedly produce an enormous response.

Design Nested Resources Carefully

Nested URLs can express relationships between resources, but excessive nesting can make URLs difficult to understand and maintain.

GET /users/42/orders
GET /users/42/orders/1001

A shallow relationship can be useful when the parent resource provides important context. However, deeply nested URLs can become cumbersome.

GET /users/42/orders/1001/items/7/reviews/3

When nesting becomes excessive, consider whether the child resource can be addressed directly or whether filtering can express the relationship more simply.

Use Authentication and Authorization Separately

Authentication answers the question of who the client is. Authorization determines what that authenticated client is allowed to do. These are related but separate concerns.

For example, a user may be authenticated successfully but still be forbidden from deleting another user's resources. The API should enforce authorization on the server rather than assuming that a valid authentication token grants access to every endpoint.

SituationTypical response
No valid authentication401 Unauthorized
Authenticated but not permitted403 Forbidden
Authenticated and permittedProcess the requested operation

Protect API Credentials

API keys, access tokens, client secrets, and other credentials should not be exposed unnecessarily. They should be transmitted over HTTPS and stored using appropriate secret-management mechanisms rather than being hard-coded into public client applications.

⚠️ A secret included in browser-side JavaScript should be considered exposed. Public clients cannot safely hide credentials from the users who receive and execute the application.

Use HTTPS

APIs that handle authentication credentials, personal information, or other sensitive data should use HTTPS. Encryption protects data in transit and helps prevent attackers from reading or modifying requests and responses while they travel between client and server.

HTTPS should be treated as a baseline transport-security requirement rather than an optional optimization. Redirecting HTTP traffic to HTTPS can help enforce the secure scheme, but sensitive endpoints should be designed to operate securely from the beginning.

Apply Rate Limiting

Rate limiting restricts how frequently a client can make requests during a defined period. It can protect APIs from accidental overload, abusive traffic, brute-force attempts, and unexpectedly expensive operations.

HTTP/1.1 429 Too Many Requests
Retry-After: 60

A useful rate-limiting design should document the relevant limits and explain what clients should do after receiving a 429 response. The limits may differ between authentication endpoints, read operations, write operations, and resource-intensive operations.

Use Caching Where Appropriate

Caching can reduce repeated work and improve response times when data does not need to be regenerated for every request. HTTP provides caching mechanisms such as Cache-Control and ETag that can be used to communicate caching behavior between servers, clients, and intermediary caches.

Cache-Control: max-age=300
ETag: "users-v42"

Caching should reflect how frequently the underlying data changes and whether the response contains information that should be cached. Sensitive or highly dynamic data requires more careful cache-control rules.

Make Idempotency Clear

Idempotency means that making the same request multiple times has the same intended effect as making it once. HTTP defines different semantics for methods, and API designers should understand those semantics when implementing operations.

This becomes particularly important for operations that may be retried because of network failures. For example, a client may not know whether a request reached the server before the connection failed. For certain create or payment-like operations, an idempotency mechanism can prevent accidental duplicate processing.

POST /orders
Idempotency-Key: 8f4c2b1e-7d6a-4c1a-9b32-123456789abc

Version APIs Deliberately

APIs evolve as applications change. New fields can often be added without breaking existing clients, but removing fields, changing their meanings, or altering response structures can be a breaking change.

There are several approaches to API versioning, including URL-based versions, headers, or content negotiation. The important part is to define a clear compatibility policy and communicate breaking changes before clients are forced to migrate.

GET /api/v1/users
GET /api/v2/users

Versioning does not eliminate the need for backward compatibility. If every minor change creates a new API version, maintaining multiple versions can become expensive. Prefer compatible additions when possible and reserve breaking changes for cases where they are actually necessary.

Prefer Backward-Compatible Changes

Adding an optional response field is generally less disruptive than renaming or removing an existing field. Similarly, adding a new endpoint is usually less disruptive than changing the behavior of an existing endpoint.

ChangeCompatibility concern
Add an optional response fieldUsually low
Add a new endpointUsually low
Add an optional request fieldUsually low
Remove a response fieldHigh
Rename a fieldHigh
Change a field typeHigh
Change existing endpoint semanticsHigh

Document the API with OpenAPI

API documentation should describe how endpoints work rather than forcing developers to infer behavior from examples. OpenAPI is a widely used specification format for describing HTTP APIs, including paths, parameters, request bodies, responses, schemas, and authentication requirements.

paths:
  /users/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: User found
        "404":
          description: User not found

A machine-readable API description can support documentation interfaces, client generation, validation, testing, and collaboration between frontend and backend teams.

Document Examples and Edge Cases

Good API documentation should not describe only the successful path. Developers also need to know what happens when authentication fails, a resource does not exist, validation fails, pagination reaches the end, or a rate limit is exceeded.

Examples are especially useful for request bodies and error responses. A small realistic example can communicate expected data types and structure more quickly than a long prose explanation.

Keep JSON Naming Consistent

JSON field naming should follow a consistent convention throughout the API. Common choices include camelCase and snake_case. Either can work, but switching conventions between endpoints creates unnecessary client-side complexity.

{
  "firstName": "Alice",
  "lastName": "Smith",
  "createdAt": "2026-09-21T10:30:00Z"
}

Consistency should also apply to related concepts. If one endpoint calls a timestamp createdAt, another endpoint should not use creationDate for the same concept without a specific reason.

Handle Dates and Times Explicitly

Date and time values can cause interoperability problems when APIs do not define their format and timezone behavior clearly. ISO 8601-style representations are commonly used because they provide an unambiguous textual format.

{
  "createdAt": "2026-09-21T10:30:00Z"
}

The API documentation should make clear whether timestamps represent UTC, include an explicit offset, or follow another convention. Clients should not have to guess how a timestamp should be interpreted.

Avoid Leaking Internal Database Structure

An API does not have to expose database tables and columns directly. Internal storage structures can change while the public API remains stable. Treating the API as its own contract makes it easier to change the implementation later.

For example, an internal database might split a person's information across several tables, while the API can expose a single user resource containing the fields clients actually need. This separation also prevents accidental exposure of internal fields.

Separate Public API Contracts from Internal Models

Database models, internal service objects, and API response models often have different requirements. Mapping internal data to an explicit API representation gives developers control over which fields are exposed and how they are named.

⚠️ Returning an entire database object directly can expose fields that were never intended to be public. Explicit response schemas make accidental data exposure less likely.

Use Consistent Request and Response Headers

HTTP headers communicate metadata such as content type, caching rules, authorization information, correlation identifiers, and content negotiation preferences. APIs should use standard headers where possible and document custom headers that clients are expected to understand.

Content-Type: application/json
Accept: application/json
Authorization: Bearer <token>

Using standard HTTP mechanisms reduces the amount of custom behavior clients need to learn. Custom headers can still be useful when they represent application-specific requirements, but they should have clearly documented semantics.

Add Request Correlation IDs

A correlation or request identifier can make production debugging easier by allowing a specific API request to be connected with server-side logs and downstream operations.

X-Request-ID: 7f2c1a9e

The exact header name and propagation strategy can vary between systems. The important principle is that logs should contain enough context to trace a request without exposing sensitive information.

Test API Behavior

API testing should cover more than successful requests. Tests should verify validation failures, authentication and authorization behavior, missing resources, conflicts, malformed input, pagination boundaries, rate limits, and important business rules.

Automated tests are particularly valuable for API contracts because a backend change can otherwise break clients without immediately producing an obvious server-side error. Testing representative request and response structures helps detect these changes early.

Test APIs with Mock Responses

Mock APIs and mock responses can help frontend developers work before the production backend is complete. They are also useful for testing loading states, empty results, validation errors, authentication failures, and other conditions that may be difficult to reproduce against a live system.

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "The requested user does not exist"
  }
}

A mock should reflect the real API contract as closely as possible. Otherwise, frontend code may be built around response shapes that later turn out to be incorrect.

Design for Empty Results

An empty collection is usually a valid result rather than an error. Clients should be able to distinguish between a successful request that returned no records and a request that failed.

{
  "data": [],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 0
  }
}

The API should document how empty collections, missing optional fields, null values, and missing resources are represented. Consistent conventions prevent client-side ambiguity.

Do Not Overuse HTTP Status Codes

Although HTTP provides many status codes, an API does not need a unique status for every business condition. A small, well-documented set of status codes is often easier for clients to handle.

Business-specific information can be placed in a structured response body while the HTTP status communicates the general category of the result. This separates protocol-level information from application-specific details.

Make APIs Observable

Production APIs should provide enough telemetry to understand performance and failures. Useful information can include request duration, status code, endpoint, request identifier, and carefully selected application metrics.

Logging should be balanced with privacy and security requirements. Request bodies, authorization tokens, passwords, and other sensitive information should not be logged indiscriminately.

Avoid Breaking Changes Without a Migration Path

When a breaking change is unavoidable, clients should have a documented migration path. Depending on the API, this may involve a new version, a deprecation period, migration documentation, or a temporary compatibility layer.

Deprecation should be communicated clearly. Clients need enough information to identify affected endpoints and understand what they should use instead.

REST API Design Checklist

  • Use consistent resource-oriented URLs.
  • Choose HTTP methods according to their intended semantics.
  • Return meaningful and documented HTTP status codes.
  • Keep request and response structures predictable.
  • Use a consistent error response format.
  • Validate all untrusted input on the server.
  • Paginate potentially large collections.
  • Use consistent filtering and sorting conventions.
  • Separate authentication from authorization.
  • Protect credentials and sensitive data.
  • Use HTTPS for API communication.
  • Apply appropriate rate limits.
  • Use caching when the data and endpoint support it.
  • Document API contracts with OpenAPI or another suitable specification.
  • Test successful and unsuccessful request paths.
  • Preserve backward compatibility where practical.

Frequently Asked Questions

What are the most important REST API best practices?

Use consistent resource-oriented URLs, appropriate HTTP methods, meaningful status codes, predictable response and error structures, server-side validation, authentication and authorization, pagination, security controls, and clear documentation.

Should REST API URLs use nouns or verbs?

Resource-oriented REST APIs generally use nouns to identify resources, while HTTP methods describe the operation. For example, GET /users retrieves users and POST /users creates a user.

Should I use PUT or PATCH for updates?

PUT is conventionally used when replacing a resource representation, while PATCH is intended for partial modifications. The API should document the exact behavior because implementations can differ.

What status code should an API return after creating a resource?

201 Created is commonly used when a request successfully creates a resource. The response can also provide a representation of the created resource and information about its location when appropriate.

How should REST APIs handle errors?

Use meaningful HTTP status codes together with a consistent structured error body. Machine-readable error codes, human-readable messages, and field-specific validation details can make errors easier for clients to handle.

Should every REST API use pagination?

Pagination is especially important for collection endpoints whose result sets can become large. Small, fixed-size collections may not need it, but APIs should consider future growth rather than assuming a collection will always remain small.

Is OpenAPI required for a REST API?

OpenAPI is not required to implement a REST API, but a machine-readable API specification can make documentation, validation, testing, client generation, and collaboration significantly easier.

Helpful REST API Development Tools

Different stages of API development benefit from different tools. REST API mock generators can provide test endpoints and sample responses before a backend is complete, while HTTP request builders make it easier to construct and inspect requests. HTTP response formatters can improve the readability of returned data, OpenAPI viewers can make API specifications easier to explore, and JSON formatters can help inspect and validate JSON request and response bodies during development and debugging.

Conclusion

REST API best practices are primarily about creating a predictable contract between clients and servers. Consistent resource URLs, HTTP methods, status codes, response structures, validation rules, and error formats reduce the amount of special-case logic that clients need to implement.

Security, pagination, caching, versioning, testing, and documentation become increasingly important as an API grows. The goal is not to apply every possible convention mechanically, but to establish clear rules that remain consistent across the API and can evolve without unnecessarily breaking existing clients.

Found an issue?

Found an error, outdated information, or something missing from this article? Let me know through the Contact page.

Your feedback helps improve our articles and keep them accurate and useful.