HTTP API Best Practices

Build robust, maintainable APIs

12 min readDeveloper Guide

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.

View All HTTP Status Codes

Return Meaningful Error Messages

Good error messages help developers debug quickly. Include what went wrong, where, and how to fix it.

✓ Good Error Response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email format",
    "details": {
      "field": "email",
      "value": "invalid-email",
      "constraint": "must be valid email"
    },
    "requestId": "abc-123"
  }
}
✗ Bad Error Response
{
  "error": "Something went wrong"
}

// Or worse: 500 with no body
// Or: 200 OK with error inside

Always 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.
Read JWT Guide

Implement Rate Limiting

Rate limiting protects your API from abuse and ensures fair resource distribution. Communicate limits clearly through headers and responses.

Standard Rate Limit Headers:
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

URL Path
/api/v1/users

Most explicit, easy to route

Header
Accept: application/vnd.api+json;version=1

Cleaner URLs, more HTTP-ish

Query Param
/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.

Related Guides