The contract your API forgot to write

The endpoint shipped on a Friday. By the following Tuesday, three consumers had integrated against it, and by the Thursday after, someone's integration was broken because the `email` field came back `null` for one customer. The API documentation, such as it is, lives in a README that says the response "includes customer info." Nobody wrote down which fields exist, which are optional, what types they hold, or what shape the nested objects take. The API has a contract; it is just an oral tradition.

A JSON Schema is that contract, written down in a form both humans and machines can enforce. It states, for every object your system produces or accepts: which properties exist, which are required, what type each holds, how long strings may be, which values an enum permits, and what shape nested structures take. Given a schema, validators can reject malformed payloads at the edge, code generators can build types in a dozen languages, and documentation generates itself. Given only example responses, developers guess — and the guessing is where the null-email bug was born.

The JSON Schema Generator above bridges the gap in the practical direction: paste a real response, get a draft 2020-12 schema with inferred types, required lists, format hints, and nested definitions. It runs in your browser — which matters, because the JSON people paste into schema generators is overwhelmingly real production data. And in the last two years it acquired a second life nobody fully predicted: schemas are now how you constrain what an AI model is allowed to say, which is why this tool has an opinionated structured-output preset. Both stories are below.

From JSON to schema in five moves

  1. Paste your JSON

    Any valid document — an API response, a config file, a message payload. The parser validates syntax first; if a trailing comma sneaks in, the error message points at it before any generation happens.

  2. Let inference run

    The tool walks the document: objects become properties, arrays get item schemas, values determine types, and repeated array elements are merged into one combined shape rather than one schema per index.

  3. Choose your strictness

    Required-all (the safe contract default), optional-all (documentation flavor), or balanced — required for fields present across merged array samples, optional for ones that flicker. You can refine per field afterward.

  4. Pick a preset

    Standard draft 2020-12 for general use, or the structured-output preset for LLM function calling and JSON mode, which trims keywords that provider runtimes reject and defaults additionalProperties to false, the configuration models actually honor.

  5. Edit and validate

    The schema is editable, and your original JSON validates against it live. Add enums for closed sets, patterns for formats that matter, bounds on lengths. Copy the finished contract into your repo as a .schema.json file.

Draft 2020-12, keyword by keyword

The 2020-12 revision is the current anchor of a specification that has been evolving for over a decade, and its vocabulary is what every generated schema speaks. The keywords the generator emits, and what each asserts:

KeywordWhat it doesExample
$schemaDeclares which dialect this schema speaks"https://json-schema.org/draft/2020-12/schema"
typeThe JSON type a value must have"string", "number", "integer", "boolean", "object", "array", "null"
propertiesThe allowed members of an object{"name": {"type": "string"}}
requiredWhich properties must be present["id", "name"]
itemsSchema for array elements{"type": "object"}
prefixItemsSchemas for tuple-style leading positionsAn ordered list before items applies
enumThe closed set of permitted values["admin", "editor", "viewer"]
constExactly one permitted value"v2"
patternECMAScript regex a string must match"^[A-Z]{2}\\d{4}$"
minLength / maxLengthString length bounds8 / 64
minimum / maximumNumeric bounds0 / 100
formatA semantic annotation, not a default assertion"email", "date-time", "uuid"
additionalPropertiesWhether extra object members are allowedfalse for closed objects
$defs + $refReusable sub-schemas, referenced by pointer"$ref": "#/$defs/Customer"
The working vocabulary of a modern schema — about fifteen keywords covers the vast majority of real contracts.

Three of those keywords carry history worth knowing. `format` was demoted from assertion to annotation — a schema with `format: "email"` describes a string as an email without promising to reject non-emails, unless the validator opts into format checking explicitly. `definitions` became `$defs` in draft 2019-09, so older schemas you meet in the wild use the previous spelling and older validators expect it. And `items` changed semantics across the array-tuple boundary: pre-2019 schemas used an array form of items for tuples, which 2020-12 replaced with `prefixItems` plus a single-schema `items` for everything after. When a generated schema or a legacy one misbehaves in an old validator, the draft mismatch is the first suspect.

