JSON Schema Validator
Validate JSON against a JSON Schema (draft 2020-12) and get every failure located by JSON Pointer, with unsupported keywords listed rather than skipped.
JSON Schema
draft 2020-12Data to validate
Result
Paste a schema and a document to validate it against.
Validation runs in your browser — neither the schema nor the data is uploaded. Anything this validator does not implement is listed above rather than skipped silently, so a “valid” result is never mistaken for a schema that was only partly applied.
About the JSON Schema Validator
This JSON validator checks a document against a JSON Schema, not merely against the JSON grammar. Whether a payload parses is a question the formatter already answers; the question that costs you an afternoon is whether it has the fields your API contract requires, in the types and ranges the contract specifies. That is what a schema describes and what this page checks.
Every failure is reported with a JSON Pointer to the exact value that failed and the keyword that rejected it — `/items/2/price`, `minimum`, "−4 is less than the minimum 0". One document can fail in a dozen places; all of them are listed, rather than only the first, because fixing them one round-trip at a time is the slow way to do this.
It implements the draft 2020-12 core and validation vocabularies: types, enum and const, numeric and string bounds, patterns, array and object constraints, `$ref` into `$defs`, and the applicators `allOf`, `anyOf`, `oneOf`, `not` and `if`/`then`/`else`. What it does not implement — remote `$ref`, `$dynamicRef`, `unevaluatedProperties` — is listed on screen when your schema uses it. A validator that quietly skips a keyword is worse than no validator, because it answers "valid" for data the real one rejects.
- Draft 2020-12 core and validation vocabularies, including if/then/else and $ref into $defs
- Every failure located by JSON Pointer, with the keyword that produced it
- Unsupported keywords listed on screen instead of silently ignored
- format checked as an assertion when you ask for it, and as an annotation when you do not
- String lengths counted in code points, so an emoji counts as one character
- Runs entirely in your browser — schemas and payloads are never uploaded
How to use it
- Paste your JSON Schema on the left. Draft 2020-12 is assumed; draft-07 schemas validate too, apart from keywords that changed name.
- Paste the document to check on the right.
- Read the failures. Each row gives the path into your data, the keyword that rejected it, and what it expected.
- Leave Enforce format on to check email, date-time, uuid and similar; turn it off to treat format as documentation, which is what the specification says it is by default.
- Check the panel underneath for anything in the schema that was not applied.
Real-world use cases
Backend & API developers
Check a request or response payload against the schema in the API contract before writing the handler that has to trust it, instead of finding the mismatch from a production error report.
QA & contract-test engineers
Validate a fixture or a captured payload against the schema it is supposed to satisfy before wiring it into a test suite, so a broken fixture fails here rather than inside an opaque test runner.
API & platform designers
Draft required fields, enums and numeric ranges directly, and see every violation a sample payload triggers at once rather than fixing one round-trip at a time.
DevOps & config engineers
Check a JSON config file against its published schema before it ships, catching a wrong type or a missing key the moment it is written rather than at process start-up.
Frontend developers
Confirm that a mocked API response used in local development or tests still matches the real contract, catching drift between the mock and the backend early.
Examples
A required property is missing
{ "type": "object", "required": ["id", "email"] }
{ "id": "usr_1" }(root) required: Missing required property "email".
The failure is reported at the object that should have contained the property, not at the property — there is no path to a value that does not exist.
A type failure inside an array
{ "type": "array", "items": { "type": "number" } }
[1, "2", 3]/1 type: Expected number, got string.
JSON Pointer indexes arrays numerically, so `/1` is the second element. Note that `"2"` is a string: JSON Schema never coerces.
if / then, for a conditional field
{
"if": { "properties": { "kind": { "const": "card" } }, "required": ["kind"] },
"then": { "required": ["number"] },
"else": { "required": ["iban"] }
}
{ "kind": "card" }(root) required: Missing required property "number".
`if` never produces an error of its own — it only selects which branch applies. This is how you express "these fields are required, but only for this variant".
A keyword that was not checked
{ "type": "object", "unevaluatedProperties": false }
{ "anything": 1 }Valid Not checked: /unevaluatedProperties — "unevaluatedProperties" is not implemented
The honest result. `unevaluatedProperties` needs the annotation-collection machinery of the full specification; rather than pretend, the page says which part of your schema it did not apply.
Common errors
| Message | Cause | Fix |
|---|---|---|
| Expected string, got null | A nullable field. In JSON Schema, `null` is its own type and is not a member of any other type. | Write `{"type": ["string", "null"]}`. OpenAPI 3.0's `nullable: true` is not JSON Schema and is not recognised here; OpenAPI 3.1 uses the type array. |
| Cannot resolve "..." — only local pointers are supported | The schema references another file or a URL, and a browser tool cannot fetch it without becoming a server-side tool. | Inline the referenced schema under `$defs` and point the `$ref` at `#/$defs/name`. |
| Matches none of the alternatives in anyOf | Every branch failed, and by design the individual reasons are not reported — with five branches you would get five sets of unrelated errors. | Validate against the branch you expect to match, on its own, to see why it failed. If your branches are mutually exclusive, `oneOf` with a discriminating `const` usually gives better messages. |
| Property "x" is not allowed by the schema | `additionalProperties: false`, and `x` is matched by neither `properties` nor `patternProperties`. | Add it to `properties`, cover it with a `patternProperties` entry, or relax `additionalProperties`. Note that `additionalProperties` does not see through `allOf` — a property declared in one branch is still additional to the other. |
| A pattern matches when it should not | `pattern` is a search, not a full match — `"pattern": "abc"` matches `xxabcxx`. | Anchor it: `^abc$`. This catches nearly everyone once. |
| An email like a@b passes | Format checks are pragmatic, and a single-label domain is legal in the RFC even though nobody wants it. | If you need stricter rules than the format registry gives you, add a `pattern` alongside `format`. Format assertion is optional in the specification precisely because implementations disagree on the edges. |
Frequently asked questions
›Is my schema or data uploaded anywhere?
No. Both are parsed and validated in your browser. Nothing is transmitted, stored or logged — which matters here, because the document you are validating is usually a real payload with real customer data in it.
›How is this different from the JSON formatter's validation?
The formatter answers a syntax question: is this valid JSON at all. This page answers a contract question: does this document match the shape you agreed on. A payload can be perfectly valid JSON and still be missing a required field, use a string where a number belongs, or carry a value outside its permitted range.
›Which draft does it support?
Draft 2020-12, which is the current one and the one OpenAPI 3.1 uses. Draft-07 schemas mostly validate unchanged; the differences that matter are that tuple validation moved from `items`/`additionalItems` to `prefixItems`/`items`, and `definitions` became `$defs` — both old spellings are still accepted here.
›Why is `format` off by default in the specification?
Because in JSON Schema `format` is an annotation: it describes the intended semantics of a string, and implementations are explicitly permitted not to enforce it. Enforcement varies enough between languages that a schema relying on it can pass in one stack and fail in another. This page lets you assert it, and tells you it is doing so.
›Can it validate against a schema hosted at a URL?
No, and that is deliberate. Fetching a remote schema would mean sending a request from this page, which breaks the guarantee that nothing here touches the network. Paste the schema in, or inline the referenced parts under `$defs`.
›Does it generate a schema from my data?
Not on this page. The JSON to TypeScript tool infers a structure from a sample document, which is the same inference problem, and its output is a good starting point for hand-writing a schema.
›Does oneOf tell me which branch matched, or why the others failed?
When more than one branch matches, the message names the matching indexes — "Matches 2 of the alternatives in oneOf (indexes 0, 2); exactly one is required" — since that is usually enough to see the overlap. When none match, the individual reasons are not surfaced, for the same reason as anyOf: with several branches you would get several sets of unrelated errors. Validate against one branch on its own to see why it failed.
›What happens if the JSON document itself has a syntax error?
Schema validation never runs on unparseable input. Each pane is parsed independently and a syntax error is shown with its line and column, the same way the JSON formatter reports one — you fix that first, and the schema check runs once both the schema and the data parse.
Last updated