In short
Good API design makes an interface predictable for the developers who use it. For REST APIs that means plural nouns for resources, HTTP methods for actions, accurate status codes, a clear versioning policy, consistent pagination, structured error responses and standard authentication such as OAuth tokens or API keys sent in headers.
API design is the part of building an API that nobody sees until it goes wrong. The code behind an endpoint can be rewritten next week. The shape of the endpoint, once other people depend on it, is much harder to change. That is why a little care up front saves a lot of awkward emails later.
Think of an API as the signage in a large building. If every floor numbers its rooms differently and some doors open inward while others slide, visitors get lost, even if every room is perfectly built. Consistent signage lets people find their way without asking. From here on, let’s be specific about what consistent means for a REST API.
Why API design matters
An API is a promise. Every client that integrates with it, whether a mobile app, a partner’s backend or a script someone wrote on a Friday afternoon, encodes your choices into its own code. Change a field name and they break. Return a 200 for an error and they silently store bad data.
Good design reduces three costs: the time it takes a new developer to understand the API, the number of support questions you receive, and the risk of breaking clients when you evolve it. If you need a refresher on the basics first, our explainer on what an API is covers requests, responses and endpoints.
Resource naming
REST APIs are organized around resources, the nouns of your domain: users, orders, invoices. Each resource gets a URL, and actions come from HTTP methods, not from verbs in the path.
- Use plural nouns for collections:
/users,/orders - Identify single items by ID:
/users/42 - Express relationships by nesting one level:
/users/42/orders - Use lowercase and hyphens for multi-word names:
/shipping-addresses - Avoid verbs in paths such as
/getUseror/createOrder
Deep nesting like /users/42/orders/7/items/3/notes becomes hard to work with. Once an item has its own stable ID, give it a top-level route (/order-items/3) and use query parameters for filtering, such as /orders?status=shipped.
HTTP methods
HTTP already defines the verbs, so use them as intended. The semantics are defined in RFC 9110, the HTTP specification.
- GET reads a resource or collection and must not change anything
- POST creates a new resource, usually in a collection, or triggers a process
- PUT replaces a resource entirely with the representation you send
- PATCH updates part of a resource
- DELETE removes a resource
Two properties matter. Safe methods (GET, HEAD) do not change server state. Idempotent methods (GET, PUT, DELETE) give the same result if repeated, which means a client can safely retry them after a network error. POST is neither, so for operations like payments, many APIs accept an idempotency key header that lets the server recognize and ignore duplicate requests.
Status codes
Status codes are how the API tells clients what happened before they read a single byte of the body. Use the right ones and clients can handle responses generically.
- 200 OK for a successful read or update that returns content
- 201 Created when a POST creates a resource, ideally with a
Locationheader pointing to it - 204 No Content for a successful request with nothing to return, such as many DELETEs
- 400 Bad Request when the request is malformed
- 401 Unauthorized when authentication is missing or invalid
- 403 Forbidden when the caller is authenticated but not allowed
- 404 Not Found when the resource does not exist
- 409 Conflict when the request clashes with current state, such as a duplicate
- 422 Unprocessable Content when the request is well formed but fails validation
- 429 Too Many Requests when the client exceeds a rate limit
- 500 Internal Server Error when something broke on your side
The most important rule is the simplest: never return 200 with an error message inside. It defeats every client that checks the status first.
Error responses
A status code says what category of problem occurred. The body should say exactly what went wrong and, where possible, how to fix it. Pick one error format and use it everywhere.
A good default is the standard format defined in RFC 9457, Problem Details for HTTP APIs, served with the media type application/problem+json:
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation failed",
"status": 422,
"detail": "The email field must be a valid address.",
"instance": "/users"
}
You can add your own fields, such as a list of invalid fields. Never include stack traces or internal details in error responses, since they help attackers more than they help users.
Versioning
Sooner or later you will need to make a breaking change. Versioning lets existing clients keep working while new ones adopt the change. The common approaches are a version in the path (/v1/users), a custom header, or a version parameter in the media type.
Path versioning is the most visible and the easiest for beginners to understand. Whatever you choose, the more important decisions are about policy. Define what counts as breaking (removing a field, renaming one, changing a type, tightening validation). Add fields freely, since well-behaved clients ignore unknown fields. Announce deprecations well in advance and give people time to move.
Pagination
Never return an unbounded collection. A list that has ten items today may have ten million next year. The two common approaches are offset pagination and cursor pagination.
Offset pagination uses parameters like ?limit=20&offset=40. It is simple and lets clients jump to any page, but it gets slow on large tables and can skip or repeat items when data changes between requests. Cursor pagination returns an opaque token pointing to the last item seen:
{
"data": [{ "id": 101 }, { "id": 102 }],
"next_cursor": "eyJpZCI6MTAyfQ"
}
The client passes ?cursor=eyJpZCI6MTAyfQ to get the next page. Cursors stay stable as data changes and perform well at scale, which is why many large public APIs use them. Whichever you pick, set a sensible default page size and a maximum.
Authentication
Most APIs need to know who is calling. Send credentials in the Authorization header, never in the URL, where they end up in logs and browser history. Always require HTTPS.
API keys are simple and fine for server-to-server use. When users grant third-party apps access to their data, OAuth 2.0 is the standard, issuing short-lived access tokens with limited scopes. Whatever the method, design for least privilege: a token should be able to do only what its holder needs, and it should be possible to revoke it without affecting anyone else.
Try it
Let’s design a tiny API on paper before writing any code. Pick a simple domain, such as a library that lends books. Write down the resources, their URLs, which methods each supports, and the status code each should return in both success and failure.
Then test your design against three questions. What does a client receive when it asks for a book that does not exist? What happens if the same “borrow” request is sent twice because of a network retry? How does a client get the second page of 10,000 books? If you can answer each one with a specific status code and response shape, your design is already better than many in production.
Common mistakes
API design mistakes tend to be small decisions that seemed harmless at the time. A short list of the classics:
- Verbs in URLs, such as
/createUser, alongside RESTful routes elsewhere - Returning 200 for every response and putting the real status in the body
- Inconsistent naming, with
user_idin one endpoint anduserIdin another - Collections without pagination
- Breaking changes shipped without a new version or warning
- Error messages that say “Something went wrong” and nothing else
- Credentials accepted in query strings
Where API design fits
APIs connect almost every other category of developer tool, from AI assistants to hosting platforms. For the wider map, see our guide to dev tools.
What to learn next: once your API is designed, it needs to run somewhere reliable. Our Docker tutorial for beginners shows how to package it, and our guide to best web hosting explains where to deploy it.
Common questions
What is API design?
API design is the process of deciding how an API looks to the developers who use it: its endpoints, data formats, methods, status codes, errors, versioning and authentication. Good API design makes the interface predictable and consistent, so clients can integrate quickly and keep working as the API evolves.
What are the principles of good REST API design?
Use nouns for resources and HTTP methods for actions, return accurate status codes, keep naming consistent, paginate collections, return structured errors, version breaking changes and secure every endpoint with HTTPS and proper authentication. Consistency across the whole API matters more than any single choice.
Should I put the version in the URL?
Putting the version in the path, such as /v1/, is common and easy to understand, which makes it a sensible default. Header-based versioning keeps URLs cleaner but is less visible. The more important decision is a clear policy about what counts as a breaking change.
What is the difference between PUT and PATCH?
PUT replaces the entire resource with the representation you send, so omitted fields may be cleared. PATCH applies a partial update, changing only the fields you include. PUT is idempotent by definition, while PATCH may or may not be, depending on how the update is expressed.
Is REST the only way to design an API?
No. GraphQL lets clients request exactly the fields they need from a single endpoint, and gRPC is popular for fast, strongly typed communication between internal services. REST remains the most common style for public web APIs because it builds directly on HTTP.
Newsletter waiting list
Get notified when our newsletter launches
We are not sending a newsletter yet. Leave your email and we will write once, when the first issue is ready.
