How to design REST APIs that developers love — resource naming, HTTP methods, status codes, versioning, pagination, and error handling patterns.
A well-designed REST API is a joy to work with. A poorly designed one generates endless support tickets. These are the patterns that separate production-grade APIs from amateur ones.
Resources are nouns, not verbs. HTTP methods are the verbs.
✓ Good ✗ Bad
GET /users GET /getUsers
POST /users POST /createUser
GET /users/123 GET /getUserById?id=123
PUT /users/123 POST /updateUser/123
DELETE /users/123 DELETE /deleteUser?id=123
GET /users/123/orders GET /getUserOrders/123
POST /users/123/orders POST /createOrderForUser
Rules:
/users, /orders, /products/order-items, not /orderItems| Method | Use | Idempotent | Body |
|---|---|---|---|
| GET | Fetch resource(s) | Yes | No |
| POST | Create new resource | No | Yes |
| PUT | Replace entire resource | Yes | Yes |
| PATCH | Update partial resource | No | Yes |
| DELETE | Remove resource | Yes | No |
Idempotent means calling it multiple times has the same result as calling it once.
2xx — Success
200 OK — GET, PUT, PATCH success
201 Created — POST success (include Location header)
204 No Content — DELETE success, or PATCH with no body
3xx — Redirection
301 Moved Permanently — resource moved, update your links
304 Not Modified — for caching (ETag/If-None-Match)
4xx — Client Errors
400 Bad Request — invalid JSON, missing required field
401 Unauthorized — missing or invalid auth token
403 Forbidden — authenticated but not allowed
404 Not Found — resource doesn't exist
409 Conflict — duplicate resource, version conflict
422 Unprocessable — valid JSON but fails validation
429 Too Many Reqs — rate limit exceeded
5xx — Server Errors
500 Internal Error — unhandled exception
502 Bad Gateway — upstream service failed
503 Unavailable — server overloaded or down for maintenance
Be consistent. Every error response should have the same structure:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "age", "message": "Must be at least 18" }
],
"requestId": "req_01HXYZ...",
"timestamp": "2025-01-15T10:30:00Z"
}
}
Never return { success: false, data: null } without error context.
Three common approaches:
# URL path (most common, easiest to cache)
GET /api/v1/users
GET /api/v2/users
# Header (clean URLs, harder to test)
GET /api/users
Accept: application/vnd.myapi.v2+json
# Query parameter (avoid — versioning should be stable)
GET /api/users?version=2
Recommendation: URL path versioning for public APIs. Only version when introducing breaking changes.
Never return unbounded lists. Three patterns:
# Offset pagination (simple, works everywhere)
GET /users?page=2&limit=20
Response:
{
"data": [...],
"pagination": {
"page": 2, "limit": 20,
"total": 340, "pages": 17
}
}
# Cursor pagination (better for real-time data)
GET /posts?after=01ARZ3NDEK&limit=20
Response:
{
"data": [...],
"nextCursor": "01ARZXXX",
"hasMore": true
}
Cursor pagination is better for feeds and frequently updated data — offset pagination skips or duplicates items when records are added/removed mid-pagination.
# Filtering
GET /users?status=active&role=admin
GET /orders?created_after=2025-01-01&created_before=2025-02-01
# Sorting
GET /users?sort=created_at&order=desc
GET /users?sort=-created_at # minus prefix for desc (OpenAPI convention)
# Field selection (sparse fieldsets)
GET /users?fields=id,name,email # return only these fields
Content-Type: application/json; charset=utf-8
X-Request-Id: req_01HXYZ... # for distributed tracing
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1705315200
Cache-Control: no-store # for auth endpoints
Test your API endpoints with the HTTP Status Codes reference and convert curl examples to fetch/axios with the cURL Converter tool.
HTTP Status Codes
Complete reference for all HTTP status codes with descriptions, use cases, and RFC links.
cURL Converter
Convert curl commands to fetch, axios, HTTPie, and other HTTP client formats.
JSON Formatter & Validator
Format, validate, and beautify JSON data instantly. Detect errors with precise line numbers.