REST API Design Best Practices: A Developer Guide
A well-designed API is a joy to integrate with. A poorly designed one generates endless support tickets, confused developers, and broken integrations. REST API design is part technical, part user experience. Your API's consumers are developers — and developer experience (DX) is just as important as correctness. This guide covers the principles and patterns of excellent REST API design.
Resource Naming: Nouns, Not Verbs
REST uses HTTP methods (GET, POST, PUT, PATCH, DELETE) as the verbs. Your URL paths should be nouns representing resources, not verbs representing actions. Prefer: /users, /orders/42/items. Avoid: /getUsers, /createOrder, /deleteUserById. Use plural nouns for collections. Use lowercase with hyphens for multi-word resources: /blog-posts, not /blogPosts or /blog_posts.
/users/42/orders means "orders belonging to user 42". Don't nest more than 2-3 levels deep — deeply nested URLs are hard to read and often signal that a resource should be a top-level endpoint.HTTP Methods and Idempotency
Use HTTP methods as intended. GET must be safe (read-only, no side effects). GET and PUT must be idempotent — calling them multiple times has the same effect as calling them once. POST is neither safe nor idempotent — it creates a new resource each time. PATCH is for partial updates (only the fields you send are changed). PUT replaces the entire resource.
Response Format Consistency
Pick a response envelope format and use it consistently. A common pattern:
// Success (single resource)
{ "data": { "id": 42, "name": "Alice" } }
// Success (collection)
{ "data": [...], "meta": { "total": 100, "page": 1 } }
// Error
{ "error": { "code": "VALIDATION_ERROR", "message": "Email is invalid", "field": "email" } }
Use our JSON Formatter to validate your response examples before publishing documentation.
Versioning Strategies
APIs need versioning because changes will break existing clients. Three common approaches:
- URL versioning:
/v1/users,/v2/users. Explicit and easy to test. The most widely used approach. - Header versioning:
Accept: application/vnd.api+json;version=1. Keeps URLs clean but is harder to test in a browser. - Query parameter:
/users?version=1. Easy but pollutes the query string.
Pagination
Never return unbounded collections. Use cursor-based pagination for large datasets that change frequently (Twitter-style feeds) — a cursor points to a specific record and is stable even as items are added. Use offset pagination for smaller datasets and admin interfaces where jumping to a page number makes sense. Always include pagination metadata: total, page, per_page, next_cursor.
Error Handling
Errors should be predictable, informative, and machine-readable. Always return consistent JSON error bodies. Include: an error code (machine-readable constant like "RATE_LIMIT_EXCEEDED"), a human-readable message, the field that caused the error (for validation), and a documentation link for complex errors. Use the correct HTTP status codes — see our companion guide to HTTP status codes.
Rate Limiting Headers
Communicate your rate limits to clients via response headers: X-RateLimit-Limit: 1000, X-RateLimit-Remaining: 847, X-RateLimit-Reset: 1711670400 (Unix timestamp — use our Timestamp Converter to interpret). When the limit is exceeded, return 429 with a Retry-After header.
Authentication Patterns
For server-to-server: API keys in Authorization: Bearer key or X-API-Key: key. For user-facing apps: OAuth 2.0 with JWTs. Always use HTTPS — sending credentials over HTTP is unacceptable in production. Implement CORS properly for browser-based clients. Generate strong API keys with our Password Generator.
Documentation: OpenAPI / Swagger
An undocumented API is an unusable API. The OpenAPI specification (formerly Swagger) is the industry standard for describing REST APIs. Write your spec in YAML (use our JSON to YAML converter to convert from JSON) and use tools like Swagger UI, Redoc, or Stoplight to render interactive documentation. Generate client SDKs automatically from the spec.
← Back