JSON to Zod Schema Generator
Turn a JSON sample into Zod schemas with inferred TypeScript types.
Use via API
Generate Zod schemas (and inferred TypeScript types) from a sample JSON document, optionally detecting string formats such as email, URL, UUID and dates. Send JSON, get JSON back.
POST https://pithlab.com/api/v1/tools/json-to-zod
curl https://pithlab.com/api/v1/tools/json-to-zod \
-H "Authorization: Bearer $PITHLAB_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"{\"id\":\"3f2b8c1e-4d5a-4b6c-9e7f-1a2b3c4d5e6f\",\"email\":\"[email protected]\",\"age\":36}","rootName":"User","detectFormats":true}'About the JSON to Zod
Generate Zod schemas and z.infer types from a JSON sample, with nested schemas, nullable and optional keys, unions and optional email, URL, UUID and date checks.
How to use the JSON to Zod
- Paste a JSON sample, such as an API response, into the input box.
- Set the root type name and choose whether to detect string formats like email, URL, UUID and dates.
- Nested objects get their own schema, declared before the schema that uses it.
- Copy the code or download it as a .ts file, then validate data with schema.parse().
Examples
Input
{"id": 1, "name": "Ada", "address": {"city": "London"}}Output
import { z } from "zod";
export const addressSchema = z.object({
city: z.string(),
});
export type Address = z.infer<typeof addressSchema>;
export const rootSchema = z.object({
id: z.number(),
name: z.string(),
address: addressSchema,
});
export type Root = z.infer<typeof rootSchema>;
Input
{"users": [{"name": "Ada", "email": null}, {"name": "Linus", "email": "[email protected]", "admin": true}]}Output
import { z } from "zod";
export const userSchema = z.object({
name: z.string(),
email: z.string().nullable(),
admin: z.boolean().optional(),
});
export type User = z.infer<typeof userSchema>;
export const rootSchema = z.object({
users: z.array(userSchema),
});
export type Root = z.infer<typeof rootSchema>;
Input
{"id": "1b4e28ba-2fa1-41d2-883f-0016d3cca427", "email": "[email protected]", "website": "https://example.com", "createdAt": "2026-10-19T09:30:00Z"}Output
import { z } from "zod";
export const accountSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
website: z.string().url(),
createdAt: z.string().datetime(),
});
export type Account = z.infer<typeof accountSchema>;
Frequently asked questions
What is the difference between nullable and optional?
A key whose value is null in the sample gets .nullable(), so null is allowed. A key that is missing from some objects in an array gets .optional(), so it may be left out. A key can have both.
How does format detection work?
With the option on, a string field gets .email(), .url(), .uuid(), .date() or .datetime() only when every sample value for that field matches. Mixed values stay a plain z.string().
Which Zod version is this for?
The output uses the classic Zod API that works in Zod 3.23 and later, including Zod 4, where methods like .email() still work but have newer top level forms such as z.email().
Why does my datetime use { offset: true }?
By default .datetime() only accepts times ending in Z. If a sample has an offset like +02:00, the generator adds { offset: true } so those values pass too.
Do I still need separate TypeScript types?
No. Each schema comes with export type X = z.infer<typeof xSchema>, so the type always matches what the schema validates. Turn this off if you only want the schemas.