Example
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["sku", "price"],
"properties": {
"sku": { "type": "string", "pattern": "^[A-Z]{3}-\\d{4}$" },
"price": { "type": "number", "exclusiveMinimum": 0 },
"tags": { "type": "array", "items": { "type": "string" } }
}
}
{
"sku": "abc-12",
"price": 0,
"tags": ["sale", 3]
}
The validator reports:
| Line | Path | Error |
|---|---|---|
| 2 | /sku |
Must match the pattern ^[A-Z]{3}-\d{4}$ |
| 3 | /price |
Must be > 0 |
| 4 | /tags/1 |
Must be string, got integer |
Note that "price": 0 fails exclusiveMinimum: 0 but would pass minimum: 0, and
the pattern needs the backslash escaped in JSON (\\d).
JSON Schema versions
The $schema keyword at the top of a schema says which version it is written in:
| Version | $schema | Notable additions |
|---|---|---|
| 2020-12 | https://json-schema.org/draft/2020-12/schema |
prefixItems for tuples, $dynamicRef. Used by OpenAPI 3.1. |
| 2019-09 | https://json-schema.org/draft/2019-09/schema |
unevaluatedProperties, dependentRequired, $defs, $anchor. |
| Draft 7 | http://json-schema.org/draft-07/schema# |
if / then / else. The most widely supported version. |
| Draft 6 | http://json-schema.org/draft-06/schema# |
const, contains, propertyNames. |
| Draft 4 | http://json-schema.org/draft-04/schema# |
Boolean exclusiveMinimum. Used by OpenAPI 2.0 (Swagger) and 3.0. |
The versions are mostly compatible. The changes that bite: definitions became $defs in
2019-09, array-form items became prefixItems in 2020-12, and draft 4’s boolean
exclusiveMinimum: true became a number in draft 6.
Common mistakes
additionalProperties: false with allOf
additionalProperties only sees the properties next to it, not those in
allOf branches, so combining schemas this way rejects every property. Use
unevaluatedProperties: false (2019-09 and later) at the top level instead.
required inside a property
required is a list on the object, not a flag on each property: write
"required": ["email"] next to properties, not "email": { "required": true }
(that was draft 3).
Forgetting type
Keywords only apply to matching types. { "minimum": 1 } accepts the string
"0", because minimum only checks numbers. Add "type": "integer" to reject it.
Validate in code and on the command line
// 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);
const validate = ajv.compile(schema);
if (!validate(data)) {
for (const e of validate.errors) console.log(e.instancePath || '/', e.message);
}
# pip install jsonschema
from jsonschema import Draft202012Validator
validator = Draft202012Validator(schema, format_checker=Draft202012Validator.FORMAT_CHECKER)
for error in sorted(validator.iter_errors(data), key=lambda e: e.path):
print(error.json_path, error.message)
# check-jsonschema: JSON, YAML and TOML files, plus built-in schemas (pip install check-jsonschema)
check-jsonschema --schemafile user.schema.json user.json
check-jsonschema --builtin-schema vendor.github-workflows .github/workflows/ci.yml
# ajv-cli (npm install -g ajv-cli ajv-formats)
ajv validate --spec=draft2020 -c ajv-formats -s user.schema.json -d user.json
Don’t have a schema yet? Generate one from sample data with JSON to JSON Schema, then tighten it by hand. For syntax errors in the JSON itself, use the JSON Validator.
Frequently asked questions
How do I validate JSON against a JSON Schema?
Paste the schema on the left and the JSON on the right. The result updates as you type: every error is listed with its line and column in the JSON, the JSON Pointer path of the failing value, and a plain-English reason. Nothing is uploaded; validation runs in your browser with Ajv.
Which JSON Schema versions are supported?
Draft 2020-12, 2019-09, draft 7, draft 6 and draft 4. By default the version is taken from the schema’s $schema keyword, and 2020-12 is assumed when $schema is missing. You can also force a version with the Draft menu.
Can I validate YAML with a JSON Schema?
Yes. Both inputs accept YAML as well as JSON, which is handy for Kubernetes manifests, Docker Compose files, GitHub Actions workflows and OpenAPI documents. Error positions point at the line in the YAML.
Why does an invalid email or date pass validation elsewhere?
Since draft 2019-09, format is only an annotation by default, so many validators ignore it unless format checking is switched on. In Ajv you need the ajv-formats package; in Python’s jsonschema you pass a format_checker. This tool checks formats unless you untick Check formats.
Does the validator follow $ref to other files or URLs?
No. References inside the same schema (#/$defs/address, #/definitions/address) work, but nothing is downloaded. Paste the referenced definitions into $defs and point $ref at them instead.
Why is my misspelled keyword not an error?
JSON Schema ignores keywords it doesn’t know, so a typo like "requried" silently does nothing. This validator lists unknown keywords as warnings so you can spot them.