Composition keywords — `allOf`, `anyOf`, `oneOf`, `not` — are how schemas express or-logic and inheritance. `oneOf` is the strict sibling: exactly one branch must match, no more, which makes it the right tool for variant shapes (a response that is either a success or an error, never both) and the wrong one for overlapping merges. The generator reaches for these when a field's observed samples can't be unified under a single type — a field that is sometimes a string and sometimes `null` becomes `anyOf` with a `null` branch, which is more honest than pretending it was always a string.

A payload and its schema, line by line

Here is the full pipeline on a realistic customer object — the kind of shape an account service returns and an integrator curses at:

{
  "id": 42,
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "role": "admin",
  "tags": ["math", "pioneer"],
  "address": {
    "city": "London",
    "postcodes": ["W1", "SW1"]
  },
  "active": true
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "name": { "type": "string", "minLength": 1 },
    "email": { "type": "string", "format": "email" },
    "role": { "type": "string" },
    "tags": { "type": "array", "items": { "type": "string" } },
    "address": {
      "type": "object",
      "properties": {
        "city": { "type": "string" },
        "postcodes": { "type": "array", "items": { "type": "string" } }
      },
      "required": ["city", "postcodes"],
      "additionalProperties": false
    },
    "active": { "type": "boolean" }
  },
  "required": ["id", "name", "email", "role", "tags", "address", "active"],
  "additionalProperties": false
}

Read the inferences like a code review, because that is what they are. The `42` became `integer` — defensible, but widen it if the API ever serializes `42.0`. The email string picked up `format: "email"`, which is a human-facing annotation unless your validator opts into format assertions. The `role` field carrying the single observed value `"admin"` is the interesting one: one sample cannot distinguish a free-text field from a closed set, so the tool leaves it a plain string and flags it as an enum candidate. The day you see `"viewer"` in a second sample, the suggestion becomes `enum: ["admin", "viewer"]` — closed vocabularies are exactly what enums are for, and exactly what single-sample inference cannot decide alone.

The nested `address` object became an inline definition with its own `required` list, and `additionalProperties: false` closes both levels. That last keyword is a strictness decision with consequences: closed objects reject any field the schema doesn't name, which catches typos like `adress` at validation time but also breaks forward compatibility the day the API adds a legitimate new field. Open objects (omit the keyword, or set it true) tolerate additions and typos alike. The generated default is closed, because contracts are promises and open objects are vibes; loosen it when you want the schema to age more gracefully than the API.

What inference can and cannot know

A generator sees one document and must generalize to a population. The honest list of what that costs:

  • Required is a guess about intent. Present in the sample reads as required; optional fields that happened to appear look mandatory. Paste two or three representative samples, or hand-tune the required list after generating.
  • Integer versus number is one value's evidence. Widen `integer` to `number` whenever fractional values are conceivable — API clients that send `42.0` fail integer assertions on some validators.
  • Format hints are heuristics. `ada@example.com` suggests email; `2026-10-25` suggests date; `123e4567-e89b` suggests uuid. Suggestions, not guarantees — a string of digits could be a phone number, a zip code, or a badly-typed id.
  • Single observed values can't prove closed sets. The enum candidate problem from the worked example. Two samples with different values turn the suggestion concrete.
  • Arrays with one element flatten the shape. The generator merges multiple elements when present; a single-element array describes its only citizen, and a future heterogeneous array will surprise the schema.
  • Nullability is invisible until it happens. A field that is `null` for some records needs `anyOf` with `null` — invisible in a sample where it was always a string. Real-world payloads are full of these, and they are precisely the fields that break integrations.

The design answer to all six is the same: generation is a first draft, and the tool behaves like it. Editable schema, live validation against your sample, merge support for multiple samples, and visible reasoning about each inference. The failure mode we explicitly designed against is the one where a generated schema gets pasted into production unreviewed and treated as gospel — a schema is a contract someone must actually agree to, not a fact a machine discovered.

The AI-shaped reason schemas are booming

For fifteen years, JSON Schema lived a quiet professional life: API contracts, config validation, message queues. Then LLMs acquired structured output, and the specification's most obscure corner became mainstream plumbing. When you ask a model to answer in JSON rather than prose, the mechanism underneath is a schema: the API is told which properties to emit, their types, and which are required — and the provider's runtime steers generation toward conforming output.

