Command Palette

Search for a command to run...

JSON Schema Generator: Turn a JSON Sample Into a Validatable Schema

JSON Schema Generator: Turn a JSON Sample Into a Validatable Schema

T
Toolz Team
|Aug 23, 2026|17 min read

Part of the Data Tools collection

I have shipped enough APIs to know the exact moment a project needs a JSON Schema. It is never at the start. It is three weeks in, when a second team starts consuming your endpoint, someone sends a malformed request body, and a null slips into a field everyone assumed was always a string. Suddenly you need a contract - a document that says, in a form a machine can enforce, "this is what a valid payload looks like." That document is a JSON Schema, and writing one by hand from an endpoint that already returns real data is one of the more tedious jobs in backend work.

TL;DR: Paste a JSON sample into the JSON Schema Generator, pick Draft-07 or 2020-12, and it infers a schema - types, required fields, merged array items, and string formats like date-time and uuid. It runs entirely in your browser, so payloads carrying tokens and personal data never leave the page. Treat the output as a strong first draft, then tighten it with the constraints only you know.

I built this tool for toolz.dev because I kept doing the same thing by hand: opening a response body, squinting at it, and transcribing its shape into a schema clause by clause. It is repetitive, and repetitive transcription is where mistakes hide. This guide explains what the generator does, where inference is reliable, where it needs your judgment, and how a generated schema fits into a real validation workflow.

What is a JSON Schema, and why generate one from data?

JSON Schema is a vocabulary for describing the structure of JSON, maintained as a specification in its own right rather than as a convention. A schema is itself a JSON document that declares the expected type of each field, which fields are required, what shape nested objects and arrays take, and - with keywords like pattern, enum, minimum, and format - what values are actually allowed. Validators in nearly every language read a schema and tell you whether a given document conforms. It is the closest thing the JSON world has to a type system that travels across service boundaries.

The reason to generate a schema from a sample, rather than write it from scratch, is that most of a schema is mechanical. Walking a payload and recording "this is a string, this is an integer, this object has these keys" is exactly the kind of work a machine should do. What is not mechanical is the semantic layer: knowing that status may only be one of four strings, that age cannot be negative, that email must match a real address pattern. Generation handles the mechanical scaffolding so you can spend your attention on the constraints that matter. You start from a document that already matches reality and add rules, instead of starting from a blank file and hoping you remembered every field.

There is a trust dimension too. When you type a schema by hand, you encode what you believe the endpoint returns. Beliefs drift from reality - a field gets added, an integer becomes nullable, an endpoint that returned a single object starts returning an array. A schema generated from an actual response is anchored to what the service genuinely sent on the day you captured it. That anchor is worth a great deal when you are debugging why validation passes in staging and fails in production.

How the generator infers a schema

The engine parses your JSON and walks the value recursively, emitting a schema node for every part of the structure. The rules are deliberately conservative, because a schema that is too loose is useless and a schema that is too strict rejects valid data.

For scalars, it distinguishes integer from number - 42 becomes integer, 4.2 becomes number - because that distinction is meaningful to validators and to anyone reading the schema. Booleans and null map to their own types. Strings become type: string, and if format detection is on, the engine checks the value against a set of well-known patterns and tags it: date-time, date, time, email, uri, uuid, and ipv4.

For objects, it records every key, infers a schema for each value, and - if required inference is enabled - marks a key required when it is present in every object at that position. For a single object that means all keys; the interesting case is arrays.

For arrays of objects, the generator does something more useful than a naive walk. Instead of emitting a separate schema for each element or a sprawling anyOf of near-identical shapes, it merges all the objects in the array into one items schema that describes a single element. A key present in every element is required; a key present in only some elements is left optional. This mirrors how real API collections behave: a paginated list where most records carry an avatarUrl but a few do not. The merged schema captures "these fields always appear, these sometimes appear" in one readable definition. You can see this in the built-in sample, where the members array has two objects - one with an active field and one without - and the generated item schema marks id and role required but leaves active optional.

For arrays of mixed scalars, the engine collapses the element types into a single type array - ["integer", "string", "boolean"] - rather than a verbose union. When object and non-object shapes genuinely mix in one array, it falls back to anyOf, which is the correct JSON Schema construct for "one of these alternatives."

