Home → JSON Schema Tutorial

JSON Schema Tutorial: Complete Guide from Beginner to Advanced (2026)

📅 Updated March 2026 ⏱ 12 min read 🏗 For API & backend developers

JSON Schema is the standard way to define and validate the structure of JSON data. If you're building APIs, processing config files, or validating form input, JSON Schema lets you describe exactly what your JSON should look like — and then validate any JSON document against that description automatically.

📐 JSON Schema Generator — Try it now Open full tool ↗
Schema will appear here…

What Is JSON Schema?

JSON Schema is a vocabulary (a set of keywords) written in JSON that describes the structure of other JSON documents. Think of it like:

JSON Schema is specified as a JSON object with special keywords like type, required, properties, minimum, pattern, and many more. The current stable version is JSON Schema Draft 2020-12 (also called Draft 10).

Your First JSON Schema

The simplest possible schema — validates that a value is a string:

{ "type": "string" }

A slightly more useful schema — validates a user object:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/user.json",
  "title": "User",
  "description": "A registered user",
  "type": "object",
  "required": ["id", "name", "email"],
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string" },
    "email": { "type": "string" }
  }
}

The $schema and $id keywords are optional but recommended — they tell validators which version to use and give the schema a unique identifier.

JSON Schema Types

The type keyword is the most fundamental — it specifies what JSON type is allowed:

TypeMatchesExample
"string"JSON strings"hello"
"number"Any number (int or float)42, 3.14
"integer"Whole numbers only42, -7
"boolean"true or falsetrue
"null"null valuenull
"array"JSON arrays[1, 2, 3]
"object"JSON objects{"key": "val"}

You can allow multiple types using an array:

{ "type": ["string", "null"] }   // accepts "hello" or null

String Validation Keywords

{
  "type": "string",
  "minLength": 3,           // minimum character count
  "maxLength": 100,         // maximum character count
  "pattern": "^[a-zA-Z]+$", // regex pattern (letters only)
  "format": "email"         // semantic format hint
}

Common format values: email uri date date-time time uuid ipv4 ipv6 hostname

Note: format validation is optional — validators may or may not enforce it. Use pattern for strict enforcement.

Number Validation Keywords

{
  "type": "number",
  "minimum": 0,           // value must be >= 0
  "maximum": 100,         // value must be <= 100
  "exclusiveMinimum": 0,  // value must be > 0 (not equal)
  "exclusiveMaximum": 100,// value must be < 100
  "multipleOf": 5         // value must be divisible by 5: 0, 5, 10...
}

Object Validation: properties, required, additionalProperties

{
  "type": "object",
  "required": ["id", "name"],          // these fields MUST be present
  "properties": {
    "id":    { "type": "integer" },
    "name":  { "type": "string", "minLength": 1 },
    "email": { "type": "string", "format": "email" },
    "age":   { "type": "integer", "minimum": 0 }
  },
  "additionalProperties": false,       // reject any unknown fields
  "minProperties": 2,                  // at least 2 properties
  "maxProperties": 10                  // at most 10 properties
}

additionalProperties is important for strict schemas. By default it's true (any extra fields are allowed). Set it to false to reject unknown fields, or set it to a schema to constrain extra fields' types.

Array Validation Keywords

{
  "type": "array",
  "items": { "type": "string" },   // each item must be a string
  "minItems": 1,                   // at least 1 item
  "maxItems": 10,                  // at most 10 items
  "uniqueItems": true              // no duplicate values
}

For arrays of objects (very common in APIs):

{
  "type": "array",
  "items": {
    "type": "object",
    "required": ["id", "name"],
    "properties": {
      "id":   { "type": "integer" },
      "name": { "type": "string" }
    }
  }
}

Nested Objects (Real Example)