The catch is that providers implement subsets of the specification, not the whole of it. The widely supported core is `type`, `properties`, `required`, `enum`, and array `items`; beyond that the ground shifts — `additionalProperties: false` is enforced by some runtimes and ignored by others, `$ref` support varies from full to absent, and the composition keywords are the most unevenly implemented of all. The generator's structured-output preset exists for exactly this: it emits the conservative core, closes objects, and stays inside the envelope the major providers document, so a schema that validates on the page also behaves at the API.

The workflow this enables has become standard practice for anyone building on LLMs: paste the exact response shape you want, generate, trim with the preset, test against the provider, iterate. Compared to hand-writing schemas — which nobody enjoys and everyone gets subtly wrong on the first attempt — generation plus a validation loop turns the fiddliest part of structured-output integration into a two-minute task. The schema you generate for a model today and the schema you generate for an API tomorrow are the same artifact speaking the same dialect; only the enforcement point differs.

Who writes schemas, and on which days

Schema-first workflows have quietly spread across most teams that move data. The populations, and the moments they reach for generation:

API designers draft the contract before or alongside the endpoint — the schema becomes the source of truth that documentation, mock servers, and contract tests all read from. Backend integrators do the reverse-engineering direction: they have a response from someone else's undocumented API and need the shape as a schema to validate against or to generate types from. Data engineers pin down event payloads before they land in a warehouse — a schema at the boundary turns downstream type errors into boundary rejections. LLM application developers, as the last section said, are the newest and fastest-growing group. And QA engineers use schemas as machine-checkable expectations in test suites: instead of asserting that a response contains an `email` string somewhere, they assert it validates against the contract, which catches missing fields, wrong types, and surprise additions in a single check.

The when is whenever a payload's shape stops being tribal knowledge — a new endpoint, a new integration, a new data pipeline, a migration to structured model output. That moment arrives weekly at most companies, which is why schema generation sits among the most steadily used developer utilities on the web rather than spiking with any season.

JSON Schema vs. TypeScript vs. Zod

ApproachEnforced whenPortable toBest at
JSON Schema (draft 2020-12)Runtime, by any validator in any languageEverywhere — it is the lingua francaContracts across language boundaries, AI structured output, config validation
TypeScript typesCompile time, in the TypeScript toolchainTS projects onlyIn-editor safety while you write application code
Zod schemasRuntime, in JavaScript/TypeScriptJS runtimes; can export to JSON SchemaColocating parse-and-validate at the edge of TS code
Three tools, one problem — the differences are enforcement time and portability.

The distinction that actually matters is the boundary. TypeScript types vanish at compile time — perfect for catching your own mistakes inside your own code, invisible to a Python consumer of your API or to an LLM runtime constraining a response. Zod runs at the boundary but lives inside the JavaScript world, which is why its own ecosystem grew a JSON Schema exporter for exactly the interop cases. JSON Schema's superpower is being nobody's favorite language's native feature: it is the neutral contract format that every ecosystem learned to speak, the way HTTP headers are nobody's framework and everybody's protocol. The JSON to TypeScript Interface tool covers the compile-time side, this one the contract side, and mature teams frequently use both on the same payload.

Schemas are built on your machine

Parsing, inference, and generation are all pure client-side computation. The JSON you paste stays in your browser's memory; the schema is assembled there; the validation view runs there. No upload, no history, no account, and no server that has ever seen your payload.

For this tool the stakes are unusually concrete. The JSON people bring to schema generators is rarely synthetic — it is a response captured from production, often containing real customer records, sometimes containing exactly the personal data a privacy policy promised to protect. A server-side generator would be building a database of production samples. The client-side design keeps the tool useful in the exact situations where it is most sensitive: pasting a payload from a system whose compliance posture you do not control, working on an air-gapped machine, or generating a contract for data you are not free to share with a third-party website. Architecture as the privacy policy — the same design we apply across every data tool on this site, from the JSON Formatter & Validator that likely brought your payload here in the first place to the converters in this cluster.

Validating against the schema

A schema nobody enforces is documentation with attitude. The live validation view on this page checks your sample against your edited schema continuously, and that loop — generate, edit, watch validation, repeat — is the fastest way to learn the vocabulary, because every keyword you add either accepts your sample (contract holds) or lights up an error line (you just described something your data is not).

