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.
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:
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
typeControls the JSON type. "string", "integer", "number", "boolean", "object", "array", "null". Use an array like ["string", "null"] to allow multiple types.
requiredArray of property names that must be present. Properties not listed are optional by default.
enumRestricts the value to a specific list. "enum": ["admin", "editor"] means no other string is valid.
formatValidates string format. Common formats: "email", "date-time", "uri", "uuid", "hostname". Format validation requires a validator that enables strict mode (ajv-formats).
patternValidates a string against a regex. Use for enforcing slug formats, phone number patterns, etc.
additionalPropertiesSet to false to reject any properties not defined in "properties". Critical for catching API response bloat or typos.
minimum / maximumInteger and number range constraints. exclusiveMinimum and exclusiveMaximum are also available.
allOf / anyOf / oneOfCompose 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
| Feature | Draft-07 | Draft-2020-12 |
|---|---|---|
| Tuple validation | items as array | prefixItems (cleaner) |
| Unknown items in array | additionalItems | unevaluatedItems |
| Unknown properties | additionalProperties | unevaluatedProperties (recursive) |
| Dynamic references | Not available | $dynamicRef / $dynamicAnchor |
| Vocabulary system | Not available | $vocabulary (meta-schema) |
| Tool support | Excellent — ajv, quicktype, OpenAPI 3.0 | Good — ajv 8+, jsonschema 4+ |
| OpenAPI compatibility | OpenAPI 3.0 uses draft-07 subset | OpenAPI 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