HTTP API Best Practices
Build robust, maintainable APIs
Related Tools
Why API Best Practices Matter
Well-designed APIs are easier to use, debug, and maintain. They reduce integration headaches, improve reliability, and create better experiences for developers consuming your API. Poorly designed APIs lead to confusion, bugs, and technical debt.
This guide covers essential HTTP API practices: proper status codes, meaningful error messages, consistent structure, security, and versioning. Follow these patterns and your APIs will be professional-grade from day one.
Use HTTP Status Codes Correctly
HTTP status codes communicate what happened. Using them correctly helps clients understand outcomes without parsing response bodies.
2xx Success
- 200 OK: Standard success (GET, PUT, PATCH)
- 201 Created: Resource created (POST)
- 204 No Content: Success with no body (DELETE, some PUT/PATCH)
4xx Client Errors
- 400 Bad Request: Invalid request format
- 401 Unauthorized: Missing/invalid authentication
- 403 Forbidden: Authenticated but not allowed
- 404 Not Found: Resource doesn\'t exist
- 409 Conflict: State conflict (duplicate, version mismatch)
- 422 Unprocessable Entity: Valid format but semantic errors
- 429 Too Many Requests: Rate limit exceeded
5xx Server Errors
- 500 Internal Server Error: Something went wrong on server
- 502 Bad Gateway: Upstream service error
- 503 Service Unavailable: Server overloaded/maintenance
Tip: Never return 200 with error details. Use appropriate 4xx codes. Clients relying on status codes will miss errors hidden in 200 responses.
Return Meaningful Error Messages
Good error messages help developers debug quickly. Include what went wrong, where, and how to fix it.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": {
"field": "email",
"value": "invalid-email",
"constraint": "must be valid email"
},
"requestId": "abc-123"
}
}{
"error": "Something went wrong"
}
// Or worse: 500 with no body
// Or: 200 OK with error insideAlways include: error code (machine-readable), message (human-readable), field/location, and optionally a unique request ID for debugging server logs.
Consistent Request/Response Structure
Request Structure
- Use JSON for request bodies (not form-data for complex data)
- Accept both camelCase and snake_case if you serve multiple clients
- Validate and sanitize all inputs before processing
- Use query parameters for filtering, pagination, sorting
Response Structure
{
"data": { ... }, // The actual resource(s)
"meta": { // Pagination, totals, etc.
"page": 1,
"perPage": 20,
"totalPages": 5,
"totalCount": 100
},
"links": { // HATEOAS links (optional)
"self": "/api/users?page=1",
"next": "/api/users?page=2"
}
}Separate data from metadata. Wrap single resources in data object, return arrays directly or in data.items. Include pagination info for collections.
Authentication Best Practices
- Use Bearer tokens (JWT): Standard Authorization header format. Include token type, not just the token value.
- Never send tokens in URL: URLs get logged, cached, shared. Use headers for sensitive data.
- Set reasonable token expiration: Short-lived access tokens (15-60 min) with refresh tokens. Balance security and user experience.
- Scope tokens appropriately: Limit what each token can do. Read-only tokens shouldn\'t allow writes.
Implement Rate Limiting
Rate limiting protects your API from abuse and ensures fair resource distribution. Communicate limits clearly through headers and responses.
X-RateLimit-Limit: 100 # Max requests per window X-RateLimit-Remaining: 95 # Requests left in current window X-RateLimit-Reset: 1625097600 # Unix timestamp when limit resets
When limit exceeded, return 429 Too Many Requests with Retry-After header (seconds until reset). Consider different limits for different endpoints (search might be stricter than read).
API Versioning
/api/v1/users
Most explicit, easy to route
Accept: application/vnd.api+json;version=1
Cleaner URLs, more HTTP-ish
/api/users?version=1
Simple but less standard
URL path versioning is most common. Keep old versions working during transition. Deprecate gracefully: announce timeline, add sunset headers, give developers time to migrate.
Performance Optimization
- Support pagination for collections (don\'t return 10,000 items at once)
- Allow field selection (?fields=id,name) to reduce payload size
- Compress responses with gzip (APIs usually have JSON text payloads)
- Cache responses with proper Cache-Control headers
- Use async processing for slow operations (return 202 Accepted with status endpoint)
Common API Mistakes
Returning 200 for Errors
Breaks HTTP semantics. Clients expect 200 = success.
No Pagination
Returning all items causes slow responses and high memory usage.
Breaking Changes Without Versioning
Renaming fields, changing types, or removing endpoints breaks existing clients.
Exposing Internal Errors
Stack traces, SQL errors, or internal paths give attackers useful information.