How to Validate an OpenAPI or Swagger Spec File

Validate OpenAPI spec and Swagger files step by step: parse, check against the official schema, add lint rules, run them in CI, and see what checks miss.

On this page

To validate an OpenAPI spec, run it through three checks in order. First parse it, so YAML or JSON syntax errors surface. Then check the parsed document against the JSON Schema published for its OpenAPI version, which catches missing required fields and wrong types. Then run a linter with rules that look across the whole document, such as unique operationIds and path parameters that match the path template. Run all three on every pull request that touches the spec. Passing them proves the file is well formed. It does not prove the API is well designed or that the server returns what the file says, and the last section covers those gaps.

The layers of spec validation

LayerWhat it checksExample failureTypical tool
SyntaxThe file is valid YAML or JSONA tab used for indentationAny YAML or JSON parser
StructureThe document matches the OpenAPI JSON Schema for its versionA response with no descriptionswagger-parser validate(), and the struct rule in Redocly or oas3-schema in Spectral
Cross-document rulesRules that span several objectsTwo operations with the same operationIdRedocly CLI or Spectral rulesets; swagger-parser for Swagger 2.0 only
Style and designYour team's conventionsPaths in camelCase, no 4xx responsesLinter rules you configure, or the APIForge design score
Each layer assumes the one above it passed.

The first two layers are objective: the file either follows the specification or it does not. The third is mostly objective too, since rules like operationId uniqueness come straight from the specification text, but a JSON Schema cannot express them. The fourth is opinion, which is why linters let you turn rules off or change their severity. If you are still fixing parser messages, the common OpenAPI errors guide lists them with fixes; this guide is about setting up the checks.

A spec that passes structural validation with three bugs

Here is what the gap between the second and third layers looks like in practice. This OpenAPI 3.0.3 file has three mistakes. Both operations use the operationId getOrder. The path /v1/orders/{id} has no parameter declared for id. The example on the 200 response sets status to cancelled, which is not in the enum.

YAMLorders.yaml
openapi: 3.0.3
info:
  title: Orders API
  version: 1.0.0
paths:
  /v1/orders:
    get:
      operationId: getOrder
      summary: List orders
      responses:
        "200":
          description: A page of orders
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Order"
        "400":
          description: Bad query
  /v1/orders/{id}:
    get:
      operationId: getOrder
      summary: Get an order
      responses:
        "200":
          description: The order
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
              example:
                id: ord_123
                status: cancelled
        "404":
          description: Order not found
components:
  schemas:
    Order:
      type: object
      required: [id, status]
      properties:
        id:
          type: string
        status:
          type: string
          enum: [pending, paid, shipped]

We ran it through @apidevtools/swagger-parser 12.1.0, a Node library for parsing and validating specs. validate() returned the document with no error. That is documented behaviour: the library's options page says validation against the OpenAPI 3 specification rules is not yet supported, so its spec option is ignored for OpenAPI 3 files and only the JSON Schema check runs. The same mistakes in a Swagger 2.0 file do fail; swagger-parser reports Duplicate operation id 'getOrder' and a missing path parameter.

APIForge's design scorer gave the file 100 out of 100, because it grades naming, HTTP usage, versioning and response coverage, not validity. Neither result is wrong, but neither tool checks whether the file is internally consistent. Rule-based linters do. According to their rule documentation, both Redocly CLI and Spectral have a rule for each of the three bugs:

BugRedocly CLI ruleSpectral rule
Duplicate operationIdoperation-operationId-uniqueoperation-operationId-unique
{id} with no path parameterpath-parameters-definedpath-params
Example value outside the enumno-invalid-media-type-examplesoas3-valid-media-example
Rule names from each project's documentation. Check your installed version's ruleset for defaults and severities.

Validate from the command line

For the structural layer, a short script around swagger-parser gives you a clear pass or fail and an exit code CI can use. It works for Swagger 2.0, OpenAPI 3.0 and OpenAPI 3.1 files, follows $ref pointers to other files, and fails on any reference that does not resolve.

JavaScriptscripts/validate-spec.mjs
import SwaggerParser from "@apidevtools/swagger-parser";

const file = process.argv[2] ?? "openapi.yaml";

try {
  const api = await SwaggerParser.validate(file);
  const version = api.openapi ?? api.swagger;
  console.log(`${file} is valid (version ${version}, ${Object.keys(api.paths ?? {}).length} paths)`);
} catch (err) {
  console.error(`${file} failed validation:\n${err.message}`);
  process.exitCode = 1;
}
Install with npm install --save-dev @apidevtools/swagger-parser.

For the rule layer, pick one linter and commit its config next to the spec, so every developer and the CI job use the same rules. With Redocly CLI, the command is redocly lint followed by the file. It ships rulesets named minimal, recommended, recommended-strict and spec, which you choose with extends in a redocly.yaml file or the --extends flag.

