Software Development

How to Design a REST API: Best Practices and Examples

A practical guide to REST API design: resource naming, HTTP methods, status codes, error format, pagination and versioning, with sample endpoints you can copy.

Quick answer

Good REST API design models your data as plural noun resources (/orders, /orders/{id}), uses HTTP methods and status codes as the HTTP standard defines them, returns consistent JSON errors, paginates every list, and versions only for breaking changes. Write the contract in OpenAPI first, then build to it.

How to Design a REST API: Best Practices and Examples

Photo: Unsplash

On this page

An API is a promise. Once other teams, partners or mobile apps start calling your endpoints, every naming choice and error format becomes hard to change. A rushed design turns into years of workarounds, support tickets and “v2” projects nobody wanted.

The good news is that REST API design is mostly about following a few well-established conventions consistently. This guide covers resource naming, HTTP methods, status codes, error responses, pagination and versioning, with sample endpoints for a simple online store you can adapt.

If you are new to the concept itself, start with what an API is, then come back here.

What makes an API “RESTful”

REST is an architectural style built on top of HTTP. In practice, a REST API means:

  • Resources (orders, customers, products) each have a URL.
  • HTTP methods (GET, POST, PUT, PATCH, DELETE) describe what you want to do with that resource.
  • Requests are stateless. Each request carries everything the server needs, such as an auth token, rather than relying on a server-side session.
  • Representations are usually JSON, and the response status code tells the client what happened.

Microsoft’s Azure Architecture Center guidance on RESTful API design sums up the goals as platform independence and loose coupling: any client should be able to call the API, and client and server should be able to evolve separately. Every practice below serves those 2 goals.

Name resources clearly

URLs should identify things, not actions. Use plural nouns and let the HTTP method express the verb.

Sample endpoints for an online store:

GET /v1/products List products GET /v1/products/{productId} Get one product POST /v1/orders Create an order GET /v1/orders/{orderId} Get one order PATCH /v1/orders/{orderId} Update part of an order DELETE /v1/orders/{orderId} Cancel or delete an order GET /v1/customers/{customerId}/orders List a customer's orders

Naming rules that save pain later:

  • Use plural nouns: /orders, not /order or /getOrders.
  • Keep nesting shallow. One level (/customers/{id}/orders) is fine. Deeper paths like /customers/{id}/orders/{id}/items/{id}/notes get brittle. Give deep objects their own top-level path if they have their own IDs.
  • Pick one case style and stick to it. Lowercase paths with hyphens (/shipping-addresses) and camelCase or snake_case JSON fields are all common. Consistency matters more than the choice.
  • Use stable, opaque IDs. Avoid exposing database auto-increment IDs if you don’t want people guessing other records.
  • Handle actions that are not CRUD with a sub-resource, such as POST /v1/orders/{orderId}/refunds. That keeps the noun-based model and gives the action its own history.

Use HTTP methods the way HTTP defines them

The HTTP standard (RFC 9110, HTTP Semantics) defines which methods are “safe” (they don’t change state) and which are “idempotent” (repeating the request has the same effect as sending it once). Clients, proxies and retry logic rely on those properties, so honor them.

Method Typical use Safe Idempotent Typical success code
GET Read a resource or list Yes Yes 200 OK
POST Create a resource or trigger an action No No 201 Created (with Location header) or 202 Accepted
PUT Replace a resource fully No Yes 200 OK or 204 No Content
PATCH Update part of a resource No Not guaranteed 200 OK
DELETE Remove a resource No Yes 204 No Content

2 common mistakes to avoid:

  • Changing data with GET. Crawlers, link previews and caches all assume GET is safe. A GET /orders/123/cancel endpoint will eventually cancel orders nobody meant to cancel.
  • Unsafe retries on POST. Because POST is not idempotent, a network timeout can lead to duplicate orders or payments. Many payment APIs solve this with an idempotency key header that the client generates and the server remembers for a time window. If your API creates money-moving or inventory-changing records, consider the same pattern.

Return the right status codes

