DevTools Hub

Search tools

Search for a developer tool

How to Validate OpenAPI Specifications

Part of the OpenAPI Toolkit

"Validate the OpenAPI spec" usually means two different checks bundled into one step. The first is structural: is this well-formed YAML or JSON that matches the shape the OpenAPI format defines? The second is semantic: does it also follow the rules a schema can't express on its own — unique operation IDs, path parameters that are actually declared, $refs that resolve to something real? A document can pass the first check completely and still be broken in ways that fail real tooling downstream.

Layer one: structural (schema) validation

OpenAPI documents are themselves described by a schema — the OpenAPI Specification defines exactly what a valid document looks like: an openapi (3.x) or swagger (2.0) version field, an info object with title and version, a paths object, and so on, each field with a defined type. A structural validator checks a document against this shape the same way JSON Schema Validator checks any JSON document against any schema — because that's literally what's happening under the hood. This layer catches typos in field names, wrong types (a string where an array is expected), and missing required fields.

Layer two: semantic checks a schema alone can't catch

A lot of real breakage passes structural validation cleanly. These rules require actually understanding what the fields mean, not just their shape:

  • Duplicate operationId. Every operation's ID is supposed to be unique across the whole document — code generators use it to name functions, and a collision means one of them silently gets clobbered or the generator errors out.
  • Undeclared path parameters. A path template like /users/{userId} needs a matching { in: "path", name: "userId", required: true } parameter declared — nothing in the schema shape enforces that the template placeholder and the parameter declaration actually agree with each other.
  • Broken $ref. A schema only checks that $ref is a string — not that the string actually points to something that exists in the document. A typo'd ref (#/components/schemas/Usre) is structurally perfect and semantically useless.
  • Responses missing a description. The field is required by the spec on every response object, easy to forget when copy-pasting an example that omitted it.

Our own OpenAPI Validator checks exactly this list — version and info fields, duplicate operationIds, path parameters declared against the path template, required response descriptions, and local $ref resolution (an external or remote $ref gets flagged as unable to check, rather than silently assumed valid). For the full catalog of what breaks and why, see Common OpenAPI Validation Errors.

Real tooling: Redocly CLI

For validating in a terminal or CI pipeline, Redocly CLI is the current standard:

npx @redocly/cli@latest lint openapi.yaml

It runs both layers at once — structural validation plus a configurable ruleset of semantic and style rules — and reports problems with the exact location in the file. If you find older tutorials pointing at swagger-cli instead: that package is deprecated, and Redocly CLI is its official replacement, covering the same validation plus considerably more.

For enforcing your own organization's standards beyond what a generic validator checks — requiring every operation to have a specific tag, banning a deprecated field, enforcing a naming convention — Spectral is the tool built for custom rulesets, and it's what several hosted API-design tools use under the hood.

The 3.0-vs-3.1 gotcha

Validating against the wrong OpenAPI version's rules produces confusing false results, and the schema-related fields changed enough between 3.0 and 3.1 that this happens often:

  • Nullable fields. OpenAPI 3.0 uses its own custom keyword — { type: "string", nullable: true }. OpenAPI 3.1 dropped that in favor of plain JSON Schema union types — { type: ["string", "null"] }. A 3.0 document validated with 3.1 rules (or vice versa) fails on this immediately.
  • exclusiveMinimum / exclusiveMaximum. In 3.0 these are booleans that modify minimum/maximum. In 3.1 they're numbers on their own, matching standard JSON Schema 2020-12 — the draft OpenAPI 3.1 fully aligns with.

If a validator reports an error on a field that looks completely ordinary, check which draft it's validating against before assuming the document is actually broken.

Wiring it into CI

A practical setup treats the two severities differently: fail the build on anything that breaks real tooling — broken refs, duplicate operation IDs, missing required fields — and surface anything else (style suggestions, missing but non-required descriptions) as a warning that doesn't block a merge. Redocly CLI's exit code reflects error-level findings by default, so a plain redocly lint step in a pipeline already gives you that split without extra scripting.

Common mistakes specific to this case

  • Stopping at schema validation. A document with zero schema errors can still have duplicate operation IDs or a dangling $ref — the two layers catch different things, and neither substitutes for the other.
  • Validating against the wrong version's rules. See the 3.0-vs-3.1 section above — a version mismatch produces errors that have nothing to do with an actual mistake in the document.
  • Assuming YAML indentation errors show up as OpenAPI errors. A YAML parser failure happens before OpenAPI-specific validation ever runs, and the resulting message describes a YAML syntax problem, not a spec violation — worth knowing so you don't go hunting through the OpenAPI spec for a rule that was never the issue.
  • Ignoring warnings on an external or unbundled $ref. A ref pointing at another file or a URL usually can't be checked without first bundling the document into one file — a validator flagging it as "can't verify" isn't the same as it being confirmed valid.

Try it yourself

Paste a document into OpenAPI Validator for an instant check of both layers — schema shape plus the semantic rules above. To read the document as endpoint documentation rather than raw YAML/JSON, use OpenAPI Viewer. Both run entirely in your browser; nothing you paste is uploaded.

Related tools