All articles
3 min read

Understanding Standard JSON API Response Formats

Consistent JSON API response formats make integration easier. Learn the JSend convention, HTTP status code mapping, pagination patterns, and structured error objects used in production APIs.

A consistent API response format means clients can handle success, errors, and pagination with the same code — no matter which endpoint they call. This guide covers the conventions used in real-world REST APIs.

The JSend Convention

JSend is a simple, widely adopted convention for JSON API responses. It defines three status values:

Success

{
  "status": "success",
  "data": {
    "user": {
      "id": 42,
      "name": "Alice",
      "email": "alice@example.com"
    }
  }
}

Failure (Client Error)

Used for validation errors and invalid requests — the client did something wrong:

{
  "status": "fail",
  "data": {
    "email": "Email is required",
    "password": "Password must be at least 8 characters"
  }
}

Error (Server Error)

Used for server-side exceptions — something went wrong on the server:

{
  "status": "error",
  "message": "Database connection failed",
  "code": 500,
  "data": null
}

HTTP Status Codes

The response format should always be paired with the correct HTTP status code. Returning 200 OK with "status": "error" in the body is a common mistake that breaks standard HTTP clients.

| Range | Meaning | Common codes | |---|---|---| | 2xx | Success | 200 OK, 201 Created, 204 No Content | | 3xx | Redirect | 301 Moved Permanently, 304 Not Modified | | 4xx | Client error | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 422 Unprocessable Entity | | 5xx | Server error | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |

Practical mapping:

  • GET /users/42200 with user data, or 404 if not found
  • POST /users (validation fails) → 422 with validation errors
  • POST /users (success) → 201 with created user
  • DELETE /users/42204 (no body)
  • GET /admin without auth → 401
  • GET /admin with auth but no permission → 403

Structured Error Objects

For richer error responses (especially useful for validation), include a list of error objects:

{
  "status": "fail",
  "errors": [
    {
      "field": "email",
      "code": "INVALID_FORMAT",
      "message": "Must be a valid email address"
    },
    {
      "field": "age",
      "code": "OUT_OF_RANGE",
      "message": "Must be between 18 and 120"
    }
  ]
}

Error codes like INVALID_FORMAT are machine-readable strings that front-end code can map to localized messages, rather than parsing server-generated English strings.

Pagination

For list endpoints, include pagination metadata:

Offset-Based Pagination

{
  "status": "success",
  "data": {
    "items": [...],
    "pagination": {
      "total": 2700,
      "page": 3,
      "per_page": 10,
      "total_pages": 270,
      "next": "/api/items?page=4&per_page=10",
      "previous": "/api/items?page=2&per_page=10"
    }
  }
}

Cursor-Based Pagination

Better for large datasets and real-time feeds (no "page drift" when items are added):

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

The client passes ?cursor=eyJpZCI6MTAwfQ== to get the next page.

Envelope vs No-Envelope

Some modern APIs skip the wrapper envelope and return the resource directly:

{
  "id": 42,
  "name": "Alice"
}

This is cleaner for simple cases. The downside is there's no standard place to include pagination metadata or error details — you have to use HTTP headers (like X-Total-Count) or accept inconsistent response shapes.

Pick one approach and stick with it across your entire API.

Real-World Example: YouTube Data API v3

{
  "kind": "youtube#videoListResponse",
  "etag": "some_etag_value",
  "pageInfo": {
    "totalResults": 1,
    "resultsPerPage": 5
  },
  "items": [
    {
      "kind": "youtube#video",
      "id": "BGODurRfVv4",
      "snippet": {
        "title": "From service dog to SURFice dog",
        "description": "Inspiring story...",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/BGODurRfVv4/default.jpg"
          }
        }
      }
    }
  ]
}

YouTube uses its own envelope format with kind, etag, and pageInfo fields — standard for large public APIs that need cache control and versioning.

API Versioning in the Response

Including the API version in responses helps clients detect when they need to update:

{
  "apiVersion": "2.1",
  "status": "success",
  "data": { ... }
}

Or via HTTP header: X-API-Version: 2.1

Conclusion

The most important choices are: always use the correct HTTP status code, be consistent across all endpoints, and use machine-readable error codes rather than English strings. Start with JSend if you want a lightweight convention, or look at JSON:API for a more comprehensive standard that also covers relationships and sparse fieldsets.