In production, the same job belongs to your platform's validator. For JavaScript and TypeScript, Ajv compiles schemas into fast validation functions and supports draft 2020-12 natively, with strict mode available when you want format assertions enforced. Python's jsonschema library validates the same drafts. .NET, Java, and Go each have mature implementations. The one piece of advice worth carrying into that work: pin the draft. Declare it with $schema on every file, match your validator version to it, and the ecosystem's historical drift — definitions versus $defs, items versus prefixItems — stays a Wikipedia footnote instead of a production incident.

Schema questions, fielded

Which JSON Schema draft does this generator output?

Draft 2020-12, the most recent published revision of the specification. The output declares it via the $schema keyword: https://json-schema.org/draft/2020-12/schema. Draft 2020-12 is the version current validators target natively, and it is the closest thing the ecosystem has to a stable baseline — earlier drafts (07, 2019-09) still exist in the wild and use slightly different keywords.

Why does the generator mark every property as required?

Because your sample JSON contains every property, and a generator only knows what it saw. Marking observed properties required is the safe default for a contract: if the field was present in the example, downstream code probably depends on it. The correct fix is judgment you apply after generation — demote the genuinely optional fields to non-required by removing them from the required array, which stays valid either way.

Does the format keyword like format: email actually validate anything?

Not by default. Since draft 2019-09, format is formally an annotation, not an assertion — a validator is not required to check that a string is a real email address, and many don't. If you need format enforcement, enable it explicitly in your validator (Ajv's strict mode option, for instance) or enforce it with pattern. The generator emits format as a hint to humans and tooling, which is what the specification intends.

Can I validate my data against the schema right here?

Yes. The generator pairs with a live validator view that runs your original JSON against the generated schema after every edit, so you can watch a tweak to required or type either pass or light up errors. For serious validation in production, Ajv for JavaScript, jsonschema for Python, and the JSON Schema support in .NET and Java all handle draft 2020-12.

Do AI APIs accept full JSON Schema?

They accept a subset. Structured-output and function-calling features across major LLM providers accept JSON Schema shapes — type, properties, required, enum, and array keywords — but ignore or reject keywords like $ref in some positions, additionalProperties handling varies, and each provider documents its own supported subset. The generator has a structured-output preset that trims to the widely accepted core, and the golden rule is: test the schema against the provider, not against the specification.

Why did my number become type integer instead of number?

Because the sample value was 42 — no decimal point, no exponent. Inference maps JSON's number type by observation: a value like 42 becomes integer, a value like 19.5 becomes number. Both are valid JSON numbers, and if your API can send 42.0 some days, widen the type to number by hand after generating. A single sample is evidence, not proof, and this is the smallest of the places where that distinction shows.

Is my JSON uploaded to a server?

No. Parsing, inference, and schema generation run entirely in your browser. Your payload — which is quite often real customer data — never leaves the page: no upload, no history, no account. That architectural choice is standard across this site's data tools, and it matters most exactly here, where the input is so often a production sample.

Can I edit the schema after generating it?

That is the intended workflow. The generator produces a correct, validated starting point; the schema view is an editable document, and the validator re-runs as you type. Tighten it (enum values, length bounds, patterns), loosen it (optional fields), or restructure it ($defs for shared shapes) — generation is the first draft, not the contract signing.

Sibling tools in the same toolbox

Schema generation sits in the middle of a small pipeline of JSON-shape tools, and the neighbors are worth knowing. The JSON to C# Class Converter turns the same payload into .NET types for the consuming side of an API. The JSON to TypeScript Interface does the same for the TypeScript world — and as the comparison above explains, the interface and the schema are complementary artifacts, not substitutes. And before any of this works, the JSON Formatter & Validator is the tool that tells you the payload you copied was never valid JSON in the first place, usually with a more useful error message than your application logs.

One payload, four tools: validate it, schema it, interface it, class it. Teams that do this once per API stop arguing about shapes and start enforcing them.

Normative references

The specification is public, readable, and shorter than its reputation suggests. These are the documents this generator implements:

Sources and further reading

We cite primary sources (standards bodies, government agencies, official documentation) wherever they exist so you can verify every number on this page yourself.