← helloandy.net

JSON Schema Validation — Complete Guide with Examples (2026)

Everything you need to validate JSON data: types, required fields, nested objects, arrays, patterns, conditional logic, and production-ready best practices.

Test Your APIs →

What Is JSON Schema?

JSON Schema is a declarative language for describing the structure of JSON data. It lets you define what fields a JSON document should have, what types those fields should be, and what constraints apply — then validate any document against those rules automatically.

If you build or consume APIs, JSON Schema is the standard way to enforce data contracts. It is used by OpenAPI (Swagger), MongoDB, Kubernetes CRDs, GitHub Actions, VS Code settings, and hundreds of other tools.

A JSON Schema is itself a JSON object. Here is the simplest possible schema that validates any JSON value:

{}

An empty object means "accept everything." To make it useful, you add keywords that constrain the data.

Basic Types and Properties

Every JSON value falls into one of seven primitive types. Use the type keyword to restrict which type is allowed:

TypeJSON ExampleNotes
string"hello"UTF-8 text
number3.14Integer or floating-point
integer42Whole numbers only
booleantruetrue or false
object{"a": 1}Key-value pairs
array[1, 2, 3]Ordered list of values
nullnullExplicit absence of value

To describe an object with specific fields, combine type, properties, and type constraints:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name":  { "type": "string" },
    "age":   { "type": "integer", "minimum": 0 },
    "email": { "type": "string", "format": "email" }
  }
}

This schema accepts an object where name is a string, age is a non-negative integer, and email follows the email format. All three fields are optional by default.

Required Fields

By default, every property in properties is optional. Use the required array to mandate specific fields:

{
  "type": "object",
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string" },
    "role":  { "type": "string", "default": "viewer" }
  },
  "required": ["id", "name"]
}

Here id and name must be present. role is optional and defaults to "viewer" in many implementations. Note that default is a documentation hint — most validators do not inject defaults unless explicitly configured.

Tip: Use "additionalProperties": false to reject any fields not listed in properties. This catches typos and prevents unexpected data from leaking into your system.

Nested Objects

Real-world data is rarely flat. JSON Schema handles nesting naturally — define a schema for each level:

{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "properties": {
        "name":  { "type": "string" },
        "address": {
          "type": "object",
          "properties": {
            "street": { "type": "string" },
            "city":   { "type": "string" },
            "zip":    { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" }
          },
          "required": ["street", "city", "zip"]
        }
      },
      "required": ["name", "address"]
    }
  },
  "required": ["user"]
}

This validates a three-level deep structure: root → user → address, with a regex pattern on the zip code.

Arrays

Use "type": "array" with the items keyword to validate array elements:

{
  "type": "object",
  "properties": {
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "minItems": 1,
      "maxItems": 10,
      "uniqueItems": true
    },
    "scores": {
      "type": "array",
      "items": { "type": "number", "minimum": 0, "maximum": 100 }
    }
  }
}

tags must be an array of 1–10 unique strings. scores must contain numbers between 0 and 100.

Tuple Validation (Draft 2020-12)

If your array has a fixed structure (like a coordinate pair), use prefixItems:

{
  "type": "array",
  "prefixItems": [
    { "type": "number", "minimum": -90, "maximum": 90 },
    { "type": "number", "minimum": -180, "maximum": 180 }
  ],
  "items": false
}

This accepts [40.7128, -74.0060] but rejects extra elements because "items": false forbids them.

Pattern Validation (Regex)

The pattern keyword applies a regular expression to string values. The regex follows ECMA-262 syntax:

{
  "type": "object",
  "properties": {
    "phone": {
      "type": "string",
      "pattern": "^\\+?[1-9]\\d{1,14}$"
    },
    "slug": {
      "type": "string",
      "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
    },
    "hex_color": {
      "type": "string",
      "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
    }
  }
}

phone validates E.164 international numbers. slug enforces lowercase-hyphenated URL slugs. hex_color accepts 3- or 6-digit hex codes.

Need help writing regex patterns? Use our AI Regex Generator to describe what you want in plain English and get the pattern instantly. See the Regex Cheat Sheet for a full syntax reference.

You can also use patternProperties to apply schemas to keys matching a regex:

{
  "type": "object",
  "patternProperties": {
    "^env_": { "type": "string" },
    "^count_": { "type": "integer", "minimum": 0 }
  },
  "additionalProperties": false
}

This accepts {"env_mode": "production", "count_users": 42} but rejects keys that do not match either pattern.

Conditional Schemas (if / then / else)

Sometimes validation rules depend on the value of another field. JSON Schema supports this with if, then, and else:

{
  "type": "object",
  "properties": {
    "payment_method": { "type": "string", "enum": ["credit_card", "bank_transfer"] },
    "card_number":    { "type": "string" },
    "routing_number": { "type": "string" }
  },
  "required": ["payment_method"],
  "if": {
    "properties": { "payment_method": { "const": "credit_card" } }
  },
  "then": {
    "required": ["card_number"]
  },
  "else": {
    "required": ["routing_number"]
  }
}

When payment_method is "credit_card", the card_number field is required. For any other value, routing_number is required instead. This is much cleaner than using oneOf with duplicated schemas.

Multiple Conditions with allOf

For more than two branches, wrap multiple if/then blocks inside allOf:

{
  "type": "object",
  "properties": {
    "type": { "type": "string", "enum": ["personal", "business", "government"] }
  },
  "allOf": [
    {
      "if": { "properties": { "type": { "const": "business" } } },
      "then": { "required": ["tax_id", "company_name"] }
    },
    {
      "if": { "properties": { "type": { "const": "government" } } },
      "then": { "required": ["agency_code", "jurisdiction"] }
    }
  ]
}

Common Validation Formats