Status codes are the first thing a client checks, so use them precisely. You don’t need dozens. A small, consistent set covers most APIs:

  • 200 OK: successful read or update.
  • 201 Created: a new resource was created. Return its URL in the Location header.
  • 204 No Content: success with no body, common for DELETE.
  • 400 Bad Request: malformed request or failed validation.
  • 401 Unauthorized: missing or invalid credentials.
  • 403 Forbidden: authenticated, but not allowed.
  • 404 Not Found: the resource does not exist (or the caller cannot know it exists).
  • 409 Conflict: the request clashes with current state, such as a duplicate or a version mismatch.
  • 422 Unprocessable Content: syntactically valid but semantically wrong input. Some teams use 400 for all validation errors instead, which is also fine if consistent.
  • 429 Too Many Requests: the client hit a rate limit. Include a Retry-After header.
  • 500 and 503: server-side errors. Never return 200 with an error message in the body.

Make errors consistent and useful

Nothing frustrates API users more than every endpoint inventing its own error shape. Pick one format and use it everywhere.

A good default is the IETF’s Problem Details for HTTP APIs standard, RFC 9457, published in July 2023 to replace RFC 7807. It defines a JSON object served with the application/problem+json media type:

json { "type": "https://api.example.com/problems/insufficient-stock", "title": "Not enough stock", "status": 409, "detail": "Only 2 units of SKU 4471 are available; 5 were requested.", "instance": "/v1/orders/8f3c2a", "errors": [ { "field": "items[0].quantity", "message": "Must be 2 or fewer" } ] }

Tips for error responses:

  • Give each error type a stable, documented identifier so clients can handle it in code.
  • Put human-readable detail in detail, but never leak stack traces, SQL or internal hostnames. The OWASP Top 10 lists security misconfiguration among the most common web risks.
  • For validation errors, list every failing field at once rather than making the client fix them one by one.
  • Include a request or trace ID so support teams can find the matching logs.

Key takeaway: Consistency beats cleverness. One naming style, one error format and one pagination pattern across every endpoint will save your API consumers more time than any individual “perfect” design choice.

Paginate every list from day one

Any endpoint that returns a list will eventually return too many items. Add pagination before launch, because adding it later is a breaking change for clients that expect the full list.

Pattern Example request Pros Cons
Offset and limit GET /v1/orders?offset=40&limit=20 Simple, supports jumping to page N Slow on large tables; items shift when data changes
Page number GET /v1/orders?page=3&pageSize=20 Easy for UIs Same drift problems as offset
Cursor (token) GET /v1/orders?pageToken=eyJpZCI6...&pageSize=20 Stable and fast on large or changing data Cannot jump to an arbitrary page

For most new APIs, cursor-based pagination is the safer default. Google’s API Improvement Proposal on pagination (AIP-158) describes a widely copied version: the request accepts a page size and an opaque page token, the response returns a next_page_token, and an empty token means the end of the list. Keep the token opaque so you can change how it works internally.

A sample response:

json { "data": [ { "id": "ord_101" }, { "id": "ord_102" } ], "nextPageToken": "c2Vla19hZnRlcl9vcmRfMTAy" }

Also set a sensible default and a maximum page size, and pair pagination with filtering and sorting parameters such as ?status=shipped&sort=-createdAt.

Version only for breaking changes

Versioning lets you change your API without breaking existing clients. The 3 common approaches:

  • URL path: /v1/orders. The most visible and the easiest to route and cache. Most public APIs use it.
  • Header: a custom header or media type parameter. Keeps URLs clean but is harder to test in a browser.
  • Query parameter: ?api-version=2026-10-01. Used by some large cloud platforms, often with date-based versions.

Whichever you choose, the bigger discipline is avoiding breaking changes in the first place:

  • Non-breaking (safe in the same version): adding a new endpoint, adding an optional field, adding a new optional query parameter.
  • Breaking (needs a new version): removing or renaming a field, changing a field’s type, making an optional field required, changing error codes clients depend on.

Tell clients to ignore unknown fields, publish a deprecation timeline, and keep the old version running long enough for consumers to move.

Security and performance basics

