This site has no tool that takes an OpenAPI spec and produces a Zod schema. This guide covers which existing tools do spec-to-code generation well, and then a separate problem the tools here do fit: checking whether the spec you're generating from still describes the API.
Generating types from the spec
If you have an OpenAPI document and want TypeScript types (and maybe Zod schemas) generated from its components.schemas, use a dedicated codegen package. openapi-typescript produces types. openapi-zod-client produces Zod schemas plus a typed client. Both read the spec's schema definitions and emit code with the same structure. That problem is solved, and a browser tool wouldn't improve on it.
What spec-first output looks like
Here's a spec schema:
# components.schemas.User
type: object
required: [id, email]
properties:
id: { type: string }
email: { type: string }
displayName: { type: string, nullable: true }openapi-zod-client turns it into a Zod schema that follows the spec's structure:
const User = z.object({
id: z.string(),
email: z.string(),
displayName: z.string().nullable().optional(),
})
type User = z.infer<typeof User>id and email are required because the spec's required array lists them. displayName is nullable and optional because the spec marks it nullable and leaves it out of required. As a translation of the spec, that's correct. Whether it describes the live API is another matter, and that's what the rest of this guide is about.
A side point: a Zod schema only helps over a plain TypeScript type if something calls User.parse(response) at runtime. TypeScript types are erased when you compile. They affect whether your editor warns you while you write code, and nothing about what happens when the response shows up at 2am missing a field. If nothing in the request path calls .parse() or .safeParse(), Zod schemas give you no more than openapi-typescript would. You get runtime protection only where you invoke it. .parse() throws on a mismatch. .safeParse() returns a result object instead, which lets you log the mismatch and decide what to do rather than failing the request.
Does the spec match the API?
Types generated from a spec are only as accurate as the spec, and specs drift from the real API more often than you'd expect. A field marked required that the endpoint sometimes leaves out. A documented response shape that differs in an edge case nobody updated the spec for. A generator can't catch any of this. It translates whatever the spec says, right or wrong, because the spec is the only input it ever reads.
Say the spec documents a user like this:
# components.schemas.User
type: object
required: [id, email, phone]
properties:
id: { type: string }
email: { type: string }
phone: { type: string }A codegen tool reads that and produces a type where phone is required, because the spec says so. Now suppose the endpoint was built before phone numbers were mandatory at signup, and older accounts were never backfilled. Some responses look like this:
{ "id": "u_1", "email": "ada@example.com" }There's no phone field. The generated type says that can't happen, and the API does it anyway. Code that trusts the type to include phone can crash at runtime in a case the type system said was impossible. The codegen tool isn't at fault. The spec is inaccurate, and the tool never looks at a real response, so it can't know.
That narrower job, checking the spec against real responses, is where the tools here help:
- Run the spec through the OpenAPI / Swagger Validator first. It checks the document against the official OpenAPI schema and flags
$refsthat point nowhere and duplicateoperationIds, the problems that make a codegen tool fail or quietly produce broken output. This finds problems in the spec document. Drift from the live API is a separate step. - Capture a few responses from the live endpoint (actual HTTP responses, not examples from the spec) and paste them into JSON to Zod or Live Types. You get a schema inferred from what the API sent, independent of what the spec says it sends.
- Compare the two schemas. In the example above, the spec-generated type has
phone: string(required). The schema inferred from your samples showsphone: string | undefinedas soon as one sample is missing it. That mismatch is the kind of drift that breaks consumers in production without an error: the spec says one thing, the API does another, and nothing was checking that they agree.
Spec-first vs. sample-first
Spec-first generation (openapi-typescript and similar) is the right default when your team maintains the spec and trusts it, as with an internal API where the spec and the implementation change in the same commit. Sample-first inference, which is what the tools here do, fits when you consume an API you don't control. The spec might be stale, incomplete, or wrong, and you want to know what the API sends regardless of the docs.
One doesn't replace the other. A type generated from the spec describes the contract. A schema inferred from samples describes what you observed. When the two disagree, the disagreement is the useful part.
If you want to catch that gap over time instead of checking once by eye, that's the job Live Types was built for. Save the inferred shape as a snapshot, then diff a fresh sample against it later to see what changed.
Using both together
In practice you want both. Generate your application types from the spec with openapi-typescript or openapi-zod-client. That's the contract your code is written against, and everyone on the team reads it the same way.
Then, now and then (after a provider deploy you didn't control, or on a schedule), capture fresh samples and run them through JSON to Zod to spot-check the spec-generated type. Two schemas for the same endpoint should agree. When they don't, that's your finding, and neither tool made a mistake. The generated type reflects the spec and the inferred schema reflects what you saw. What you're checking is whether those two descriptions of one endpoint still match.