Skip to content
FormatKit

Free online JSON Schema validator

JSON Schema validator that lists every failure

This JSON Schema validator is free and unregistered: paste a document and a draft 2020-12 schema and every mismatch comes back at once, each with a JSON Pointer to the node that failed and a second pointer to the rule inside your schema that rejected it. It implements 35 keywords — prefixItems, patternProperties, oneOf, if/then/else and $ref into local $defs among them — and names the ones it does not, because a keyword silently skipped turns a pass into a lie. Checking a 1.6 MB document against a referenced schema takes about 90 milliseconds, in your own browser.

  • 100% free
  • No signup
  • Draft 2020-12
  • 35 keywords
  • A pointer on every failure

Left: the document. Right: the schema it is supposed to match.

Paste a document on the left and a schema on the right — or load the sample pair, which fails in four different ways on purpose.

Keywords this validator applies35 of draft 2020-12, plus boolean schemas

Type and value
typeenumconst
Numbers
minimummaximumexclusiveMinimumexclusiveMaximummultipleOf
Strings
minLengthmaxLengthpattern
Objects
propertiespatternPropertiesadditionalPropertiespropertyNamesrequireddependentRequireddependentSchemasminPropertiesmaxProperties
Arrays
prefixItemsitemsminItemsmaxItemsuniqueItems
Combining
allOfanyOfoneOfnotifthenelse
Structure
$ref$defsdefinitionstrue / false schemas

Refused, and why

  • unevaluatedProperties needs the annotation results of every applicator that ran; approximating it produces wrong verdicts on allOf
  • unevaluatedItems same annotation problem as unevaluatedProperties, in the array direction
  • contains not implemented, along with minContains and maxContains
  • $dynamicRef dynamic scope resolution, and nothing that needs it fits on one page
  • $dynamicAnchor only meaningful with $dynamicRef
  • $anchor plain-name fragments are not resolved; use a JSON Pointer such as #/$defs/Address
  • contentSchema requires decoding the string content first, which this validator does not do

How to validate JSON against a schema

Two panes, one verdict, and an address for everything that went wrong.

  1. Put the document on the left and the schema on the right

    Paste both, or open them from disk with the buttons above each pane. Either side can be up to 5 MB. If you have no schema yet, press Sample pair for an order payload and a draft 2020-12 schema that it fails in four different ways on purpose.

  2. Read the failures top to bottom

    Every one carries a JSON Pointer to the offending node — /lines/1/qty, not “an item in an array” — the keyword that rejected it, and a sentence saying what was expected and what arrived. Beside each is the pointer of the rule inside your schema, so #/$defs/Line/properties/qty/minimum takes you straight to the line to argue with.

  3. Fix one side or the other

    The failure list copies as tab-separated text for a ticket or a code review. Watch the notes under it too: they report a $ref that could not be resolved, a keyword this validator does not implement, and duplicate keys in the document, all of which change what the verdict actually means.

Technical specifications

Draft2020-12. Earlier drafts largely work as written: definitions resolves like $defs, and an items array is applied as draft-07 tuple form with a note explaining that 2020-12 calls it prefixItems
Keywords applied35, plus boolean schemas — type, enum, const, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, minLength, maxLength, pattern, properties, patternProperties, additionalProperties, propertyNames, required, dependentRequired, dependentSchemas, minProperties, maxProperties, prefixItems, items, minItems, maxItems, uniqueItems, allOf, anyOf, oneOf, not, if, then, else, $ref, $defs and definitions
Keywords refusedunevaluatedProperties, unevaluatedItems, contains with minContains and maxContains, $dynamicRef, $dynamicAnchor, $anchor and contentSchema. They are listed in the results when your schema uses one, because a skipped keyword changes what a pass means
Annotations read but never assertedformat, title, description, default, examples, deprecated, readOnly, contentMediaType and contentEncoding — the format-annotation vocabulary's default behaviour
ReferencesSame-document JSON Pointers only: #, #/$defs/Name, #/definitions/Name and any pointer into the schema. A reference that does not resolve is reported as a failure, never skipped
Failure reportEvery failure, not the first: a JSON Pointer (RFC 6901) into the document, the keyword, a sentence in plain English, and the pointer of the rule inside your schema. Capped at 500
Measured throughputA 1.6 MB array of 10,000 records against a schema with $ref, enum, pattern and additionalProperties: 140,001 nodes checked in about 90 milliseconds
Comparison rules and priceNumbers as IEEE-754 doubles, string lengths in Unicode code points, enum and const by structural equality so 1 equals 1.0 and key order is ignored. Free, no signup, and both files stay in your browser

Frequently asked questions

My JSON is valid but the validator says it fails — how?

Valid and correct are different questions, and each has its own tool. Parsing decides whether the text is JSON at all; a schema decides whether the resulting value is the thing you agreed to exchange — that total is a number rather than the string "12.50", that status is one of four spellings rather than any word at all, that a line item cannot have a quantity of zero. A payload can be flawless JSON and wrong for its purpose in a dozen ways, which is exactly the gap a schema exists to close.