Here's a schema for a typical REST API user with a nested address:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "User",
  "type": "object",
  "required": ["id", "name", "email"],
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique user ID",
      "minimum": 1
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "role": {
      "type": "string",
      "enum": ["admin", "editor", "viewer"]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "address": {
      "type": "object",
      "properties": {
        "street":  { "type": "string" },
        "city":    { "type": "string" },
        "country": { "type": "string", "minLength": 2, "maxLength": 2 },
        "zip":     { "type": "string", "pattern": "^[0-9]{5}$" }
      },
      "required": ["city", "country"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "uniqueItems": true
    }
  }
}

Reusing Schemas with $ref and $defs

When the same sub-schema appears in multiple places, define it once in $defs and reference it with $ref:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "Address": {
      "type": "object",
      "required": ["city", "country"],
      "properties": {
        "street":  { "type": "string" },
        "city":    { "type": "string" },
        "country": { "type": "string" }
      }
    },
    "PhoneNumber": {
      "type": "string",
      "pattern": "^\\+?[0-9\\s\\-]{7,15}$"
    }
  },
  "type": "object",
  "properties": {
    "billingAddress":  { "$ref": "#/$defs/Address" },
    "shippingAddress": { "$ref": "#/$defs/Address" },
    "phone": { "$ref": "#/$defs/PhoneNumber" }
  }
}

Note: In older drafts (Draft 4–7), $defs was called definitions. If you see {"$ref": "#/definitions/..."}, that's the older syntax.

Composition Keywords: allOf, anyOf, oneOf, not

These keywords let you combine schemas:

allOf — must match ALL schemas

{
  "allOf": [
    { "type": "object", "required": ["id"] },
    { "properties": { "name": { "type": "string" } } }
  ]
}
// Data must satisfy BOTH schemas

anyOf — must match AT LEAST ONE schema

{
  "anyOf": [
    { "type": "string" },
    { "type": "number" }
  ]
}
// Data can be a string OR a number

oneOf — must match EXACTLY ONE schema

{
  "oneOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "string", "minLength": 10 }
  ]
}
// Must be a short string OR a long string, not something matching both

not — must NOT match the schema

{
  "not": { "type": "null" }
}
// Any value except null

Conditional Validation: if / then / else

Apply different rules based on field values:

{
  "type": "object",
  "properties": {
    "accountType": { "type": "string", "enum": ["personal", "business"] },
    "companyName": { "type": "string" },
    "ssn":         { "type": "string" }
  },
  "if":   { "properties": { "accountType": { "const": "business" } } },
  "then": { "required": ["companyName"] },
  "else": { "required": ["ssn"] }
}

This schema requires companyName if accountType is "business", and ssn otherwise.

The enum Keyword

Restrict a value to a fixed set of options:

{ "enum": ["red", "green", "blue"] }
// Accepts only these three strings

{ "type": "string", "enum": ["pending", "active", "suspended", "deleted"] }
// Status field with allowed values

The const Keyword

Requires the value to be exactly one specific value:

{ "const": "v2" }
// Only "v2" is valid — useful for versioned APIs

Complete API Schema Example

Here's a production-ready schema for a product catalog API:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://api.example.com/schemas/product.json",
  "title": "Product",
  "description": "A product in the catalog",
  "type": "object",
  "required": ["id", "name", "price", "currency", "inStock"],
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "description": "Unique product identifier"
    },
    "sku": {
      "type": "string",
      "pattern": "^[A-Z]{2}-[0-9]{6}$",
      "description": "Stock-keeping unit code, e.g. AB-123456"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "description": {
      "type": ["string", "null"],
      "maxLength": 2000
    },
    "price": {
      "type": "number",
      "minimum": 0,
      "exclusiveMinimum": 0
    },
    "currency": {
      "type": "string",
      "enum": ["USD", "EUR", "GBP", "JPY"]
    },
    "inStock": {
      "type": "boolean"
    },
    "stock": {
      "type": "integer",
      "minimum": 0
    },
    "categories": {
      "type": "array",
      "items": { "type": "string" },
      "minItems": 1,
      "uniqueItems": true
    },
    "images": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url":    { "type": "string", "format": "uri" },
          "alt":    { "type": "string" },
          "width":  { "type": "integer", "minimum": 1 },
          "height": { "type": "integer", "minimum": 1 }
        }
      }
    },
    "createdAt":  { "type": "string", "format": "date-time" },
    "updatedAt":  { "type": "string", "format": "date-time" }
  }
}

