Published Mar 12, 2026

Designing REST APIs that developers will actually love

A well-designed API is a joy to work with. A poorly-designed one causes endless frustration, bugs, and support tickets. After building and consuming dozens of APIs, here are the principles that matter most.

Use consistent, predictable naming

Resources should be nouns, not verbs. Use plural forms and nest related resources logically:

GET    /users           — list all users
GET    /users/123       — get a specific user
POST   /users           — create a user
PATCH  /users/123       — update a user
DELETE /users/123       — delete a user
GET    /users/123/posts — list posts by a user

Avoid mixing conventions. If you use snake_case for JSON fields, use it everywhere. If you use camelCase, be consistent about that. Pick one and stick with it.

Return proper HTTP status codes

Do not return 200 OK for everything. Status codes communicate intent:

  • 200 — success
  • 201 — resource created
  • 204 — success with no content (good for DELETE)
  • 400 — client sent a bad request
  • 401 — not authenticated
  • 403 — authenticated but not authorized
  • 404 — resource not found
  • 409 — conflict (e.g., duplicate resource)
  • 422 — validation error
  • 429 — rate limited
  • 500 — server error

Design useful error responses

When something goes wrong, give the developer enough information to fix it:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address"
      },
      {
        "field": "age",
        "message": "Must be a positive integer"
      }
    ]
  }
}

A consistent error format across your entire API saves developers hours of debugging.

Pagination

Never return unbounded lists. Use cursor-based pagination for large datasets:

{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6MTAwfQ==",
    "has_more": true
  }
}

Cursor-based pagination is more reliable than offset-based pagination when data changes frequently — items will not be skipped or duplicated as new records are inserted.

Versioning

Version your API from day one. The two most common approaches:

  1. URL path versioning: /v1/users — simple and explicit
  2. Header versioning: Accept: application/vnd.api+json;version=1 — cleaner URLs but harder to test in a browser

URL path versioning is simpler and works well for most APIs.

Rate limiting

Always include rate limit information in response headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1616029200

This lets clients implement backoff strategies without guessing.

A great API does not surprise developers. It follows conventions, communicates clearly through status codes and error messages, and handles edge cases gracefully.