How to use the JSON Schema Generator

Step 1: Paste a representative sample

Drop in an API response, a fixture, a config file, or a webhook body. The single most important thing you can do for accuracy is to paste a representative sample. If you have several records from a real response, include them all inside an array - the generator will merge them and infer optionality correctly. A one-record sample tells the engine that every field it sees is always present, which is often wrong. Load the built-in sample first to see how nested objects, arrays of objects, and formatted strings are handled before you paste your own.

Step 2: Choose the dialect

Pick Draft-07 for the widest compatibility across validation libraries, or 2020-12 for the current specification. The tool writes the correct $schema identifier onto the root so your validator applies the right rules. For the object and array shapes this generator produces, the structural output is the same across both dialects; the visible difference is the identifier. If you are unsure which your tooling supports, Draft-07 is the safe default - it has the broadest library support of any version.

Step 3: Set your options

Add a title if you want the schema self-documenting. Decide whether to emit required - most of the time you want it, but during early exploration you may prefer a looser schema. Keep format detection on unless you are seeing false positives. And turn on strict mode (additionalProperties: false) when the schema guards something you fully control, like a config file or a request body, and you want unexpected keys rejected rather than ignored.

Step 4: Generate, review, and export

Press Generate, then read the output critically. Check that required matches your intent, that integer-versus-number came out right, and that any detected formats are correct rather than coincidental. When it looks right, copy the schema or download it as a .json file ready to drop into your validator or repository.

A worked example

Consider this response from a hypothetical /projects endpoint:

{
  "id": "5b2a1f6e-8c3d-4a1b-9f7e-2c1d3e4f5a6b",
  "name": "Toolz",
  "createdAt": "2026-01-14T09:30:00Z",
  "score": 4.8,
  "members": [
    { "id": 1, "role": "owner", "active": true },
    { "id": 2, "role": "editor" }
  ]
}

The generator produces a schema where id is a string with format: "uuid", createdAt is a string with format: "date-time", score is a number (not an integer, because of the decimal), and members is an array whose items schema requires id and role but not active. That last detail is the payoff: from two example members it correctly inferred that active is optional. Doing that reasoning by hand across a large payload is exactly the kind of careful, boring work that a generator removes.

Where inference ends and your judgment begins

I want to be direct about the limits, because a generated schema handed straight to production is a mistake. Inference sees types and structure; it cannot see intent.

It cannot know that role is an enum of owner, editor, and viewer - from the sample it only knows role is a string. It cannot know that score ranges from 0 to 5, that name has a maximum length, or that a code which happens to look like a UUID is actually an opaque identifier that should stay a plain string. It infers required from presence, so an optional field that happens to appear in your sample will be marked required until you correct it. And it works from the data you give it: if your sample never includes a null for a nullable field, the schema will not know that field can be null.

The right mental model is scaffolding. The generator builds the frame accurately - every field, its type, the nesting, the array shapes, the presence-based required list. You then add the semantic constraints: enums, patterns, numeric bounds, and any formats the engine could not see from one value. This is faster and less error-prone than starting from nothing, because the tedious structural transcription is already done and correct.

Draft-07 versus 2020-12: which should you pick?

Consideration Draft-07 2020-12
Library support Broadest; supported almost everywhere Growing; check your validator
Status Widely deployed, stable Current specification
$schema value http://json-schema.org/draft-07/schema# https://json-schema.org/draft/2020-12/schema
Array item keywords items for single-item schemas items / prefixItems split for tuples
Best when Maximum compatibility matters You want the newest spec features

For the schemas this tool generates - objects, required lists, arrays of a single item shape - both dialects express the same structure. The practical decision comes down to what your validation library supports. If you are wiring the schema into an established stack, match the version your validator documents. If you are starting fresh and have no constraint, Draft-07 remains the pragmatic choice for its unmatched ecosystem support.

Common use cases

Documenting an existing API. When you inherit an endpoint with no schema, generating one from a real response gives you an accurate starting document in seconds. You then refine it into a published contract. This pairs naturally with generating types for your client code - the same sample can feed the JSON to TypeScript tool so your server contract and client types come from the same source of truth.