Design these in rather than bolting them on:

  • HTTPS only, with authentication via OAuth 2.0 or scoped API keys. Check authorization on every object, not just every endpoint, so users cannot read other customers’ orders by changing an ID.
  • Rate limiting with clear 429 responses.
  • Input validation against your schema on every request.
  • Caching headers such as ETag and Cache-Control on GET responses, and If-Match for safe concurrent updates.
  • Event notifications for long-running work. Instead of making clients poll, send webhooks or publish events, a pattern explained in our guide to event-driven architecture.

Where to start: a step-by-step design process

  1. List the use cases. Write down what clients need to do, in plain language (“a customer views past orders”).
  2. Model the resources. Turn nouns into resources and decide their relationships and IDs.
  3. Write the contract first in OpenAPI. The OpenAPI Specification, maintained by the OpenAPI Initiative (version 3.2.0 was released in September 2025), lets you describe endpoints, schemas and errors in a file that generates docs, mocks and client SDKs.
  4. Review it with real consumers. Have a frontend or partner developer try the mock before you write backend code.
  5. Pick your conventions and write them down. Naming, errors, pagination, dates (use ISO 8601 in UTC) and versioning in a 1-page style guide.
  6. Build, test and lint the contract. Use contract tests in your CI/CD pipeline so the implementation never drifts from the spec.
  7. Publish docs and a changelog. Treat the API as a product with its own release notes.

Not sure REST is the right fit at all? Our comparison of REST vs GraphQL covers when a query-based API makes more sense.

Conclusion

Strong REST API design comes down to predictable patterns: noun-based resources, correct HTTP methods and status codes, one error format, pagination on every list and versioning reserved for real breaking changes. Write the contract before the code, get feedback early, and document your conventions so every new endpoint feels familiar. Your future self, and every developer who calls your API, will thank you.

Sources

Questions readers ask

Frequently asked questions

What are the most important REST API design best practices?

The most important practices are using plural noun resources, applying HTTP methods and status codes correctly, returning one consistent error format, paginating every list, and versioning only for breaking changes. Add HTTPS, authentication and rate limiting from the start. Writing the contract first in OpenAPI helps you catch design problems before any code exists.

Should REST API endpoints use nouns or verbs?

Use nouns. The URL identifies the resource, such as /orders/123, and the HTTP method supplies the verb: GET to read, POST to create, PATCH to update, DELETE to remove. For actions that don't fit create, read, update or delete, model the action as a sub-resource, such as POST /orders/123/refunds, instead of a verb path like /cancelOrder.

What is the best way to version a REST API?

URL path versioning, such as /v1/orders, is the most common and easiest for clients to understand and test. Header and query parameter versioning also work. The more important rule is to avoid breaking changes: add fields and endpoints without bumping the version, and create a new version only when you must remove, rename or change the type of something.

What is the difference between PUT and PATCH?

PUT replaces the entire resource with the representation you send, so missing fields may be cleared. PATCH applies a partial update to only the fields you include. Under the HTTP standard, PUT is idempotent, meaning repeating it has the same effect as sending it once, while PATCH is not guaranteed to be. Many APIs offer PATCH for everyday edits.

How should a REST API return errors?

Return an accurate HTTP status code plus a consistent JSON body. A good default is RFC 9457 Problem Details, which uses fields like type, title, status, detail and instance with the application/problem+json media type. List all validation errors at once, include a request ID for support, and never expose stack traces or internal system details.

What is cursor-based pagination?

Cursor-based pagination returns a page of results plus an opaque token that points to where the next page starts. The client sends that token back to get the next page, and an empty token means there are no more results. It stays fast and accurate on large or frequently changing data, unlike offset pagination, but it cannot jump straight to page 50.

Do I need OpenAPI to design a REST API?

You don't strictly need it, but it is strongly recommended. An OpenAPI file is a machine-readable contract that generates documentation, mock servers, client SDKs and contract tests. Designing in OpenAPI first lets consumers review and try the API before you build it, which is far cheaper than changing a live API.

Reader reviews

Was this guide useful

No reviews yet. Share your experience and help other readers.

Write a review
Your rating
Never shown publicly

Reviews are checked before they appear. We never show your email. Please review only places or services you have used.

Manishdai
Written by

Manishdai

Manishdai writes about the technology that is reshaping every industry — from AI agents and cloud platforms to healthtech, fintech and the IT companies building it all across the United States.