JSON Schema Versions

VersionReleaseKey Changes
Draft 42013First widely-adopted version
Draft 62017$ref improvements, exclusiveMinimum changes
Draft 72018if/then/else, readOnly/writeOnly, content encoding
Draft 2019-092019$defs (replaces definitions), $anchor, $recursiveRef
Draft 2020-122020prefixItems, $dynamicRef, improved $ref

Recommendation: Use Draft 2020-12 for new projects. For maximum compatibility with older tools, Draft 7 is still widely supported. Always declare "$schema" to specify which version you're targeting.

Validating JSON Against a Schema (Code)

JavaScript: Ajv

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

const ajv = new Ajv();
addFormats(ajv); // adds format: "email", "date-time", etc.

const schema = {
  type: 'object',
  required: ['id', 'name'],
  properties: {
    id:   { type: 'integer' },
    name: { type: 'string', minLength: 1 },
    email:{ type: 'string', format: 'email' }
  }
};

const validate = ajv.compile(schema);
const data = { id: 1, name: 'Alice', email: 'alice@example.com' };

if (validate(data)) {
  console.log('Valid!');
} else {
  console.log('Errors:', validate.errors);
}

Python: jsonschema

from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "required": ["id", "name"],
    "properties": {
        "id":   {"type": "integer"},
        "name": {"type": "string", "minLength": 1}
    }
}

data = {"id": 1, "name": "Alice"}

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

Generate a schema from your JSON

Paste any JSON object and get a ready-to-use JSON Schema automatically.

Generate JSON Schema →

Frequently Asked Questions

What is JSON Schema? +
JSON Schema is a vocabulary written in JSON that describes the structure, types, and constraints of other JSON documents. You define it once — what fields are required, what types they must be, what values are allowed — then use a validator library to check any JSON document against it automatically.
What are the basic JSON Schema types? +
JSON Schema supports seven types: string, number, integer, boolean, null, array, and object. Use the type keyword: {"type": "string"}. You can allow multiple types: {"type": ["string", "null"]}.
How do I make a field required in JSON Schema? +
Use the required keyword at the object level as an array of field names: {"type": "object", "required": ["id", "name"], "properties": {...}}. Fields listed in required must be present in the data for validation to pass.
What is the difference between allOf, anyOf, and oneOf? +
allOf requires the data to satisfy ALL listed schemas. anyOf requires it to satisfy AT LEAST ONE. oneOf requires it to satisfy EXACTLY ONE (mutually exclusive schemas). Use anyOf for flexible types, allOf to extend/merge schemas, and oneOf when schemas are mutually exclusive.
What is additionalProperties in JSON Schema? +
By default, JSON objects can have any additional properties beyond what's defined in properties. Setting "additionalProperties": false makes the schema strict — it will reject any properties not listed in properties or patternProperties. This is useful for catching typos in field names.
Which JSON Schema version should I use? +
For new projects, use Draft 2020-12 (the latest stable version). For maximum compatibility with older tools (like OpenAPI 3.0), use Draft 7. Always declare "$schema": "https://json-schema.org/draft/2020-12/schema" in your schema file to make the version explicit.
How do I generate a JSON Schema from existing JSON? +
Use the free JSON Schema Generator at jsonwebtools.com. Paste your JSON and it creates a base schema automatically. You'll still need to review and refine it — add constraints, mark required fields, and add descriptions — but the generator gives you an excellent starting point.

Related Tools & Guides

JSON Schema Generator  |  JSON Schema Validator  |  JSON Schema Examples  |  How to Validate JSON  |  JSONPath Cheatsheet  |  JSON to TypeScript