JSON to Zod schema
Paste a JSON response and the Zod schema for it appears as you type, one schema per object shape, each declared before the schemas that use it. Nothing you paste leaves this tab.
How to use it#
- Paste a JSON sample into the box above, such as a response from the API you are calling.
- Name the top-level schema. Each schema is that name followed by Schema.
- Keep inferred types to get a TypeScript type beside each schema, and tick semicolons if your code uses them.
- Copy the schema from beside the result, or download it as a .ts file.
Why a schema rather than an interface#
A TypeScript interface is checked when the code compiles and then disappears. If an API sends something different from what the interface says, nothing notices until a value is used and turns out to be missing. A Zod schema is checked when the data arrives: RootSchema.parse(response) either returns the data, typed, or throws an error naming the field that did not match. safeParse runs the same check and returns the result instead of throwing, so there is nothing to catch.
The inferred types come from the schema itself, through z.infer, so the type and the check cannot drift apart. That is the reason to start from a schema when the JSON comes from somewhere you do not control. For compile time types only, JSON to TypeScript writes plain interfaces from the same sample.
How the schema is worked out#
Text becomes z.string(), numbers z.number(), true and false z.boolean(), and null z.null(). An object becomes a z.object schema of its own, named after the key it sits under, and an array becomes z.array of whatever it holds. Objects with the same keys and types share one schema.
Across the elements of an array, a key some objects lack comes out with .optional(), a value that is null in some and set in others comes out with .nullable(), and a place that holds two kinds of value becomes a z.union. Every schema is declared after the schemas it uses, because a const cannot be read before its line runs.
An empty array becomes z.array(z.unknown()), and an object with no keys becomes z.record(z.string(), z.unknown()). The two argument form of z.record works in both Zod 3 and Zod 4, as does everything else the output uses.
Tightening what a sample cannot show#
A sample shows what values looked like, not what the API allows. An email address comes out as z.string() and an ID as z.number(), and both can be tightened by hand: z.string().email(), z.number().int().positive(). A timestamp such as 2026-09-28T10:00:00Z can be checked with z.string().datetime(), or turned into a Date with z.coerce.date(). A field that was null throughout the sample is z.null(), which will reject the real value when one arrives, so change it to the type it should hold with .nullable() after it.
Zod strips keys it does not know about by default, so an API that adds a field will not break parsing. Add .strict() to an object to reject extra keys instead, or .passthrough() to keep them. Zod 4 also writes these two as z.strictObject and z.looseObject.
Frequently asked questions#
Does this work with Zod 4?
#
Yes. The output uses z.object, z.array, z.union, z.record with two arguments, and the optional and nullable modifiers, which mean the same in Zod 3 and Zod 4. The import is the same in both.
Why is a field z.null()?
#
Because it was null in the sample and nothing else was seen there. The schema would then reject any real value. Change it to the type it holds with .nullable() after it, such as z.string().nullable(), or paste a sample where it is filled in.
How are optional fields decided?
#
Across the elements of an array. When objects in the same array have different keys, a key missing from any of them gets .optional(). A single object has nothing to compare against, so all of its keys come out required.
Do I still need the TypeScript types?
#
You do not need to write them. With inferred types on, each schema has a type beside it built with z.infer, which follows the schema if you edit it. Turn them off if you would rather infer types where you use them.
Can it turn a JSON Schema into Zod?
#
No. It reads a JSON document as an example of the data and writes a schema that matches it. A JSON Schema pasted here is read the same way, as an object with keys such as type and properties, so the result would describe the schema file rather than the data it describes.
Is the JSON I paste sent anywhere?
#
No. It is parsed and converted in this browser tab as you type, so a response with real customer data in it stays on your machine.