API design checklist
Everything in one place
We’ve covered a lot of ground. URLs, methods, status codes, error formatting, pagination, filtering, versioning, rate limiting, bulk operations, long-running tasks, and HATEOAS. That’s a lot to keep in your head when you’re building an endpoint.
This lesson pulls everything together into a checklist you can use before shipping any new endpoint or API version. Think of it as the review process for your API design.
The checklist
URLs
- URLs use nouns, not verbs (
/books, not/getBooks) - Collection URLs are plural (
/books, not/book) - Item URLs use the collection path plus an ID (
/books/:id) - Nesting is limited to two levels (
/books/:id/reviews, not deeper) - Nested routes are used only when the child depends on the parent
- Independent resources use flat routes with query filters
HTTP methods
- GET retrieves data and has no side effects
- POST creates new resources
- PUT replaces an entire resource
- PATCH updates specific fields
- DELETE removes a resource
- No data modification happens on GET
Status codes
- 200 for successful GET, PUT, PATCH
- 201 for successful POST (with Location header)
- 204 for successful DELETE (no body)
- 400 for malformed requests
- 401 for unauthenticated
- 403 for unauthorized
- 404 for not found
- 409 for conflicts (duplicates)
- 422 for validation errors (optional, 400 is also acceptable)
- 429 for rate limiting (with Retry-After)
Error responses
- Every error uses the same format:
{ error: { code, message, details? } } - Error codes are machine-readable strings (
NOT_FOUND, not404) - Validation errors include per-field details
- Error messages are helpful but don’t leak implementation details
Pagination
- List endpoints are paginated (never return unbounded results)
- Default limit is reasonable (20)
- Maximum limit is enforced (100)
- Cursor-based pagination for data that changes frequently
- Response wraps data in an object:
{ data: [...], pagination: { ... } }
Filtering and sorting
- Filters use field names as query parameters (
?genre=fiction) - Sort uses
-prefix for descending (?sort=-publishedAt) - Search uses
?q=for full-text - Unknown parameters are ignored (not errored)
- Sort field whitelist prevents SQL injection
Versioning
- Breaking changes require a version bump
- Additive changes do not require a version bump
- Deprecated versions include
DeprecationandSunsetheaders
Rate limiting
- Every response includes
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset - 429 responses include
Retry-After - Limits are per-client (API key or IP)
General
- Responses are JSON with
Content-Type: application/json - POST and PUT endpoints validate
Content-Type - Idempotency keys are supported for non-idempotent operations
- Long-running operations return 202 with a status URL
Common mistakes to watch for
Returning bare arrays. If GET /books returns [...] instead of { data: [...] }, you can’t add pagination metadata later without breaking every consumer. Always wrap lists in an object from the start.
Using 200 for everything. A created resource returns 200 instead of 201. A deleted resource returns 200 with { success: true } instead of 204. Status codes carry meaning. Use them.
Inconsistent naming. One endpoint returns authorId, another returns author_id, a third returns AuthorID. Pick a convention (camelCase or snake_case) and stick with it everywhere.
Exposing database internals. Column names like created_at in your responses leak the database schema. Consider mapping to createdAt (camelCase) for the API surface, or at least use the same convention consistently.
No pagination on list endpoints. Works fine with 10 items. Falls over with 10,000. Always paginate, even if the list is small today.
Ignoring idempotency. A payment API without idempotency keys charges the customer twice when the network drops the response and the client retries.
The review process
Before merging a new endpoint, walk through these questions:
- Read the URL aloud. Does it describe a resource? Can you guess what it does without documentation?
- Check the method. Is it the right HTTP method? Does GET have side effects?
- Check the status codes. Does the happy path return the right code? Do error cases return the right codes?
- Check the error format. Does it match the standard format? Does it include a machine-readable code?
- Test with curl. Can you use the endpoint with curl and understand the response?
- Check pagination. If it returns a list, is it paginated? Is the limit capped?
If all six pass, the endpoint is ready.
Exercises
Exercise 1: Go through the checklist for your bookstore API. How many items pass?
Exercise 2: Find any inconsistency in naming (like author_id vs authorId). Fix it across all endpoints.
Exercise 3: Review a real API you use against this checklist. How does it score?
What is the most common API design mistake?