REST API Design Best Practices for 2026

A comprehensive guide to building REST APIs that developers love to use. Learn the patterns that separate good APIs from great ones.

REST API Design Best Practices

Why API Design Matters

A well-designed API is a joy to work with. It feels intuitive, guides developers toward correct usage, and rarely produces surprising errors. A poorly designed API creates friction at every integration point — documentation gets longer, bugs multiply, and developers grow frustrated.

Your API is a product. It represents your system to the outside world, and like any product, its quality determines adoption. This guide covers the principles that make APIs excellent: clarity, consistency, predictability, and developer experience.

URL Design: The Foundation of Your API

URLs are the primary interface developers interact with. They should be intuitive, consistent, and self-documenting. Think of URLs as sentences describing resources — they should read naturally.

Use Nouns, Not Verbs

HTTP methods already express actions. Your URLs should identify resources, not operations:

// ❌ BAD — Verbs in URLs
GET /getUsers
GET /getUserById/123
POST /createUser
PUT /updateUser/123
DELETE /deleteUser/123

// ✅ GOOD — Nouns and HTTP methods
GET /users
GET /users/123
POST /users
PUT /users/123
DELETE /users/123

Use Plural Nouns

Consistency matters. Always use plural nouns for collections:

GET /users          // List users
GET /users/123       // Get specific user
GET /users/123/orders // Get user's orders
GET /orders/456/items // Get order's items

Nest Resources Logically

URL structure should reflect relationships:

/companies/123/employees     // Employees of company 123
/companies/123/employees/456 // Specific employee
/repositories/789/issues     // Issues in repo 789

But don't nest too deeply. After 2-3 levels, URLs become unwieldy. If you find yourself writing URLs like /a/b/c/d/e, reconsider your resource structure.

Avoid File Extensions

Content negotiation handles different response formats:

// ❌ Avoid
GET /users.json
GET /users.xml

// ✅ Use headers
GET /users
Accept: application/json

// Or query parameters (if you prefer)
GET /users?format=json

HTTP Methods: Use Them Correctly

Each HTTP method has specific semantics. Using them correctly makes your API predictable:

GET — Retrieve Resources

GET requests should be safe (no side effects) and idempotent (multiple calls produce same result). Never modify data in a GET handler.

GET /users              // List users
GET /users?page=2       // Paginated list
GET /users/123          // Get specific user
GET /users/123/orders   // Nested resource

POST — Create Resources

POST creates new resources. The server generates the identifier:

POST /users
Body: {"name": "John", "email": "john@example.com"}

Response: 201 Created
Location: /users/456
Body: {"id": 456, "name": "John", "email": "john@example.com"}

PUT — Full Replacement

PUT replaces an entire resource. Include all fields — missing fields get nullified:

PUT /users/123
Body: {"name": "John Updated", "email": "john@new.com"}

Response: 200 OK
Body: {"id": 123, "name": "John Updated", "email": "john@new.com"}

PATCH — Partial Update

PATCH updates only specified fields, preserving others:

PATCH /users/123
Body: {"name": "John Patched"}

Response: 200 OK
Body: {"id": 123, "name": "John Patched", "email": "john@example.com"}

DELETE — Remove Resources

DELETE removes resources and should be idempotent (deleting twice returns same result):

DELETE /users/123

Response: 204 No Content

HTTP Status Codes: Communicate Clearly

Status codes tell developers what happened. Use them correctly:

Success Codes

  • 200 OK: Successful GET, PUT, PATCH
  • 201 Created: Successful POST that created a resource
  • 202 Accepted: Request accepted but processing is async
  • 204 No Content: Successful DELETE or action with no response body

Client Error Codes

  • 400 Bad Request: Invalid request syntax or missing required fields
  • 401 Unauthorized: Authentication required or failed
  • 403 Forbidden: Authenticated but not authorized
  • 404 Not Found: Resource doesn't exist
  • 409 Conflict: Request conflicts with current state (duplicate, version conflict)
  • 422 Unprocessable Entity: Valid syntax but invalid content (validation errors)
  • 429 Too Many Requests: Rate limit exceeded

Server Error Codes

  • 500 Internal Server Error: Unexpected server error
  • 502 Bad Gateway: Upstream server error
  • 503 Service Unavailable: Temporary overload or maintenance
  • 504 Gateway Timeout: Upstream request timed out

Error Response Format

Consistent error responses make debugging easier. Use a standard format:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request data",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address"
      },
      {
        "field": "name",
        "message": "Name is required"
      }
    ]
  }
}

Always include enough information for developers to understand and fix the issue. But never expose sensitive implementation details.

Pagination and Filtering

Large collections require pagination. Be consistent:

GET /users?page=2&per_page=20

Response:
{
  "data": [...],
  "pagination": {
    "page": 2,
    "per_page": 20,
    "total": 156,
    "total_pages": 8
  }
}

Support filtering, sorting, and field selection to give clients flexibility:

GET /users?status=active&sort=name:asc&fields=id,name,email

API Versioning

APIs evolve. Versioning prevents breaking changes from affecting existing integrations:

URL Path Versioning (Most Common)

GET /v1/users
GET /v2/users

Header Versioning

GET /users
Accept: application/vnd.myapi.v2+json

We recommend URL versioning for its simplicity and visibility in logs and browser testing.

Authentication Patterns

Choose authentication based on your use case:

API Keys (For Server-to-Server)

X-API-Key: your-api-key-here

Bearer Tokens (OAuth/JWT)

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Rate Limiting Headers

Always communicate rate limits to clients:

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

Documentation Standards

Great APIs have great documentation. Include:

  • Authentication explanation with examples
  • All endpoints with request/response examples
  • Error codes and what they mean
  • Rate limits and best practices
  • SDK examples in multiple languages

Summary Checklist

  • URLs use nouns, plural forms, logical nesting
  • HTTP methods used correctly
  • Status codes accurately reflect outcomes
  • Errors follow consistent format with actionable details
  • Pagination handles large collections
  • Versioning prevents breaking changes
  • Authentication is clear and secure
  • Documentation is comprehensive and examples are working

Great API design isn't about following rules blindly — it's about reducing friction for developers. Every decision should ask: "Would this be obvious to someone using this API for the first time?"

Frequently Asked Questions

What's the difference between PUT and PATCH?

PUT replaces the entire resource — send all fields, missing ones are removed. PATCH applies a partial update — send only the fields you want to change. PUT is idempotent (same result on repeat), PATCH may or may not be. Use PUT for full updates, PATCH for incremental ones, and document the merge strategy clearly.

Should I use singular or plural nouns in URLs?

Plural. /users/123 not /user/123. Reason: /users describes the collection, and /users/123 describes a specific item in that collection — consistent for both list and detail. Singular breaks the symmetry and forces /user/123 vs /users in different endpoints.

How do I version a REST API?

Three options: (1) URL versioning: /v1/users — simple, cacheable, easy to test. (2) Header versioning: Accept: application/vnd.api+json;version=2 — clean URLs, harder to test in browser. (3) Query param: /users?api-version=2 — worst of both worlds. For most APIs, URL versioning wins on simplicity. Maintain at least 2 versions simultaneously for 6-12 months after a new release.

← Back to Blog
Copied!