REST API Best Practices
Design patterns and guidelines for building scalable, maintainable APIs
URL Design
Well-designed URLs are intuitive, predictable, and follow REST conventions.
๐ Use Nouns for Resources
URLs should represent resources (things), not actions. Use HTTP methods to define the action.
GET /users - Get all usersGET /users/123 - Get user 123POST /users - Create a userDELETE /users/123 - Delete user 123
GET /getUsersPOST /createUserGET /deleteUser/123POST /users/delete
๐ Use Plural Nouns
Consistently use plural nouns for resource collections, even when accessing a single resource.
/users/users/123/products/orders/456/items
/user/user/123/product/order/456/item
๐๏ธ Use Kebab-Case
Use lowercase letters and hyphens for multi-word resource names.
/user-profiles/order-items/api-keys
/userProfiles/UserProfiles/user_profiles
๐ Use Hierarchical Relationships
Express relationships between resources through URL hierarchy.
GET /users/123/orders - User's ordersGET /orders/456/items - Order's itemsGET /posts/789/comments - Post's comments
GET /getOrdersByUser?userId=123GET /orderItems?orderId=456
API Versioning
Version your APIs to allow evolution without breaking existing clients. See our comprehensive versioning guide for detailed strategies.
URL Path Versioning
โญ Recommended
Include the version in the URL path. Most explicit and widely used approach.
GET /api/v1/usersGET /api/v2/users
Pros: Clear, cacheable, easy to implement
Cons: Not technically RESTful (versions aren't resources)
Header Versioning
Use a custom header to specify the API version.
GET /api/usersAPI-Version: 2
Pros: Clean URLs, more RESTful
Cons: Less visible, harder to test in browser
Query Parameter Versioning
Pass the version as a query parameter.
GET /api/users?version=2
Pros: Easy to add, explicit
Cons: Can be omitted, caching issues
Accept Header Versioning
Use content negotiation with the Accept header.
GET /api/usersAccept: application/vnd.api.v2+json
Pros: Most RESTful approach
Cons: Complex, easy to get wrong
Pagination
Always paginate large collections to improve performance and usability. See our pagination guide for cursor, offset, and keyset strategies.
Offset-Based Pagination
Use page and limit (or offset) parameters.
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 150,
"total_pages": 8,
"has_next": true,
"has_prev": true
},
"links": {
"self": "/api/users?page=2&limit=20",
"first": "/api/users?page=1&limit=20",
"prev": "/api/users?page=1&limit=20",
"next": "/api/users?page=3&limit=20",
"last": "/api/users?page=8&limit=20"
}
}
Pros: Simple, allows jumping to any page
Cons: Performance degrades with large offsets, inconsistent with data changes
Cursor-Based Pagination
Use an opaque cursor for more efficient pagination.
{
"data": [...],
"pagination": {
"limit": 20,
"has_more": true,
"next_cursor": "eyJpZCI6MTIwfQ",
"prev_cursor": "eyJpZCI6ODB9"
}
}
Pros: Consistent performance, handles real-time data
Cons: Can't jump to arbitrary pages
Filtering, Sorting & Field Selection
๐ Filtering
Use query parameters for filtering resources.
For complex queries, consider:
?filter[status]=active- Bracketed notation?q=search+term- Full-text search?price_gte=100&price_lte=500- Range filters
๐ Sorting
Use a sort parameter with field names and direction.
Common conventions:
sort=name- Ascending by namesort=-name- Descending by name (minus prefix)sort=name:asc- Explicit directionsort=-created_at,name- Multiple fields
๐ Field Selection (Sparse Fieldsets)
Allow clients to request only specific fields to reduce payload size.
{
"data": [
{"id": 1, "name": "John", "email": "john@example.com"},
{"id": 2, "name": "Jane", "email": "jane@example.com"}
]
}
Error Handling
Consistent error responses help clients understand and handle failures gracefully. See our error handling guide for standard formats and best practices.
Standard Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request could not be validated",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
},
{
"field": "age",
"code": "OUT_OF_RANGE",
"message": "Age must be between 18 and 120"
}
],
"request_id": "abc123-def456",
"timestamp": "2023-06-20T14:30:00Z",
"documentation_url": "https://api.example.com/docs/errors#VALIDATION_ERROR"
}
}
๐ฏ Use Appropriate Status Codes
Match HTTP status codes to error types. 4xx for client errors, 5xx for server errors.
๐ Provide Actionable Messages
Error messages should help developers understand what went wrong and how to fix it.
๐ Include Documentation Links
Link to relevant documentation for common errors to help developers resolve issues quickly.
๐ Include Request IDs
Return a unique request ID for tracking and debugging purposes.
Authentication & Security
๐ API Keys
Simple authentication for server-to-server communication.
X-API-Key: your-api-key-here
Best for: Public APIs, simple integrations
๐ซ JWT (Bearer Tokens)
Self-contained tokens for stateless authentication.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Best for: User authentication, mobile apps
๐ OAuth 2.0
Industry standard for delegated authorization.
Best for: Third-party integrations, user consent flows
Security Best Practices
๐ Always Use HTTPS
Never transmit sensitive data over plain HTTP. Enforce HTTPS everywhere.
โฑ๏ธ Rate Limiting
Protect your API from abuse with rate limits. Return 429 when exceeded.
โ Input Validation
Validate all input data. Never trust client input.
๐ก๏ธ CORS Configuration
Configure CORS headers appropriately for browser clients.
Response Design
๐ฆ Consistent Response Envelope
Wrap responses in a consistent structure.
// Success response
{
"data": {...},
"meta": {
"request_id": "abc123",
"timestamp": "2023-06-20T14:30:00Z"
}
}
// Collection response
{
"data": [...],
"pagination": {...},
"meta": {...}
}
// Error response
{
"error": {...}
}
๐ Use ISO 8601 for Dates
Always use ISO 8601 format with timezone for dates.
"2023-06-20T14:30:00Z""2023-06-20T14:30:00+02:00"
"June 20, 2023""20/06/2023"1687271400 (Unix timestamp without context)
๐ Use camelCase or snake_case Consistently
Pick one naming convention and stick with it throughout your API.
{"user_id": 1, "created_at": "..."}
{"userId": 1, "createdAt": "..."}
{"userId": 1, "created_at": "..."}
Documentation
Good documentation is essential for API adoption and developer experience.
๐ OpenAPI/Swagger
Use OpenAPI specification to document your API. It enables automated documentation, client generation, and testing.
๐ก Include Examples
Provide complete request and response examples for every endpoint.
๐งช Interactive Testing
Allow developers to test API calls directly from the documentation.
๐ Keep It Updated
Documentation should be versioned and updated alongside your API code.