JSON Formatter Hub

Free online JSON formatter, validator, and developer tools.

REST API JSON Best Practices: Design Patterns Every Developer Should Know

A well-designed JSON API is a joy to consume. A poorly designed one creates bugs, confusion, and endless Slack threads asking β€œwhat does this field actually mean?” The decisions you make about naming, error formats, pagination, and date handling ripple through every client that ever calls your API. This guide covers the patterns that separate maintainable APIs from painful ones, with concrete JSON examples for each.

1. Field Naming Conventions

Pick snake_case or camelCase and be consistent across every endpoint.

snake_case (most common in Python/Ruby APIs)

{
  "user_id": 42,
  "first_name": "Alice",
  "last_login_at": "2025-05-01T10:30:00Z",
  "is_active": true
}

camelCase (most common in JavaScript/Node APIs)

{
  "userId": 42,
  "firstName": "Alice",
  "lastLoginAt": "2025-05-01T10:30:00Z",
  "isActive": true
}

Naming rules that hold regardless of case style

2. Response Structure

Wrap your response in a consistent envelope.

Single resource response

{
  "data": {
    "id": "usr_42",
    "name": "Alice",
    "email": "alice@example.com",
    "created_at": "2024-01-15T08:00:00Z"
  }
}

Collection response

{
  "data": [
    { "id": "usr_42", "name": "Alice" },
    { "id": "usr_43", "name": "Bob" }
  ],
  "meta": {
    "total": 248,
    "page": 1,
    "per_page": 20
  }
}

3. Error Response Format

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request body validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address",
        "value": "not-an-email"
      }
    ],
    "request_id": "req_8f3k2j9s"
  }
}

HTTP status code + error code pairing

HTTP StatusError CodeWhen to Use
400VALIDATION_FAILEDRequest body or query params failed validation
401UNAUTHORIZEDNo valid authentication credentials
403FORBIDDENAuthenticated but not permitted
404NOT_FOUNDResource does not exist
409CONFLICTOptimistic lock failure, duplicate key
429RATE_LIMITEDToo many requests
500INTERNAL_ERRORUnexpected server error

4. Pagination

Offset pagination

// Request: GET /users?page=3&per_page=20
{
  "data": [ ... ],
  "meta": { "total": 248, "page": 3, "per_page": 20, "total_pages": 13 },
  "links": {
    "first": "/users?page=1&per_page=20",
    "prev": "/users?page=2&per_page=20",
    "next": "/users?page=4&per_page=20",
    "last": "/users?page=13&per_page=20"
  }
}

Cursor pagination

// Request: GET /events?limit=20&after=cursor_abc123
{
  "data": [ ... ],
  "meta": { "has_next_page": true, "has_prev_page": true },
  "links": {
    "next": "/events?limit=20&after=cursor_xyz789",
    "prev": "/events?limit=20&before=cursor_abc123"
  }
}

5. Dates and Times

Use ISO 8601 with UTC timezone for all timestamps.

// GOOD
{
  "created_at": "2025-05-01T10:30:00Z",
  "scheduled_at": "2025-06-15T14:00:00+05:30",
  "birth_date": "1990-07-22"
}

// BAD
{
  "created_at": "May 1, 2025",
  "created_at": 1746094200,
  "created_at": "01/05/2025 10:30"
}

6. Null vs Missing Fields

// Field present and null β€” value explicitly set to null
{ "name": "Alice", "middle_name": null }

// Field absent β€” doesn't apply
{ "name": "Alice" }

7. Versioning

URL versioning (most common)

GET /v1/users
GET /v2/users

What counts as a breaking change

BreakingNon-breaking (safe to ship)
Removing a fieldAdding a new optional field
Renaming a fieldAdding a new endpoint
Changing a field's typeMaking a required field optional
Changing error codesAdding new valid enum values

8. Anti-Patterns to Avoid

Anti-pattern: Using HTTP 200 for errors

// BAD
HTTP 200 OK
{ "success": false, "error": "User not found" }

// GOOD
HTTP 404 Not Found
{ "error": { "code": "NOT_FOUND", "message": "User with id usr_999 does not exist" } }

Anti-pattern: Encoding data in field names

// BAD
{ "price_usd": 10.99, "price_eur": 9.50, "price_gbp": 8.75 }

// GOOD
{ "prices": [
    { "currency": "USD", "amount": 10.99 },
    { "currency": "EUR", "amount": 9.50 }
] }

Summary: The API Design Checklist

ConcernRecommendation
Field namingsnake_case or camelCase β€” pick one and enforce it everywhere
Response shapeWrap in data key; never bare arrays at top level
Error formatConsistent envelope: code, message, details, request_id
HTTP status codesUse them correctly β€” 200 means success
PaginationCursor for feeds; offset for admin; always include links
DatesISO 8601 with UTC (Z suffix) always
Null handlingnull = known absence; omit = not applicable
IDsStrings for large IDs; consistent prefix per resource type
VersioningURL versioning for simplicity; plan for breaking changes

Further Reading