YAMLredocly.yaml
extends:
  - recommended
rules:
  operation-4xx-response: error
  no-unused-components: warn

Spectral works the same way with a .spectral.yaml file that extends its built-in OpenAPI ruleset, spectral:oas. Run it with npx @stoplight/spectral-cli lint orders.yaml. Spectral's documentation lists no-$ref-siblings as an error by default, which matters for OpenAPI 3.0 files, where keywords next to a $ref were ignored by most tooling.

YAML.spectral.yaml
extends: ["spectral:oas"]
rules:
  operation-success-response: error

You do not need both linters. They overlap heavily on OpenAPI rules; choose the one whose output format and config style your team prefers, and add rules as you find problems they should have caught.

Run validation in CI

The goal is that a broken spec never merges. Run the checks on pull requests that change the spec file, and make the job required in branch protection. Redocly's github-actions output format turns each problem into an annotation on the changed line, so reviewers see it in the diff.

YAML.github/workflows/openapi.yml
name: Validate OpenAPI
on:
  pull_request:
    paths:
      - "openapi.yaml"
      - "schemas/**"
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - name: Structural validation
        run: node scripts/validate-spec.mjs openapi.yaml
      - name: Lint rules
        run: npx @redocly/cli lint openapi.yaml --format=github-actions
  • Filter on paths so the job only runs when the spec or its referenced files change. Include every directory that holds split schema files.
  • If the spec is generated from code, generate it in CI and validate the output. Validating a committed copy that nobody regenerates checks a stale file.
  • Start new rules as warnings on an existing spec, fix the backlog, then promote them to errors. A job that fails on day one with 300 problems gets disabled.
  • Pin linter versions in package.json. A new release can add rules or change defaults and fail builds that passed yesterday.

What spec validation does not catch

A file can pass every layer and still mislead the people who use it. Plan for these gaps separately:

  • Server behaviour. The spec can say a field is required while the server omits it. Only testing real responses finds that; see how to validate API responses against the OpenAPI schema.
  • Weak schemas. type: object with no properties is valid. So is a schema with no required array, which makes every field optional in generated clients. The guide on OpenAPI response design covers what a complete response looks like.
  • Breaking changes. Renaming a field produces a valid new file. Catching it means comparing against the previous published version, which is a review step, not a validation step. The API versioning guide covers which changes break clients.
  • Wrong descriptions. A validator cannot tell that a description says cents while the server returns dollars.
  • Security design. A spec can declare no security at all and still be valid. Review it against the API security best practices.

Swagger 2.0 files

Swagger 2.0 gets slightly better coverage from swagger-parser than OpenAPI 3 does, because the library's rule checks were written for 2.0: duplicate operationIds, missing or extra path parameters, array parameters without items, and duplicate parameters all fail. Linters still support 2.0 with their own rule sets. If you maintain a 2.0 file, the Swagger vs OpenAPI guide covers what changes when you migrate, and the Swagger validator page loads 2.0 files directly.

Check a spec in APIForge

The OpenAPI validator is built for looking at a spec, not for gating a build. Paste a URL to a JSON or YAML file, a Swagger UI page, or upload the file. APIForge parses it, resolves internal references and public HTTP references, and lists every operation with its parameters and schemas. Loading is deliberately lenient: a local reference that does not resolve becomes a placeholder schema labelled Unmodeled schema reference with the pointer, and circular references are ignored instead of failing the load. That lets you explore and run requests against an imperfect spec, and it is the reason to keep a strict validator and a linter in CI.

From the same workspace you can open the 0-100 design score, which flags operations with no 2xx or no error responses, verbs in paths, missing descriptions, missing version prefixes and inconsistent naming. Use it for the fourth layer, alongside the linter config your team agrees on.

Frequently asked questions

Validation checks the file against the OpenAPI specification: correct structure, required fields, resolvable references. Linting adds rules, some taken from the specification text and some that are style choices, such as requiring a 4xx response on every operation. Most teams run both.

Yes, if the tool supports 3.1. swagger-parser 12.1.0 accepts 3.1.0 to 3.1.2, and Redocly and Spectral both have OpenAPI 3.1 support. Check the version you have installed, since 3.1 schemas follow JSON Schema 2020-12 and older tools reject syntax such as type arrays.

No. It means the document is well formed. Whether the server matches it needs response testing, and whether the design is good needs review or a design score.

Sources

  1. Swagger Parser options: validate.schema and validate.specAPIDevTools
  2. Redocly CLI lint commandRedocly
  3. Redocly CLI built-in rulesRedocly
  4. Spectral OpenAPI rules referenceStoplight Spectral
  5. OpenAPI Specification 3.1.0OpenAPI Initiative