Skip to content
api-design logo

API Design Principles

api-design

REST API design best practices for consistent, intuitive APIs

SKILL.md

Full skill instructions

API Design Principles

Design APIs that are intuitive, consistent, and a joy to use.

URL Structure

Resources as Nouns

# GOOD - nouns
GET /​users
GET /​users/​123
GET /​users/​123/​orders

# BAD - verbs
GET /​getUsers
GET /​fetchUserById/​123
POST /​createNewUser

Plural Resource Names

# GOOD - plural
GET /​users
GET /​orders
GET /​products

# BAD - singular
GET /​user
GET /​order

Hierarchical Relationships

# Parent-child relationships
GET /​users/​123/​orders          # Orders for user 123
GET /​orders/​456/​items          # Items in order 456

# Avoid deep nesting (max 2 levels)
# BAD
GET /​users/​123/​orders/​456/​items/​789/​reviews

# GOOD - flatten when needed
GET /​order-items/​789/​reviews

HTTP Methods

MethodUsageIdempotentSafe
GETRead resourceYesYes
POSTCreate resourceNoNo
PUTReplace resourceYesNo
PATCHPartial updateYesNo
DELETERemove resourceYesNo
GET    /​users          # List users
POST   /​users          # Create user
GET    /​users/​123      # Get user 123
PUT    /​users/​123      # Replace user 123
PATCH  /​users/​123      # Update user 123
DELETE /​users/​123      # Delete user 123

Request/​Response Format

Consistent JSON Structure

// Success response
{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "[email protected]"
  }
}

// Collection response
{
  "data": [
    { "id": "123", "name": "John" },
    { "id": "456", "name": "Jane" }
  ],
  "meta": {
    "total": 100,
    "page": 1,
    "perPage": 20
  }
}

// Error response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email format",
    "details": [
      { "field": "email", "message": "Must be a valid email" }
    ]
  }
}

Use camelCase

// GOOD
{
  "firstName": "John",
  "lastName": "Doe",
  "emailAddress": "[email protected]"
}

// BAD - inconsistent
{
  "first_name": "John",
  "LastName": "Doe",
  "email-address": "[email protected]"
}

Status Codes

Success (2xx)

200 OK           - GET, PUT, PATCH success
201 Created      - POST success (include Location header)
204 No Content   - DELETE success

Client Errors (4xx)

400 Bad Request     - Invalid syntax, validation error
401 Unauthorized    - No/​invalid authentication
403 Forbidden       - Authenticated but not allowed
404 Not Found       - Resource doesn't exist
409 Conflict        - State conflict (duplicate, version)
422 Unprocessable   - Valid syntax but semantic error
429 Too Many Requests - Rate limited

Server Errors (5xx)

500 Internal Server Error - Unexpected error
502 Bad Gateway          - Upstream service failed
503 Service Unavailable  - Temporarily unavailable
504 Gateway Timeout      - Upstream timeout

Pagination

Offset-based (simple)

GET /​users?page=2&perPage=20

Response:
{
  "data": [...],
  "meta": {
    "total": 100,
    "page": 2,
    "perPage": 20,
    "totalPages": 5
  },
  "links": {
    "first": "/​users?page=1&perPage=20",
    "prev": "/​users?page=1&perPage=20",
    "next": "/​users?page=3&perPage=20",
    "last": "/​users?page=5&perPage=20"
  }
}

Cursor-based (scalable)

GET /​users?cursor=eyJpZCI6MTIzfQ&limit=20

Response:
{
  "data": [...],
  "meta": {
    "hasMore": true,
    "nextCursor": "eyJpZCI6MTQzfQ"
  }
}

Filtering & Sorting

Filtering

GET /​users?status=active
GET /​users?role=admin&status=active
GET /​users?createdAt[gte]=2024-01-01
GET /​users?name[contains]=john

Sorting

GET /​users?sort=name           # Ascending
GET /​users?sort=-createdAt     # Descending (prefix -)
GET /​users?sort=lastName,firstName

Field Selection

GET /​users?fields=id,name,email
GET /​users/​123?fields=id,name,email

Versioning

URL Path (recommended)

GET /​v1/​users
GET /​v2/​users

Header

GET /​users
Accept: application/​vnd.api+json;version=2

Authentication

Bearer Token

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API Key

X-API-Key: sk_live_abc123
# Or in query (less secure)
GET /​users?api_key=sk_live_abc123

Rate Limiting

Include headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
Retry-After: 60

Documentation

Every endpoint needs:

  • Description of what it does
  • Request parameters with types
  • Request body schema
  • Response schema for each status code
  • Example request/​response
  • Error cases
# OpenAPI example
/​users:
  post:
    summary: Create a new user
    requestBody:
      required: true
      content:
        application/​json:
          schema:
            $ref: '#/​components/​schemas/​CreateUser'
    responses:
      '201':
        description: User created
        content:
          application/​json:
            schema:
              $ref: '#/​components/​schemas/​User'
      '400':
        description: Validation error

Common Mistakes

MistakeProblemFix
Verbs in URLs/​getUserUse /​users/:id
Inconsistent namingMix of casesPick one (camelCase)
Wrong status codes200 for errorsUse 4xx/​5xx appropriately
No paginationMemory issuesAlways paginate collections
Breaking changesClients breakVersion your API
No rate limitingAbuseImplement limits
Exposing internalsSecurity riskTransform responses

Checklist

  • URLs use nouns, not verbs
  • HTTP methods used correctly
  • Consistent response format
  • Appropriate status codes
  • Pagination for collections
  • Filtering and sorting support
  • Rate limiting implemented
  • Authentication documented
  • Errors are descriptive
  • API is versioned