Principles of Good API Design

A well-designed API feels intuitive to its consumers. Developers should be able to predict how endpoints behave based on conventions, rather than needing to consult documentation for every request. Consistency, clear naming, and proper use of HTTP semantics are the foundation of good API design.

The best APIs are also stable. They evolve without breaking existing clients, and they communicate changes clearly through versioning and deprecation policies.

Resource Naming Conventions

URLs should represent resources as nouns, not actions. Use plural nouns for collections and include identifiers for specific resources.

GET    /api/posts          # List all posts
GET    /api/posts/42       # Get a specific post
POST   /api/posts          # Create a new post
PUT    /api/posts/42       # Update a post
DELETE /api/posts/42       # Delete a post

Avoid verbs in your URLs. The HTTP method already communicates the action. Instead of /api/getPosts or /api/createPost, let the method do the work.

For nested resources, keep the hierarchy shallow. /api/posts/42/comments is clear, but deeply nested paths like /api/users/5/posts/42/comments/7/replies become unwieldy. Consider promoting deeply nested resources to top-level endpoints with query parameters for filtering.

HTTP Status Codes

Use status codes correctly to communicate the result of each request:

  • 200 OK for successful GET, PUT, and PATCH requests
  • 201 Created for successful POST requests that create a new resource
  • 204 No Content for successful DELETE requests
  • 400 Bad Request when the client sends invalid data
  • 401 Unauthorized when authentication is missing or invalid
  • 403 Forbidden when the user lacks permission for the requested action
  • 404 Not Found when the requested resource does not exist
  • 422 Unprocessable Entity when the request is well-formed but fails validation
  • 500 Internal Server Error for unexpected server-side failures

Returning the correct status code helps clients handle responses programmatically without parsing error messages.

Pagination and Filtering

Any endpoint that returns a collection should support pagination. Cursor-based pagination is generally preferred over offset-based pagination for large datasets because it handles insertions and deletions gracefully.

GET /api/posts?cursor=abc123&limit=20
GET /api/posts?status=published&sort=-created_at

Include pagination metadata in the response so clients know whether more results are available. A common pattern is to include a nextCursor field or Link headers pointing to the next page.

Error Responses

Return structured error responses with enough detail for the client to understand and fix the problem. A consistent error format reduces the burden on API consumers.

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The request body contains invalid fields.",
    "details": [
      { "field": "email", "message": "Must be a valid email address." },
      { "field": "name", "message": "Required field is missing." }
    ]
  }
}

Always use the same error structure across all endpoints. Inconsistent error formats are one of the most common complaints from API consumers.

Versioning

Version your API from day one, even if you only have one version. The most common approach is to include the version in the URL path, such as /api/v1/posts. This makes the version explicit and easy to route at the infrastructure level. Plan for how you will deprecate old versions and communicate breaking changes to your consumers.