JSON Schema Validator
- Runs on your device
- Nothing uploaded
- Free, no sign-up
Checks as you type. Ctrl+Enter (Cmd+Enter on a Mac) validates right away. Click any path in the results to jump to that spot.
Drafts 4 through 2020-12, picked from $schema
Validation runs on @hyperjump/json-schema 1.18.0, an open-source validator by Jason Desrosiers (Hyperjump) that is listed on json-schema.org's tools page. A copy is served from this site with its MIT license. It covers the newer keywords too, including $dynamicRef, unevaluatedProperties, unevaluatedItems, dependentSchemas and prefixItems.
The draft comes from your $schema value, so http://json-schema.org/draft-07/schema# runs as draft 7 even if the selector says something else. The page tells you when that happens. A schema without $schema uses the selector, and Auto means 2020-12. Your schema is also checked against its draft's meta-schema first, so a typo like "type": "objekt" is reported as a schema error instead of silently passing.
Test suite results for this validator
We ran the official JSON Schema Test Suite (commit 5571928, October 13, 2026) against the exact json-schema-engine.js file this page loads, through the same function the Validate button calls. Remote schemas the suite needs were registered locally, since this page never fetches. Required tests are the core of each spec. Optional tests cover behavior the spec leaves open, and format tests check format as an assertion.
| Draft | Required tests | Optional tests | Format tests |
|---|---|---|---|
| Draft 4 | 610 / 618 (98.7%) | 97 / 100 (97.0%) | 291 / 291 (100.0%) |
| Draft 6 | 833 / 841 (99.0%) | 102 / 106 (96.2%) | 414 / 414 (100.0%) |
| Draft 7 | 921 / 929 (99.1%) | 108 / 118 (91.5%) | 803 / 809 (99.3%) |
| Draft 2019-09 | 1,257 / 1,261 (99.7%) | 134 / 158 (84.8%) | 884 / 890 (99.3%) |
| Draft 2020-12 | 1,298 / 1,302 (99.7%) | 140 / 162 (86.4%) | 884 / 890 (99.3%) |
What still fails in the required tests: a schema whose $id is a file: URI (4 tests per draft; the library refuses to register those), a $ref with a sibling $id in drafts 4, 6 and 7 (2 tests), and a base URI change inside a subschema in drafts 4, 6 and 7 (2 tests). In format tests, leap-second times such as 23:59:60Z are rejected (6 tests in draft 7 and later). In 2019-09 and 2020-12, most optional failures (14 of 24, and 14 of 22) come from the old dependencies keyword, which those drafts replaced with dependentRequired and dependentSchemas.
Reading an error: instance path and schema path
Every error shows two JSON Pointers. The first, in code, is where the problem is in your data. The second, in brackets, is the keyword in your schema that failed. With the schema above and the data {"name":"","age":-1,"nickname":"A"} you get four errors:
/missing required property: email (schema:/required)/namemust be at least 1 character long (schema:/properties/name/minLength)/agemust be >= 0, got -1 (schema:/properties/age/minimum)/nicknameis not allowed ("additionalProperties" is false here) (schema:/additionalProperties)
Fix them from the top. A missing required property often explains the errors below it. Click either path in the results and the validator selects that spot in the text box, which helps a lot in long files.
Worked example: checking an API response
Pick API response with $ref and $defs in the example menu above. The schema describes an order: an id like ord_7f3k9q2m, a status from a fixed list, a three-letter currency, and an items array where every line item is checked against one shared definition in $defs/lineItem. The response has five mistakes, and the validator reports each one:
/statusmust be one of ["pending","paid","shipped","refunded"]. The value is"Paid".enumcompares exactly, so case matters. Fix the data, or list both spellings if your API really sends both./currencymust match pattern "^[A-Z]{3}$"."usd"is lowercase. Patterns are case sensitive and JSON Schema has no case-insensitive flag, so write the character class you need, such as^[A-Za-z]{3}$./items/1/qtymust be integer, got string. The second item has"qty": "1". Quoted numbers are strings in JSON. This one is very common when data passes through a form or a CSV first./items/2/pricemust be > 0, got 0. The schema uses"exclusiveMinimum": 0, so zero itself is not allowed. Use"minimum": 0if free items are fine./items/2/coloris not allowed.lineItemsets"additionalProperties": false, so any key that is not listed is rejected. Addcolortopropertiesif it is a real field.
Notice the schema paths for items 3 to 5 start with /$defs/lineItem. The error happened inside the referenced definition, so that is where you edit the rule, and the fix applies to every item at once. The /items/1 and /items/2 parts of the instance path are array indexes, counted from zero.
Common error messages and what to do
| Message | What it usually means | Fix |
|---|---|---|
| must be integer, got string | A number arrived in quotes, like "42". | Send a real number, or allow both with "type": ["integer", "string"] plus a pattern. |
| must be integer, got number | The value has a fraction, like 36.5. | Use "type": "number" if decimals are valid. |
| missing required property: x | The key is absent. A key set to null counts as present. | Add the key, or remove it from required. |
| is not allowed ("additionalProperties" is false here) | A key that is not listed in properties or matched by patternProperties. Often a typo in the data or the schema. | Add the key to properties, or fix its spelling. |
| is not allowed ("unevaluatedProperties" is false here) | Same idea, but it also sees keys defined inside allOf, $ref and if/then. | Use this instead of additionalProperties when you combine schemas. |
| must be one of [...] | The value is not in enum. The comparison is exact: case, type and whitespace all count. | Match one listed value exactly. |
| must match pattern "..." | The regex did not match. Patterns are not anchored unless you add ^ and $. | Test the regex on its own and add anchors if the whole value must match. |
| is not a valid email (or date-time, uri, ...) | A format check failed. Only shown when format is checked for your draft or setting. | Fix the value, or set Format checks to Never if you only want annotations. |
| must match at least one subschema in anyOf | None of the options matched. The nested lines show why each option failed. | Read the option you meant to match and fix that one. |
| matches more than one subschema in oneOf | The options overlap, so the data matched two of them. Common with number and integer, or objects without required. | Make the options exclusive, for example with required or const on a type field, or use anyOf. |
| has "a", so it also needs "b" | A dependentRequired (or draft 4 to 7 dependencies) rule. | Send both keys together, or neither. |
| The schema itself has a problem | The schema does not match its draft's meta-schema, like "type": "objekt" or "required": "name" instead of an array. | Click the path to see the bad keyword and check its spelling and value type. |
What changed between drafts
Most schemas work the same in every draft, but these differences cause real bugs when a schema written for one draft is read as another. That is why the $schema line matters.
| Topic | Draft 4 | Drafts 6 and 7 | 2019-09 | 2020-12 |
|---|---|---|---|---|
| Schema ID | id | $id | $id, plus $anchor | $id, $anchor |
| Reusable parts | definitions | definitions | $defs | $defs |
Keywords next to $ref | Ignored | Ignored | Applied | Applied |
| Exclusive limits | exclusiveMinimum: true next to minimum | exclusiveMinimum: 5 (a number) | A number | A number |
| Tuples | items: [...] + additionalItems | Same | Same | prefixItems: [...] + items |
| Conditionals | None | if / then / else from draft 7 | Yes | Yes |
| Property dependencies | dependencies | dependencies | dependentRequired, dependentSchemas | Same as 2019-09 |
| Closing combined schemas | Not possible | Not possible | unevaluatedProperties, unevaluatedItems | Same |
| New keywords | (baseline) | const, contains, propertyNames, true/false schemas (6); $comment, readOnly, writeOnly (7) | minContains, maxContains, $recursiveRef | $dynamicRef replaces $recursiveRef |
format (this page's default) | Checked | Checked | Annotation only | Annotation only |
$schema value | http://json-schema.org/draft-04/schema# | http://json-schema.org/draft-07/schema# (or draft-06) | https://json-schema.org/draft/2019-09/schema | https://json-schema.org/draft/2020-12/schema |
The array form of items is the change that bites most often. In 2020-12, "items": [{...}, {...}] is a schema error, and the same tuple is written with prefixItems. The Tuple with prefixItems example above shows the 2020-12 way.
Large files
Validation runs in your browser tab, so the limit is your device's memory. In our test on October 20, 2026, in desktop Chrome, a 5 MB file with 50,000 records took about 0.6 seconds from click to result, and a 10 MB file with 100,000 records about 2 seconds. Phones are slower. Above about 400 KB, the page stops checking on every keystroke and waits for the Validate button, so typing stays smooth. If there are thousands of errors, the first 200 are listed with a button to show the rest. Usually they are the same mistake repeated, so fix the first one and check again.
Where this validator stops
- No remote references. A
$refto another file or URL is reported as unresolvable. Bundle the referenced schemas into$defsfirst. - No custom meta-schemas.
$schemamust be one of the five official drafts. Custom vocabularies and OpenAPI dialects are not available here. - Draft 3 and older are not supported.
- One schema, one instance. To check many files at once, use one of the libraries in the code section below in your CI.
JSON Schema mistakes that pass silently
- additionalProperties defaults to allowed. Unknown keys pass unless you set
"additionalProperties": false. - Typos in keyword names.
"requried"is an unknown keyword, so it is ignored and the rule never runs. - integer vs number.
1.5failsintegerbut passesnumber.1.0counts as an integer in draft 6 and later. - Draft differences.
definitionsbecame$defs, array-formitemsbecameprefixItemsin 2020-12, andexclusiveMinimumchanged from a boolean in draft 4 to a number. The table above has the full list. Set$schemaso every tool reads your schema the same way. - Keywords next to $ref in drafts 4 to 7. In those drafts,
{"$ref": "#/definitions/x", "maxLength": 5}ignores themaxLength. Wrap both inallOf, or move to 2019-09 or later.
Validate JSON Schema in JavaScript, Python and Go
The same schema and the bad instance {"age": -1} in each language. Every snippet reports the missing email and the negative age with their paths.
JavaScript: Ajv with the 2020-12 build
import Ajv2020 from "ajv/dist/2020.js"; // npm i ajv ajv-formats
import addFormats from "ajv-formats";
const schema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
required: ["email"],
properties: { email: { type: "string", format: "email" }, age: { type: "integer", minimum: 0 } },
};
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv); // format is only checked when you add this
const validate = ajv.compile(schema);
if (!validate({ age: -1 })) {
for (const e of validate.errors) console.log(e.instancePath || "/", e.message);
}
Python: jsonschema
from jsonschema import validators # pip install jsonschema
schema = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["email"],
"properties": {"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0}},
}
cls = validators.validator_for(schema) # picks the draft from $schema
cls.check_schema(schema)
v = cls(schema, format_checker=cls.FORMAT_CHECKER)
for err in v.iter_errors({"age": -1}):
print(err.json_path, err.message)
Go: github.com/santhosh-tekuri/jsonschema/v6
package main
import (
"fmt"
"strings"
"github.com/santhosh-tekuri/jsonschema/v6" // go get github.com/santhosh-tekuri/jsonschema/v6
)
const schemaJSON = `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["email"],
"properties": {"age": {"type": "integer", "minimum": 0}}
}`
func main() {
schemaDoc, _ := jsonschema.UnmarshalJSON(strings.NewReader(schemaJSON))
c := jsonschema.NewCompiler()
if err := c.AddResource("schema.json", schemaDoc); err != nil {
panic(err)
}
sch, err := c.Compile("schema.json")
if err != nil {
panic(err)
}
inst, _ := jsonschema.UnmarshalJSON(strings.NewReader(`{"age": -1}`))
fmt.Println(sch.Validate(inst)) // nil when valid, otherwise every error with its path
}
Each snippet was run on October 14, 2026 with Ajv 8.20.0 with ajv-formats 3.0.1, jsonschema 4.26.0 and santhosh-tekuri/jsonschema v6.0.3.
JSON Schema specs and test suite
- JSON Schema specification: current and past drafts
- Draft 2020-12: Core
- Draft 2020-12: Validation: type, enum, minimum, format and the other assertion keywords
- Draft 2019-09
- Draft 7
- Draft 6 release notes
- Draft 4 core
- Official JSON Schema Test Suite: the tests behind the pass rates on this page
- Bowtie: compliance reports for many validators
- @hyperjump/json-schema: the validator this page runs
- Ajv documentation
- python-jsonschema documentation
- santhosh-tekuri/jsonschema (Go)
FAQ
Does validation upload my JSON?
No. The schema and the instance stay in your browser. The validator is a local copy of the open-source @hyperjump/json-schema library, and this page never fetches anything while it validates, not even a remote $ref.
Which JSON Schema drafts are supported?
Drafts 4, 6, 7, 2019-09 and 2020-12. If your schema has a $schema keyword, that draft is used, as the spec requires. If it has none, the draft selector decides, and Auto falls back to 2020-12. Draft 3 and older are not supported.
How compliant is this validator?
We ran the official JSON Schema Test Suite against the exact script this page loads. Required tests pass at 98.7% for draft 4, 99.0% for draft 6, 99.1% for draft 7, and 99.7% for both 2019-09 and 2020-12. The table on this page lists every number, including the optional and format tests, and what fails.
Why did my $ref fail?
References inside the same document work, including $defs, definitions, $anchor and embedded $id. References to other files or URLs are not fetched. Copy the referenced schema into $defs and point $ref at it.
Is format checked?
By default it follows each draft: drafts 4, 6 and 7 check format, while 2019-09 and 2020-12 treat it as an annotation only. Use the format setting to always check it or never check it.
Why does data with extra fields still pass?
JSON Schema allows extra properties by default. Add additionalProperties: false to the object schema to reject unknown keys, or unevaluatedProperties: false in 2019-09 and later when you combine schemas with allOf.
Which draft should I use for a new schema?
Use 2020-12 unless a tool you depend on only reads an older draft. OpenAPI 3.1 uses 2020-12, while OpenAPI 3.0 uses its own extended subset of an older draft, which this page does not support. Whatever you pick, put it in $schema so every validator reads the schema the same way.
How do I find an error in a long file?
Click the path at the start of any error and the validator selects that value in the instance box. Click the schema path to select the rule that failed. If the JSON itself is broken, the message gives the line and column and a Show me button jumps there.
Can I generate a schema from sample data?
Yes. Use the JSON Schema generator to draft one from an example, then tighten required fields and constraints by hand and test it here.