How this converter works
Zod validates data at runtime, which is exactly where TypeScript types stop helping. Paste a real response and the tool infers a schema, so you don't hand-write a dozen nested z.object calls. Every array element is merged: a key missing from one element becomes .optional(), a null next to a string becomes .nullable(), a key that is both becomes .nullish(), and mixed primitives become z.union([...]).
With Detect formats on, a string field gets a validator only when every value in the sample matches it: z.email(), z.url(), z.uuid(), z.iso.datetime(), z.iso.date(), z.iso.time() or z.ipv4() in Zod 4, and the chained z.string().email() style in Zod 3. Whole numbers become z.int(). The Detected formats table lists each choice with the value that triggered it, so you can loosen anything your real data might break.
By default each object gets its own named schema (orderSchema, profileSchema …) declared before it is used, plus an exported z.infer type. Identical shapes under the same key share one schema. Switch to One nested schema for a single inline expression, pick strict or passthrough handling of unknown keys, and set the root name to match your endpoint.
Several samples can be merged at once: paste JSON Lines (one JSON value per line) and the schema covers all of them, with keys missing from some lines marked optional. Everything runs in your browser — the JSON is never uploaded.
Frequently asked
Which Zod version does this target?
Zod 4 by default: top-level formats like z.email() and z.iso.datetime(), z.int(), and z.strictObject / z.looseObject. Choose Zod 3 for z.string().email(), z.number().int() and .strict() / .passthrough(). Without formats and with the default object mode, the output runs on both.
Why is a field marked optional?
Because at least one element of an array of objects (or one JSON Lines sample) was missing that key. A sample that always contains the field can't prove it is ever absent. Turn on Make every field optional if you'd rather be defensive.
What happens to keys that are not in the schema?
z.object strips them from the parsed result. Unknown keys → Reject uses z.strictObject (Zod 4) or .strict() (Zod 3) to fail on them; Keep uses z.looseObject or .passthrough() to let them through.
Why is a field z.unknown()?
It was null in every sample (or an always-empty array), so there is nothing to infer from. The tool flags these in a warning — replace them with the real type, for example z.string().nullable().
Can it detect enums?
Yes, with Detect enums on. Short strings that repeat across an array — at least twice per distinct value — become z.enum([...]), or z.literal() when only one value appears. It is off by default because a sample rarely shows every allowed value.
Can I get Date objects instead of strings?
Turn on Coerce dates. ISO date and datetime fields become z.coerce.date(), so parse() returns a Date and z.infer gives Date.
Can I use the generated schema for forms?
Yes. The output is a plain Zod schema, so it works with react-hook-form resolvers, tRPC inputs, server actions and request validation without changes.