JSON Schema
API Development

What is JSON Schema and How to Validate Your API Responses

APIs break silently. A field changes from a string to a number, a required key goes missing, an enum gains a new value — and your app crashes in production hours later. JSON Schema is how you catch those breaks at the boundary, before the bad data propagates.

April 29, 2026
12 min read
PDF Mavericks Team

What is JSON Schema?

JSON Schema is a vocabulary for describing and validating JSON data structures. A schema is itself a JSON document that specifies what another JSON document should look like: its types, required properties, value constraints, and structure. The specification is maintained by the JSON Schema organization at json-schema.org.

The current stable release is draft-2020-12 (published December 2020). The most widely deployed version in production tooling is draft-07 (2018). Both are supported by major validators.

What JSON Schema can enforce:

Data types (string, number, integer, boolean, array, object, null)
Required vs optional fields
String patterns (regex), formats (email, date-time, URI)
Number ranges (minimum, maximum, multipleOf)
Array length, uniqueItems, item schemas
Enum values (only these exact values allowed)
Object property schemas and additionalProperties restrictions
Composition with allOf, anyOf, oneOf, not

The Example: Validating a User API Response

We'll build a schema for a GET /user/:id API response that returns:

{
  "user": {
    "id": 42,
    "username": "jane_doe",
    "email": "jane@example.com",
    "age": 28,
    "roles": ["editor", "viewer"],
    "createdAt": "2024-01-15T09:30:00Z",
    "active": true
  }
}

Writing the Schema (draft-07)

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://api.example.com/schemas/user-response.json",
  "title": "User API Response",
  "description": "Schema for the GET /user/:id endpoint",
  "type": "object",
  "required": ["user"],
  "properties": {
    "user": {
      "type": "object",
      "required": ["id", "username", "email", "roles", "createdAt", "active"],
      "properties": {
        "id": {
          "type": "integer",
          "description": "Unique user identifier",
          "minimum": 1
        },
        "username": {
          "type": "string",
          "minLength": 3,
          "maxLength": 50,
          "pattern": "^[a-z0-9_]+$"
        },
        "email": {
          "type": "string",
          "format": "email"
        },
        "age": {
          "type": "integer",
          "minimum": 13,
          "maximum": 120
        },
        "roles": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": ["admin", "editor", "viewer"]
          },
          "minItems": 1,
          "uniqueItems": true
        },
        "createdAt": {
          "type": "string",
          "format": "date-time"
        },
        "active": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    }
  }
}

This schema enforces: id is a positive integer, username is lowercase alphanumeric, email passes email format validation, roles is a unique array of allowed strings, createdAt is ISO 8601, and no undocumented properties are allowed (additionalProperties: false).

Key Schema Keywords Explained

type

Controls the JSON type. "string", "integer", "number", "boolean", "object", "array", "null". Use an array like ["string", "null"] to allow multiple types.

required

Array of property names that must be present. Properties not listed are optional by default.

enum

Restricts the value to a specific list. "enum": ["admin", "editor"] means no other string is valid.

format

Validates string format. Common formats: "email", "date-time", "uri", "uuid", "hostname". Format validation requires a validator that enables strict mode (ajv-formats).

pattern

Validates a string against a regex. Use for enforcing slug formats, phone number patterns, etc.

additionalProperties

Set to false to reject any properties not defined in "properties". Critical for catching API response bloat or typos.

minimum / maximum

Integer and number range constraints. exclusiveMinimum and exclusiveMaximum are also available.

allOf / anyOf / oneOf

Compose schemas. allOf: data must match all schemas. anyOf: at least one. oneOf: exactly one.

Validating with ajv (Node.js)

Ajv (Another JSON Validator) is the most widely used JavaScript/Node.js JSON Schema validator. Install with npm install ajv ajv-formats.

import Ajv from 'ajv'
import addFormats from 'ajv-formats'

const ajv = new Ajv()
addFormats(ajv) // Adds email, date-time, uri, etc.

const schema = {
  // ... paste the schema above
}

const validate = ajv.compile(schema)

// Validate an API response
async function fetchUser(id: number) {
  const res = await fetch(`/api/user/${id}`)
  const data = await res.json()

  if (!validate(data)) {
    console.error('API response validation failed:', validate.errors)
    throw new Error('Invalid API response shape')
  }

  return data // TypeScript: data is still 'any' here
  // Combine with Zod or manual casting for typed access
}

Validating with jsonschema (Python)

The jsonschema library (pip install jsonschema) supports drafts 3, 4, 6, 7, 2019-09, and 2020-12.

import json
import jsonschema
from jsonschema import validate, ValidationError

# Load schema from file
with open('user-response.schema.json') as f:
    schema = json.load(f)

# Validate API response
def validate_user_response(data: dict) -> bool:
    try:
        validate(instance=data, schema=schema)
        return True
    except ValidationError as e:
        print(f"Validation error: {e.message}")
        print(f"  Path: {' -> '.join(str(p) for p in e.path)}")
        return False

# Example invalid response — age is a string
invalid_response = {
    "user": {
        "id": 42,
        "username": "jane_doe",
        "email": "jane@example.com",
        "age": "28",  # Should be integer, not string
        "roles": ["editor"],
        "createdAt": "2024-01-15T09:30:00Z",
        "active": True
    }
}
validate_user_response(invalid_response)
# Output: Validation error: '28' is not of type 'integer'

Draft-07 vs Draft-2020-12

FeatureDraft-07Draft-2020-12
Tuple validationitems as arrayprefixItems (cleaner)
Unknown items in arrayadditionalItemsunevaluatedItems
Unknown propertiesadditionalPropertiesunevaluatedProperties (recursive)
Dynamic referencesNot available$dynamicRef / $dynamicAnchor
Vocabulary systemNot available$vocabulary (meta-schema)
Tool supportExcellent — ajv, quicktype, OpenAPI 3.0Good — ajv 8+, jsonschema 4+
OpenAPI compatibilityOpenAPI 3.0 uses draft-07 subsetOpenAPI 3.1 uses draft-2020-12

Practical recommendation: Use draft-07 if you're working with OpenAPI 3.0, existing tooling, or teams that are new to JSON Schema. Switch to draft-2020-12 if you're on OpenAPI 3.1 or need dynamic references for complex schema inheritance.

When to Use JSON Schema vs Zod vs OpenAPI

JSON Schema

Use when you need language-agnostic validation, when schema documents need to be stored/shared/versioned independently, or when integrating with OpenAPI specs. JSON Schema is the universal standard.

Zod (TypeScript)

Use in TypeScript applications where you want type inference and runtime validation in one package. Zod is TypeScript-first; JSON Schema is language-agnostic. You can convert between them with zod-to-json-schema.

OpenAPI / Swagger

Use when you need full API documentation, not just data validation. OpenAPI embeds JSON Schema for data types. If you're already writing OpenAPI specs, extract your schemas from there rather than writing them separately.

Validate JSON in Your Browser

Paste your JSON and explore its structure with our formatter. JSON Schema validation support is on our roadmap — for now, use ajv online at ajv.js.org/repl or our formatter to inspect and validate structure visually.

Open JSON Formatter