Which draft of JSON Schema does this validate against?

Draft 2020-12, the current one. Four of its changes matter in daily use: tuple validation moved out of items into prefixItems, so items now means “every remaining element” and nothing else; definitions became $defs; $ref may sit alongside other keywords instead of replacing them, which is why { "$ref": "#/$defs/Money", "minimum": 0 } now works as written; and $recursiveRef gave way to $dynamicRef. A draft-07 schema mostly runs here unchanged — an items array is accepted as tuple form with a note, and definitions resolves the same as $defs.

Why does my pattern match text in the middle of the string?

Because pattern is not anchored. JSON Schema borrows ECMA-262 regular expressions and applies them as a search, so "pattern": "[0-9]{3}" is satisfied by "abc123def" — the three digits are in there somewhere. Anchor it yourself with ^ and $ when you mean the whole string. The related trap is that ^ and $ match around line breaks only with the m flag, which a schema has no way to set, so a multi-line string cannot sneak past an anchored pattern.

Does "format": "email" actually reject a bad address here?

No, and that is the specification's default rather than a shortcut. In draft 2020-12 format belongs to the format-annotation vocabulary, where it describes a value without asserting anything; asserting it is opt-in behaviour that a validator may offer and many do not. So date-time, email, uri and uuid are read and shown here, never enforced. If a format has to hold, back it with a pattern as well, and remember that the two most-requested formats — email and uri — have grammars no sane regular expression captures in full.

Why is additionalProperties: false not catching the extra key inside my allOf?

Because additionalProperties only sees the properties declared in the same schema object it appears in, and allOf puts them in different objects. A base schema listing three properties combined through allOf with an extension listing two more leaves each half unaware of the other's names, so additionalProperties: false in the base rejects the extension's own keys. This is the best-known sharp edge in the format, and draft 2019-09 added unevaluatedProperties precisely to solve it — a keyword this validator refuses rather than approximates, because getting it wrong would produce confidently incorrect verdicts.

Does "type": "integer" reject 1.0?

No — 1.0 passes, and 1.5 fails. Since draft 6 the integer type means a number with a zero fractional part rather than a number written without a decimal point, so 1.0 and 1e2 both qualify. That is deliberate: JSON has one number type and languages disagree about how to write a whole one, so testing the value rather than the spelling is the only portable rule. Numbers are compared here as IEEE-754 doubles, the same as ajv, which means a 20-digit ID is compared as its rounded value even though the report prints the literal you supplied.

Can it follow a $ref to another file or a schema URL?

No, and a reference it cannot follow is reported as a failure rather than quietly skipped. Everything here runs in the browser with no server behind it, so there is nothing to fetch https://example.com/schemas/order.json with; only same-document JSON Pointers such as #/$defs/Address, #/definitions/Address and # itself resolve. Paste the referenced schema into $defs and repoint the $ref at it. Marking an unresolvable reference as passing would be the worst of the available answers — a document reported clean against a rule that never ran.

About validating JSON against a schema

Two questions get asked of the same payload and only one of them is about JSON. The first is whether the bytes parse, which the JSON checker answers from the grammar alone. The second is whether the value that comes out is the thing two systems agreed to exchange — an object with a total that is a number above zero, a status drawn from four permitted spellings, at least one line item, and no properties nobody planned for. Nothing in the JSON specification has an opinion about any of that; it is all contract, and a JSON Schema is where the contract is written down in a form a machine can check. In practice the two run in sequence, since nothing that fails to parse can be measured against a shape.

Draft 2020-12 is the version to write new schemas in, and the changes from draft-07 are the ones that trip people up when they copy an old file. Tuple validation left items for prefixItems, so an items array is now legacy syntax rather than current spelling. definitions became $defs and got a formal place in the specification. $ref stopped replacing its siblings and became an ordinary keyword you can put constraints beside, which removed the most common reason for wrapping everything in allOf. And the keyword set was carved into vocabularies, which is how format ended up as an annotation that describes a value rather than an assertion that constrains it — a detail worth knowing before you rely on "format": "email" to keep bad addresses out of a database.

The reason every failure here carries a JSON Pointer is that anything vaguer is unusable on real data. RFC 6901 defines the notation — a slash-separated path where ~1 stands for a literal slash inside a key and ~0 for a tilde — and it is the same notation JSON Patch and OpenAPI use, so the address you get back is one you can paste into other tools. Reporting only the first failure, which many validators do, means running the loop once per problem on a document that may have forty; the list here is complete and in document order, capped at 500 on the assumption that anything beyond that is a mismatched pairing rather than a bug hunt. If you have the payload but no schema to check it against yet, the schema generator will infer a first draft from a sample and leave you tightening it rather than starting from an empty file.

Where the document and the schema go

Nowhere. Both panes are read by JavaScript inside this tab and the validator runs there too, so an internal schema and a production payload can be checked against each other without either being uploaded. It is also why remote $ref resolution is not on offer: fetching a schema URL would mean sending part of the problem somewhere else.