How to validate JSON against schema
Run ajv validate --spec=draft2020 -c ajv-formats -s user.schema.json -d user.json. A document that conforms prints user.json valid and exits 0. A document that breaks the schema prints one entry per violation, each carrying an instancePath that points at the offending field, and exits 1.
Why check this
A field that changes type from integer to string breaks every typed client that reads it, and no status code reports that. Schema validation turns the contract into a test you can run in regression, on every build, and again after a database migration renames a column. Run it on the payload you captured, before anyone argues about whose side is wrong.
Prerequisites
- Node 18 or later.
npxdownloads the validator on first use and caches it. - ajv-cli 5.0.0 and ajv-formats 3.0.1. Both go into the same
npxcall, because ajv-cli does not bundle the format vocabulary. - The JSON Schema 2020-12 specification for keyword meanings.
- Three files in the working directory. The schema,
user.schema.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "email", "createdAt"],
"additionalProperties": false,
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"createdAt": { "type": "string", "format": "date-time" },
"roles": {
"type": "array",
"minItems": 1,
"items": { "enum": ["admin", "editor", "viewer"] }
}
}
}
A payload that conforms, user.json:
{
"id": 42,
"email": "tester@example.com",
"createdAt": "2026-09-11T08:30:00Z",
"roles": ["editor"]
}
And the payload under test, user-bad.json:
{
"id": "42",
"email": "tester@example.com",
"roles": ["owner"],
"nickname": "t"
}
Steps
- Step 1.
Compile the schema on its own, before any data touches it.
npx --yes -p ajv-cli@5.0.0 -p ajv-formats@3.0.1 ajv compile --spec=draft2020 -c ajv-formats -s user.schema.jsonschema user.schema.json is validA schema that fails here makes every later result meaningless, so this line comes first.
- Step 2.
Validate the payload that is supposed to conform.
npx --yes -p ajv-cli@5.0.0 -p ajv-formats@3.0.1 ajv validate --spec=draft2020 -c ajv-formats -s user.schema.json -d user.jsonuser.json valid - Step 3.
Validate the payload under test with
--all-errors, so the run reports every violation instead of stopping at the first.npx --yes -p ajv-cli@5.0.0 -p ajv-formats@3.0.1 ajv validate --spec=draft2020 -c ajv-formats --all-errors -s user.schema.json -d user-bad.jsonuser-bad.json invalid [ { instancePath: '', schemaPath: '#/required', keyword: 'required', params: { missingProperty: 'createdAt' }, message: "must have required property 'createdAt'" }, { instancePath: '', schemaPath: '#/additionalProperties', keyword: 'additionalProperties', params: { additionalProperty: 'nickname' }, message: 'must NOT have additional properties' }, { instancePath: '/id', schemaPath: '#/properties/id/type', keyword: 'type', params: { type: 'integer' }, message: 'must be integer' }, { instancePath: '/roles/0', schemaPath: '#/properties/roles/items/enum', keyword: 'enum', params: { allowedValues: [Array] }, message: 'must be equal to one of the allowed values' } ]Read
instancePathas a pointer into the data andschemaPathas a pointer into the schema. The first two entries carry an emptyinstancePathbecause the fault is the object itself, not one of its fields. - Step 4.
Read the exit code, which is what a pipeline reacts to.
echo $?1
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| user.json valid, exit 0 | Every keyword in the schema held | Nothing. Keep the command in the pipeline. |
| keyword: 'type' at /id | The field is there with the wrong JSON type | Decide which side is wrong. A quoted number is usually a serialiser default. |
| keyword: 'required' with an empty instancePath | A mandatory field is absent from the payload | Treat it as a contract break, not as a null value. |
| keyword: 'additionalProperties' | The payload carries a field the schema does not list | Either the API grew a field or the schema went stale. |
| schema ... is invalid | The schema does not compile | Fix the schema. Any data result is unusable until it does. |
Common mistakes
What to check next
- How to check if API returns valid JSON: run this first, because an unparsable body never reaches the schema.
- How to check content-type of API response: the header that decides whether a client tries to parse at all.
- How to validate OpenAPI spec: where the schema comes from when the API is described rather than sampled.
- How to test API error responses: error bodies need their own schema, and they rarely have one.
- Json schema validator online: paste a schema and a payload without installing anything.
FAQ
How to validate a JSON schema itself?
Use ajv compile with no -d flag, as in step 1. It reports unknown keywords, unresolved $ref targets and unknown formats. A schema that compiles is well formed. Whether it describes the right contract is a review question, not a tool question.
How to validate a JSON response from an API?
Save the body to a file first, then point -d at it. Validating a piped stream hides which bytes failed. Capturing the body also gives you an artefact to attach to the bug report when the schema rejects it.
How to validate a JSON schema in Postman?
Postman runs ajv inside a test script: pm.response.to.have.jsonSchema(schema). It defaults to draft-07, so a $schema line naming 2020-12 is ignored and keywords such as prefixItems stop working. Keep one schema file and run the CLI in CI.
How to validate JSON against an OpenAPI schema?
Copy the schema object out of components/schemas and feed it to ajv. OpenAPI 3.1 schemas are JSON Schema 2020-12, so they transfer without edits. OpenAPI 3.0 schemas use a dialect with nullable, which ajv rejects until that keyword is removed.
Verified
Verified by Maks Vernyajv-cli 5.0.0ajv-formats 3.0.1node 22.23.2
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
- Checker: json-schema validate JSON against provided schema
- API testing checklist
- All api checks checks
intermediate6 minpublished updated Maks Verny