JSON API Standard

JSON:API is a specification for building REST APIs that use JSON. It defines a consistent structure for requests and responses so that client and server teams can communicate clearly without arguing about format decisions. When APIs follow JSON:API, any client that understands the standard can work with any compatible server.

Why JSON:API Exists

Every developer building a REST API makes dozens of small decisions. How should a list of items be wrapped? How should errors be formatted? How should related data be included? Without a standard, every API looks different. Teams spend time negotiating these decisions instead of building features.

Think of JSON:API like a standardized shipping container. Before shipping containers existed, every ship and port had to deal with a different box size and shape. After standardization, any container fits any ship and any crane. JSON:API does the same for APIs.

JSON:API Content Type

APIs that follow JSON:API must use this Content-Type header:

Content-Type: application/vnd.api+json

Basic JSON:API Response Structure

A JSON:API response has a predictable structure with specific keys.

Single Resource Response

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "Getting Started with JSON",
      "body": "JSON is a lightweight data format...",
      "publishedAt": "2026-01-15",
      "readTime": 5
    },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "42" }
      },
      "tags": {
        "data": [
          { "type": "tags", "id": "10" },
          { "type": "tags", "id": "11" }
        ]
      }
    },
    "links": {
      "self": "https://api.example.com/articles/1"
    }
  }
}

Collection Response (Multiple Resources)

{
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": {
        "title": "Getting Started with JSON",
        "publishedAt": "2026-01-15"
      }
    },
    {
      "type": "articles",
      "id": "2",
      "attributes": {
        "title": "JSON Schema Explained",
        "publishedAt": "2026-02-20"
      }
    }
  ],
  "meta": {
    "total": 47,
    "page": 1,
    "perPage": 2
  },
  "links": {
    "self": "https://api.example.com/articles?page=1",
    "next": "https://api.example.com/articles?page=2",
    "last": "https://api.example.com/articles?page=24"
  }
}

Diagram: JSON:API Document Structure

JSON:API Document
├── data           ← Main resource or array of resources
│   ├── type       ← Resource type name (e.g., "articles", "users")
│   ├── id         ← Unique identifier (always a string)
│   ├── attributes ← The actual data fields (not id, not relationships)
│   └── relationships ← Links to related resources
├── included       ← Related resource data (sideloaded, optional)
├── meta           ← Non-data info: pagination, totals, etc.
├── links          ← URLs: self, next, prev, first, last
└── errors         ← Error objects (replaces "data" when errors occur)

Key Rules of JSON:API

1. type and id are Always Present

Every resource object must have a type (what kind of resource it is) and an id (the unique identifier). The id is always a string, even if it is a number in your database.

{
  "type": "users",
  "id": "301"      // "301" as a string, not 301 as a number
}

2. attributes Holds the Data Fields

All data fields go inside attributes. The id and type stay at the same level as attributes, not inside it.

"attributes": {
  "name": "Priya Mehta",
  "email": "priya@example.com",
  "joinedAt": "2025-06-01"
}

3. relationships Connects Resources

Instead of embedding a full author object inside an article, JSON:API uses a relationships section with just the type and id of the related resource. The full related data can optionally appear in the included section.

"relationships": {
  "author": {
    "data": { "type": "people", "id": "42" }
  }
}

The included Section (Sideloading)

Sideloading means including related resource data in the same response to avoid a second network request. The included array holds full resource objects for all referenced relationships.

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": { "title": "Getting Started with JSON" },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "42" }
      }
    }
  },
  "included": [
    {
      "type": "people",
      "id": "42",
      "attributes": {
        "name": "Arjun Sharma",
        "bio": "Senior developer and technical writer"
      }
    }
  ]
}

The article references author with id "42." The full author data appears in included. The client assembles the complete picture from these two sections.

JSON:API Error Response

When something goes wrong, JSON:API uses a standardized errors array instead of data.

{
  "errors": [
    {
      "id": "ERR-001",
      "status": "422",
      "code": "VALIDATION_FAILED",
      "title": "Unprocessable Entity",
      "detail": "The email field must contain a valid email address.",
      "source": {
        "pointer": "/data/attributes/email"
      }
    }
  ]
}

Error Object Fields

Field    Meaning
-----    -------
id       Unique ID for this specific error occurrence
status   HTTP status code as a string
code     Application-specific error code
title    Short, human-readable summary
detail   Longer explanation specific to this occurrence
source   Pointer to the field that caused the error

JSON:API Request: Creating a Resource

POST /articles HTTP/1.1
Content-Type: application/vnd.api+json

{
  "data": {
    "type": "articles",
    "attributes": {
      "title": "JSON Patch Explained",
      "body": "JSON Patch allows partial updates...",
      "publishedAt": "2026-09-01"
    },
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "42" }
      }
    }
  }
}

JSON:API Request: Updating a Resource

PATCH /articles/1 HTTP/1.1
Content-Type: application/vnd.api+json

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON Patch Explained (Updated)"
    }
  }
}

Filtering, Sorting, and Pagination in JSON:API

JSON:API recommends query parameter conventions for common operations:

GET /articles?filter[status]=published        Filter by status
GET /articles?sort=-publishedAt               Sort by date (- means descending)
GET /articles?sort=title                      Sort by title (ascending)
GET /articles?page[number]=2&page[size]=10    Get page 2 with 10 items
GET /articles?include=author,tags             Sideload author and tags
GET /articles?fields[articles]=title,body     Sparse fieldsets (only get some fields)

JSON:API vs Plain REST JSON

Feature                     Plain REST          JSON:API
-------                     ----------          --------
Response structure          Varies by team      Standardized
Error format                Varies              Standardized errors array
Relationships               Embedded (nested)   Normalized with included
Pagination info             Custom              meta + links
Content-Type                application/json    application/vnd.api+json
Client tooling support      Generic             JSON:API-aware libraries
Learning curve              Low                 Moderate

JSON:API Libraries

  • JavaScript: jsonapi-serializer, json-api-client
  • Python: marshmallow-jsonapi, flask-rest-jsonapi
  • Ruby: JSONAPI::Resources (Rails)
  • PHP: laravel-json-api
  • Java: katharsis

When to Use JSON:API

JSON:API is a great choice when your team is building a large API that multiple clients will consume (web, mobile, third parties), when you want to standardize response formats across many endpoints, or when you want to support features like sparse fieldsets and relationship sideloading out of the box.

For small, single-team projects or simple CRUD APIs, plain JSON with consistent conventions is often sufficient. JSON:API adds structure but also adds weight.

Summary

JSON:API is a specification that defines a standard structure for REST API requests and responses. It uses data, type, id, attributes, relationships, included, meta, links, and errors as the building blocks of every response. It standardizes filtering, sorting, pagination, and error reporting through query parameter conventions. Use it when consistency across many API endpoints and clients is more important than simplicity.

Leave a Comment

Your email address will not be published. Required fields are marked *