REST API Best Practices Every Developer Should Know
Designing a REST API that other developers love takes more than just getting the HTTP verbs right. Here are the patterns that separate good APIs from great ones.
URLs name resources, not actions
The most common mistake in REST API design is putting verbs in URLs. /getUsers, /createPost, /deleteAccount/42 — these feel natural because they describe what you want to happen, but they're wrong in REST. The URL identifies a resource. The HTTP method describes the action. GET /users and POST /users hit the same URL — the method tells you whether you're reading or creating.
Keep URLs lowercase and hyphen-separated. /blog-posts is conventional; /blogPosts mixes URL conventions with camelCase, which belongs in code, not in paths. Use plural nouns for collections (/users, /orders) and identify individual resources by ID (/users/42).
Status codes are part of the contract
A lot of APIs return 200 OK for everything and put the actual result in a success field in the body. This makes every client do extra parsing to figure out whether the request actually succeeded, and it wastes the information that HTTP status codes were designed to carry.
Return 201 Created when a POST creates a new resource. Return 204 No Content for successful deletes — there's nothing to return. Use 400 for client validation errors, 401 when the request is missing authentication, 403 when the user is authenticated but doesn't have access, 404 when the resource doesn't exist, and 409 for conflicts like a duplicate email on registration. Save 500 for actual unexpected failures — don't return it for validation errors just because you haven't bothered to distinguish them.
Version from the start, not after the first breaking change
The right time to add versioning to an API is before anyone is using it. Adding it later means coordinating a migration across every existing client at once, or living with inconsistent URL patterns forever. A /v1/ prefix on every route is the simplest approach and the one most teams actually follow through on.
GET /v1/users/42
GET /v2/users/42
Header-based versioning (Accept: application/vnd.myapp.v1+json) is cleaner in theory but harder to test in a browser, harder to document, and harder to explain to new API consumers. URL versioning wins on practical grounds for most teams.
Error responses need a consistent shape
Clients that integrate with your API will eventually hit an error. When they do, they should get a response they can parse programmatically — not just an HTTP status code and a message string they have to read. A machine-readable error code, a human-readable message, and a list of field-level errors for validation failures covers most cases.
{
"status": 400,
"code": "VALIDATION_ERROR",
"message": "email is required",
"errors": [
{ "field": "email", "message": "must not be blank" }
]
}
The code field is what clients actually branch on. The message is for humans debugging the integration. Don't make clients parse message strings to figure out what went wrong — that breaks every time you reword an error. When you're inspecting raw API responses during development, the JSON Formatter makes payload structure immediately readable.
Never return an unbounded collection
An endpoint that returns all records from a table will work fine in development with fifty rows of test data and quietly fail in production when there are fifty thousand. Pagination should be on from the start for any collection endpoint, not bolted on later when someone complains about a slow response.
GET /users?page=2&size=25
Include pagination metadata in the response so clients know whether there are more pages to fetch. Offset pagination is simpler to implement; cursor-based pagination handles large datasets better and avoids the "rows shifted between pages" problem you get when records are being created during pagination. For most applications, offset pagination is fine until it isn't — and by then you'll know why you need to switch.