A good REST API feels obvious. You guess the URL and you're right; you read one error and you know exactly what to fix. A bad one makes you keep the docs open in a second monitor and reverse-engineer behavior from 200-responses that secretly contain failures.
The difference is rarely the framework. It's a handful of conventions applied consistently. Here are twelve rules that, followed together, produce an API integrators don't complain about.
1. Name resources with plural nouns, not verbs
The HTTP method is the verb. The path is the thing.
GET /users # list
POST /users # create
GET /users/42 # read
PATCH /users/42 # partial update
DELETE /users/42 # delete
Not /getUser, /createUser, /user/delete. The method already says what you're doing.
2. Nest only to show ownership — then stop
GET /users/42/orders is fine: orders belong to a user. But don't go three levels deep. Past one level of nesting, switch to query parameters: GET /orders?user=42&status=open.
3. Use HTTP status codes honestly
Return the code that's true, not 200 with {"success": false}.
-
200OK,201Created,204No Content -
400bad request,401unauthenticated,403unauthorized,404not found,409conflict,422validation failed -
429rate limited,500server error
A client should be able to branch on the status line alone.
4. Return a consistent error shape
Every error, everywhere, in the same structure. Machine-readable code, human message, and field-level detail when relevant.
{
"error": {
"code": "validation_failed",
"message": "Email is not valid.",
"details": [{ "field": "email", "issue": "format" }]
}
}
Pick this shape once and never deviate. Clients build error handling around it.
5. PATCH for partial, PUT for full replacement
PATCH /users/42 with { "email": "..." } changes one field. PUT means "replace the whole resource with this representation." Don't make PUT behave like PATCH; you'll surprise everyone.
6. Make writes idempotent where you can
PUT and DELETE should be safe to retry. For POST (create), support an Idempotency-Key header so a client that retries after a network blip doesn't create two orders.
7. Paginate from day one
A collection that returns "all rows" works in dev and falls over in production. Decide on a strategy early. Cursor pagination scales better than offset for large, changing datasets.
GET /users?limit=50&cursor=eyJpZCI6MTAwfQ
{ "data": [ ... ], "next_cursor": "eyJpZCI6MTUwfQ", "has_more": true }
8. Filter, sort, and select with query params
Keep the path about identity; put the query in the query string.
GET /orders?status=open&sort=-created_at&fields=id,total
-created_at for descending is a tiny convention that saves a lot of guessing.
9. Version at the edge, before you need to
Put a version in the URL (/v1/...) or a header from the very first release. The day you must make a breaking change, /v2 lets existing clients keep working instead of breaking in production.
10. Be consistent about field casing and dates
Pick snake_case or camelCase and apply it to every field in every response. Use ISO 8601 UTC for timestamps (2026-06-20T11:00:00Z). Mixed casing and ambiguous local dates are a thousand tiny paper cuts.
11. Don't leak your database
Your API is a contract, not a SQL dump. Don't expose internal auto-increment IDs if they reveal volume, don't return columns clients shouldn't see, and don't let the response shape change because you refactored a table. The representation is yours to design.
12. Document with real examples, and keep them true
The best docs are copy-pasteable requests and their exact responses. Generate an OpenAPI spec from the code so the docs can't drift from behavior — a lying example is worse than none.
The through-line
Every rule above is really one rule: be predictable. When naming, status codes, errors, pagination, and dates are uniform across every endpoint, a developer who learns one part of your API has effectively learned all of it.
If you want these conventions as a ready-made starting point — endpoint templates, the standard error envelope, pagination helpers, and an OpenAPI skeleton — the REST API Design Guide packages them so a new service is consistent on day one instead of after the third refactor.
Bottom line
APIs are read far more often than they're written. Spend the design effort up front on consistency, and every future integrator — including future you — inherits an interface they can guess their way through.
Top comments (0)