JSON Schema Explained for Beginners (With Examples)
Data modeling 10 min readUpdated October 12, 2026
JSON itself says nothing about what a document should contain. Is age a number or a string? Is email required? JSON Schema is a standard way to answer those questions in a machine-checkable form. A schema is just another JSON document that describes the shape of your data, so you can validate input automatically and document your API at the same time.
A first schema
Suppose your API accepts a user like this:
{
"id": 42,
"name": "Asha",
"email": "asha@example.com",
"roles": ["admin", "beta"]
}A schema for it looks like this:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "User",
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" },
"roles": {
"type": "array",
"items": { "type": "string", "enum": ["admin", "editor", "beta"] },
"uniqueItems": true
}
},
"required": ["id", "name", "email"],
"additionalProperties": false
}Read it from top to bottom: the document is an object; it may have the four listed properties with the given types and limits; id, name and email must be present; and no other properties are allowed.
The keywords you will use most
| Keyword | Applies to | What it does |
|---|---|---|
type | Any value | string, number, integer, boolean, object, array or null. Can be a list, such as ["string", "null"]. |
properties | Objects | Describes each known key with its own sub-schema. |
required | Objects | Lists keys that must be present. Listing a key in properties alone does not make it required. |
additionalProperties | Objects | false rejects any key not listed in properties. The default is to allow them. |
items | Arrays | The schema every element must match. |
minItems, maxItems, uniqueItems | Arrays | Limit the length, and require distinct elements. |
enum, const | Any value | Restrict to a list of allowed values, or to exactly one. |
minimum, maximum | Numbers | Inclusive limits. exclusiveMinimum and exclusiveMaximum exclude the boundary. |
minLength, maxLength, pattern | Strings | Limit length, or require a match with a regular expression. |
format | Strings | Hints such as email, date-time, uri, uuid. Many validators only check them when you turn format validation on. |
Two defaults that surprise people
Properties are optional unless listed in required, and extra properties are allowed unless you set additionalProperties to false. A typo such as emial therefore passes a schema that does not forbid extra keys, while the real email is reported missing only if it is required.
Valid and invalid examples
{
"id": "42",
"name": "",
"roles": ["admin", "admin", "root"],
"nickname": "A"
}idis the string "42", not an integer.nameis shorter thanminLength1.emailis required but missing.rolescontains a duplicate and a value ("root") that is not in theenum.nicknameis not allowed becauseadditionalPropertiesisfalse.
A good validator reports all of these with the path to each problem, so the person who sent the data can fix it in one pass.
Combining and reusing schemas
Real schemas repeat structures. Define them once under $defs and point to them with $ref.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"billing": { "$ref": "#/$defs/address" },
"shipping": { "$ref": "#/$defs/address" }
},
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{6}quot; }
},
"required": ["street", "city"]
}
}
}To express choices, use allOf (must match all), anyOf (at least one) and oneOf (exactly one). For a value that may be missing data, allow null explicitly with "type": ["string", "null"].
Validate data in code
Python: jsonschema
# pip install jsonschema
import json
from jsonschema import Draft202012Validator
schema = json.load(open("user.schema.json", encoding="utf-8"))
data = json.load(open("user.json", encoding="utf-8"))
validator = Draft202012Validator(schema)
for error in sorted(validator.iter_errors(data), key=lambda e: [str(part) for part in e.path]):
location = "/".join(str(part) for part in error.path) or "(root)"
print(f"{location}: {error.message}")JavaScript: Ajv
// npm install ajv ajv-formats
import Ajv2020 from "ajv/dist/2020.js";
import addFormats from "ajv-formats";
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv); // enables checks for "email", "date-time", and so on
const validate = ajv.compile(schema);
if (!validate(data)) {
console.log(validate.errors);
}Start from sample data, then tighten
Writing a large schema by hand is slow, so it is common to generate a first draft from a sample document and then edit it. A generator can see the types in your sample, but it cannot know your rules. A sample with one string field says nothing about whether the field is required, how long it may be, or whether it is really one of a fixed set of values.
- Generate a draft from a representative sample, ideally the most complete one you have.
- Decide which properties are truly required and remove the rest from
required. - Add limits such as
minimum,maxLength,patternandenumwhere your business rules say so. - Set
additionalPropertiesdeliberately,falsefor strict inputs and the default for forward-compatible ones. - Test the schema against valid and invalid examples, including empty arrays, nulls and boundary values.
Where schemas are used
- API contracts. OpenAPI 3.1 uses JSON Schema to describe request and response bodies.
- Input validation. Reject bad data at the edge of your system with a clear message.
- Configuration files. Editors can use a schema to offer autocomplete and flag mistakes while you type.
- Test data and documentation. The schema is a precise, shared definition of a data format that both humans and tools can read.
Frequently asked questions
Which JSON Schema version should I use?
Draft 2020-12 is the current version of the specification and a good default for new work. Draft-07 is still widely supported by older tools. Always include $schema so validators know which rules to apply.
Is JSON Schema the same as an OpenAPI schema?
They are closely related. OpenAPI 3.1 aligns with JSON Schema 2020-12, while OpenAPI 3.0 used a modified subset with some differences, such as how nullable values are written.
Does JSON Schema check business rules?
Only the ones that can be expressed as structure and limits. Rules that depend on other data, such as "this id must exist in the database" or "end date must be after start date", still need code.
Why does my `format: email` check not fail on a bad address?
In the specification, format is an annotation unless format validation is enabled. Turn it on in your validator, for example with ajv-formats in Ajv, and remember that format checks are approximate by nature.