The format keyword provides semantic validation for strings. It is advisory by default in Draft 2020-12 — enable format assertion in your validator for strict checking.

FormatValidatesExample
emailRFC 5321 email addressuser@example.com
uriFull URI (RFC 3986)https://helloandy.net/api-tester/
uri-referenceURI or relative reference/api/v2/users
dateISO 8601 date2026-03-14
date-timeISO 8601 date-time2026-03-14T10:30:00Z
timeISO 8601 time10:30:00+05:00
ipv4IPv4 address192.168.1.1
ipv6IPv6 address::1
uuidRFC 4122 UUID550e8400-e29b-41d4-a716-446655440000
hostnameInternet hostnameapi.example.com
json-pointerJSON Pointer (RFC 6901)/user/address/city
regexValid regex string^[a-z]+$

Tools for JSON Schema Validation

You do not need to build validation from scratch. Here are the best tools by language:

Quick Validation Example (JavaScript)

import Ajv from "ajv";
import addFormats from "ajv-formats";

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);

const schema = {
  type: "object",
  properties: {
    email: { type: "string", format: "email" },
    age:   { type: "integer", minimum: 18 }
  },
  required: ["email", "age"]
};

const validate = ajv.compile(schema);

const data = { email: "test@example.com", age: 25 };
if (validate(data)) {
  console.log("Valid!");
} else {
  console.log(validate.errors);
}

Quick Validation Example (Python)

from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "properties": {
        "email": {"type": "string", "format": "email"},
        "age":   {"type": "integer", "minimum": 18}
    },
    "required": ["email", "age"]
}

data = {"email": "test@example.com", "age": 25}

try:
    validate(instance=data, schema=schema)
    print("Valid!")
except ValidationError as e:
    print(f"Invalid: {e.message}")

Schema Composition ($ref, allOf, oneOf, anyOf)

Large schemas become unwieldy fast. JSON Schema provides composition keywords to split and reuse definitions:

{
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city":   { "type": "string" },
        "country": { "type": "string" }
      },
      "required": ["street", "city", "country"]
    }
  },
  "type": "object",
  "properties": {
    "billing_address":  { "$ref": "#/$defs/address" },
    "shipping_address": { "$ref": "#/$defs/address" }
  }
}

The $ref keyword lets you define address once and reuse it for both billing and shipping. For complex APIs, you can split schemas into separate files and reference them by URI.

Best Practices

1. Always Set additionalProperties

Explicitly set "additionalProperties": false on objects that should not accept unknown fields. This prevents silent data loss and catches misspelled keys early. If you need extensibility, set it to true or provide a schema for the additional properties.

2. Use $ref for Reusable Definitions

Extract repeated structures into $defs and reference them with $ref. This keeps schemas DRY and makes updates easier — change the definition in one place and every reference picks it up.

3. Validate on Both Client and Server

Client-side validation provides fast feedback. Server-side validation provides security. Use the same JSON Schema on both sides to guarantee consistency. Libraries like Ajv work in browsers and Node.js alike.

4. Use format with Assertion Enabled

In Draft 2020-12, format is advisory by default. If you rely on it for validation (email, date, URI), configure your validator to enforce format assertions. In Ajv, pass { validateFormats: true } or use the ajv-formats plugin.

5. Document with title and description

Add "title" and "description" to every property and definition. These appear in auto-generated docs, IDE tooltips, and validation error messages. Future-you will thank present-you.

6. Version Your Schemas

Include a $id with a version identifier (e.g., "$id": "https://api.example.com/schemas/user/v2"). This lets consumers pin to a specific version and makes breaking changes explicit.

7. Test Your Schemas

Write tests that cover valid data, each individual constraint violation, edge cases (empty strings, zero, null), and boundary values (minLength, maximum). Treat schemas as code — they deserve the same test coverage.

Pro tip: Use the API Tester to send JSON payloads to your API and observe how your server-side schema validation responds. This is the fastest way to debug schema issues without writing test scripts.

Frequently Asked Questions

What is the difference between JSON and JSON Schema?

JSON (JavaScript Object Notation) is a data format for storing and exchanging structured data. JSON Schema is a separate specification that describes the expected structure, types, and constraints of a JSON document. Think of JSON as the data and JSON Schema as the blueprint that validates whether that data is correct.

Which JSON Schema draft version should I use in 2026?

Use Draft 2020-12 (the latest stable release) for new projects. It adds features like $dynamicRef and prefixItems for tuple validation. If you are maintaining an older codebase, Draft 7 and Draft 2019-09 are still widely supported by most validation libraries.

Can JSON Schema validate nested objects and arrays?

Yes. JSON Schema supports deep nesting through the properties keyword for objects and the items keyword for arrays. You can define schemas for each level of nesting, including arrays of objects, objects containing arrays, and any combination of the two.

How do I make JSON Schema validation errors more readable?

Most validation libraries return structured error objects with a JSON Pointer path to the failing field. Use the title and description keywords in your schema to add human-readable context. Libraries like Ajv (JavaScript) and jsonschema (Python) support custom error messages and formatters.

Is JSON Schema the same as OpenAPI schema?

OpenAPI (formerly Swagger) uses an extended subset of JSON Schema. OpenAPI 3.1 achieved full compatibility with JSON Schema Draft 2020-12. Earlier OpenAPI versions (3.0 and below) had differences such as lacking support for if/then/else and using nullable instead of type arrays. For API design, OpenAPI 3.1+ schemas are interchangeable with standard JSON Schema.

Build and test APIs faster

Send JSON payloads to any endpoint and see validation responses instantly — no setup required.

Open API Tester →

More Free Developer Tools

Regex Generator Cron Generator API Tester Regex Cheat Sheet AI Chat CLAUDE.md Writer