If your API has ever received {"age": "twenty-three"} when it expected a number, you already know why JSON Schema exists.
JSON Schema lets you describe the exact shape of valid JSON data — types, required fields, string patterns, numeric ranges, and much more — and then validate any JSON document against that description. It's the schema language baked into OpenAPI (Swagger), used by Ajv (the most popular JS validator), and understood by almost every modern API toolchain.
This guide covers the essentials: what JSON Schema is, how to write one, and which keywords you'll use 90% of the time.
What Is JSON Schema?
A JSON Schema is itself a JSON document. It describes what another JSON document must look like to be considered valid.
{
"type": "object",
"required": ["name", "email"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" }
}
}
This schema says: "I expect an object with at least name (a non-empty string) and email (a valid email address)." Any JSON that satisfies those rules is valid against this schema.
Draft Versions — Which One Should You Use?
JSON Schema has several published drafts. The one you'll encounter most often in production systems is draft-07:
- Used by OpenAPI 3.0 (the Swagger standard)
- Supported by Ajv (JavaScript),
jsonschema(Python),json-schema-validator(Java) - Introduced
if/then/elseconditional validation - The most widely deployed version as of 2026
The current specification is draft 2020-12, but draft-07 still dominates tooling. Stick with draft-07 unless you have a specific reason to upgrade.
The Keywords You'll Use Most
type
Specifies the JSON type of the value. Valid values: string, number, integer, boolean, null, array, object.
{ "type": "integer" }
{ "type": ["string", "null"] }
An array of types means the value can be any of those types — useful for nullable fields.
properties and required
properties defines the expected fields in an object and their schemas. required lists which fields must be present.
{
"type": "object",
"required": ["id", "name"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"bio": { "type": "string" }
}
}
Here, id and name are required; bio is optional but must be a string if present.
additionalProperties
Controls whether an object can have fields not listed in properties.
-
"additionalProperties": false— reject any unknown fields -
"additionalProperties": { "type": "string" }— allow extra fields but only if they're strings
For strict API validation where unknown fields should be rejected, false is the right choice.
enum and const
enum restricts a value to a fixed set of options. const restricts it to exactly one value.
{ "type": "string", "enum": ["admin", "editor", "viewer"] }
{ "const": "published" }
String Constraints
{
"type": "string",
"minLength": 3,
"maxLength": 50,
"pattern": "^[a-z0-9_]+$",
"format": "email"
}
-
minLength/maxLength— character count bounds -
pattern— must match this regular expression -
format— semantic type:email,uri,date,date-time,ipv4,hostname
Note: format is technically optional in validators — some enforce it, some treat it as annotation only.
Number Constraints
{
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 5
}
For exclusive bounds (i.e., strictly greater than / less than), use exclusiveMinimum and exclusiveMaximum. In draft-07, these accept numeric values directly: "exclusiveMinimum": 0 means > 0, not ≥ 0.
Array Constraints
{
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
}
When items is a single schema, every element in the array must match it. For tuple validation (different schemas per position), set items to an array of schemas.
allOf, anyOf, oneOf
These combine multiple schemas:
-
allOf— data must be valid against all schemas (logical AND) -
anyOf— data must be valid against at least one schema (logical OR) -
oneOf— data must be valid against exactly one schema
{
"oneOf": [
{ "properties": { "type": { "const": "circle" } }, "required": ["radius"] },
{ "properties": { "type": { "const": "rect" } }, "required": ["width", "height"] }
]
}
This is the JSON Schema way to write a discriminated union.
$ref and $defs
Reuse schema definitions instead of duplicating them:
{
"$defs": {
"Address": {
"type": "object",
"required": ["street", "city"],
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
},
"type": "object",
"properties": {
"billing": { "$ref": "#/$defs/Address" },
"shipping": { "$ref": "#/$defs/Address" }
}
}
$defs (or definitions in older schemas) holds reusable definitions. $ref points to them using a JSON Pointer: #/$defs/TypeName.
if / then / else
Draft-07's conditional validation. If the data matches the if schema, it must also satisfy then. If it doesn't match if, it must satisfy else (if provided).
{
"if": { "properties": { "plan": { "const": "paid" } }, "required": ["plan"] },
"then": { "required": ["paymentMethod"] }
}
This requires paymentMethod only when plan is "paid".
Validate Your Schema Right Now
You can test any of the examples above — paste the schema on the left, paste some JSON on the right, and see clear path-based error messages instantly. No signup, no server upload, 100% in your browser:
👉 JSON Schema Validator — SnappyTools
Error messages show the exact JSON path where validation failed (e.g. $.user.address.zip), which makes debugging far faster than generic "invalid payload" errors.
Validating in Code
JavaScript (Node.js / browser):
npm install ajv ajv-formats
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv();
addFormats(ajv);
const schema = { /* your schema */ };
const validate = ajv.compile(schema);
if (!validate(data)) {
console.error(validate.errors);
}
Python:
pip install jsonschema
from jsonschema import validate, ValidationError
try:
validate(instance=data, schema=schema)
print("Valid!")
except ValidationError as e:
print(e.message, list(e.absolute_path))
Both libraries implement draft-07 and use the same schema documents — so schemas you write and test in the browser validator work identically in production.
Key Takeaways
- JSON Schema describes the shape and constraints of valid JSON data
- Draft-07 is the most widely deployed version — it's what OpenAPI 3.0 uses
- Essential keywords:
type,properties,required,additionalProperties,enum,$ref,allOf/anyOf/oneOf,if/then/else - Validate immediately in the browser: snappytools.app/json-schema-validator
- Use Ajv (JS) or jsonschema (Python) for production validation — same schemas, no rewriting
Built something that produces JSON? Add schema validation before it hits production — it takes 10 minutes to write a basic schema and it will save you hours of debugging.
Top comments (0)