Back to Blog
·8 min read

JSON Best Practices for APIs

A guide to designing clean, consistent JSON API responses that developers will love.

Well-designed JSON APIs are a joy to consume. Inconsistent naming, unpredictable structures, and undocumented conventions are among the top frustrations developers face when integrating third-party APIs. This guide covers the conventions that make the biggest practical difference.

1. Pick a naming convention and stick to it

The two dominant conventions are camelCase (common in JavaScript ecosystems) and snake_case (common in Python and Ruby). Neither is objectively better — consistency is what matters. Mixing both in the same API is the worst outcome.

  • ·camelCase: { "userId": 1, "firstName": "Alice" }
  • ·snake_case: { "user_id": 1, "first_name": "Alice" }
  • ·Avoid PascalCase, kebab-case, or SCREAMING_SNAKE_CASE for field names

2. Never return a bare array at the top level

Always wrap your response in an object, even when returning a list. A bare array can never be extended without a breaking change, and it complicates error handling.

// Bad
[{ "id": 1 }, { "id": 2 }]

// Good
{
  "data": [{ "id": 1 }, { "id": 2 }],
  "total": 2
}

3. Use ISO 8601 for all dates and times

Always represent dates as ISO 8601 strings, not Unix timestamps or locale-formatted strings. ISO 8601 is unambiguous, human-readable, and natively supported by JavaScript's Date constructor.

// Bad
{ "createdAt": 1714521600, "date": "05/01/2025" }

// Good
{ "createdAt": "2025-05-01T00:00:00Z" }

4. Standardize error responses

RFC 7807 (Problem Details) provides a standardized structure for HTTP error responses. Even if you don't follow it exactly, picking a consistent error shape and using it everywhere is essential.

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "issue": "Invalid email format" }
    ]
  }
}

5. Handle pagination consistently

Offset-based pagination is simple but breaks when items are inserted or deleted. Cursor-based pagination is more robust for real-time data. Whatever you choose, document it and apply it to every list endpoint.

// Cursor-based (recommended for large/live datasets)
{
  "data": [...],
  "pagination": {
    "nextCursor": "eyJpZCI6MTAwfQ==",
    "hasMore": true
  }
}

6. Be deliberate about null vs. absent fields

Decide upfront: does a missing field mean the same as null? Many APIs treat them differently — null means "known to be empty", while an absent field means "not applicable in this context". Document your convention and apply it uniformly.

Tip: Use null for fields that exist but have no value. Omit fields entirely when they are not applicable to the current resource type.

Ad