Validating request bodies. For a request body you control, generate a schema from a valid example, turn on strict mode to reject unexpected keys, and add the enums and bounds your endpoint enforces. Now malformed requests fail at the edge with a clear validation error instead of causing confusing failures deep in your handler.

Config-file validation. Applications that read JSON config benefit enormously from a schema. Generate one from a known-good config, tighten it, and validate on startup so a typo in a config key fails loudly instead of silently disabling a feature.

Testing and fixtures. A schema doubles as a test asset. Validate your fixtures against it in CI so a fixture that drifts out of shape is caught before it produces a misleading green test.

Contract testing between services. When two services agree on a payload, a shared schema is the contract. Generating it from a real message and refining it gives both teams a document they can validate against independently.

Privacy: why this runs in your browser

API samples are some of the most sensitive text a developer handles. They routinely contain access tokens, session identifiers, email addresses, internal record IDs, and occasionally personal data that should never be pasted into a random web form. That is precisely why the JSON Schema Generator does all of its work client-side. The parsing, inference, and serialization happen in JavaScript in your browser. Nothing is uploaded, logged, or stored on a server. You can verify this by opening your network tab while you generate, or by disconnecting from the internet - the tool still works. I care about this because I would not use a tool that shipped my payloads to someone else's server, and I would not ask you to either. The same principle runs through the whole of toolz.dev, which is the argument I make at length in the data privacy in online tools write-up.

How it fits the wider JSON toolkit

A schema is one artifact in a larger JSON workflow. Before you generate a schema, it helps to have clean, valid input - the JSON Formatter will format and validate a payload so you are not feeding malformed text into the generator. After you have a schema, you often want types for your application code, which is where JSON to TypeScript comes in. And if your pipeline moves between formats, the JSON to YAML converter handles the conversion many config and CI systems expect. I have written about how these pieces connect in the ultimate guide to JSON tools, and about assembling a broader kit in the web developer toolkit overview. The point of a connected toolkit is that a single sample can flow through several tools - schema, types, format conversion - without ever leaving your browser.

FAQ

How do I generate a JSON Schema from JSON?

Paste your JSON into the editor, pick Draft-07 or 2020-12, and press Generate. The tool infers the type of every field, extracts the required keys, and outputs a schema you can copy straight into a validator. Nothing is uploaded - inference runs entirely in your browser.

What is the difference between Draft-07 and 2020-12?

They are two versions of the JSON Schema specification. Draft-07 has the widest support across libraries and is a safe default. 2020-12 is the current release and changes how arrays and sub-schemas are expressed, among other things. For the object and array shapes this tool produces the structure is the same; the main visible difference is the $schema identifier.

How does the tool decide which fields are required?

A key is marked required when it appears in every object the generator sees. For a single object that means every key; for an array of objects it means keys present in all elements. Keys that appear in only some records are left out of required, mirroring how APIs omit optional fields. You can turn required-field detection off entirely.

What happens with an array of objects?

The objects are merged into one items schema describing a single element, and the property is typed as an array of it. Keys present in every element become required; keys present in only some stay optional. This keeps the schema readable instead of producing a large anyOf of near-identical shapes.

Which string formats does it detect?

It recognises date-time, date, time, email, uri, uuid, and ipv4 strings and adds the matching format keyword. Detection is best-effort from a single sample, so review the results - a code that happens to look like a UUID will be tagged as one. You can disable format detection if you prefer plain string types.

Can I generate a schema from a single sample?

Yes, but one sample only shows one possible shape. A field that is a number in your sample might be null or a string elsewhere, and an optional field that happens to be present will be marked required. The more representative the sample - ideally several real records - the more accurate the inferred types and required list.

Is a generated schema ready for production validation?

Treat it as a strong starting point rather than a finished document. Inference captures types, structure, and required fields accurately, but semantic constraints - enums, string patterns, numeric minimums and maximums, formats it cannot see from one value - still need to be added by hand. Generating removes the tedious scaffolding so you can focus on those rules.

Is my JSON uploaded to a server?

No. The entire inference engine runs as JavaScript in your browser. Nothing is transmitted, logged, or stored. You can confirm this by watching your network tab while you generate, or by disconnecting from the internet - the tool still works.


Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!