Catch breaking OpenAPI changes before your clients do.

SpecDiff compares two OpenAPI 3.x documents and classifies every contract change as breaking, warning, safe or info, so API changes can be gated in pull requests instead of being discovered by clients after a deploy.

The Playground runs the comparison in your browser. Your documents are not uploaded.

The problem

A pull request that removes a response field, makes a query parameter required or narrows an enum is hard to spot in a large YAML diff. SpecDiff reads both documents, resolves every $ref (including recursive schemas and relative files in the CLI), and tells you which changes can break existing clients, with the rule that fired and where in each document it happened.

Direction-aware by design

A schema describes data flowing in one direction, and what counts as compatible differs in each. The same edit can be safe in a request and breaking in a response.

Compatibility depends on direction
EditIn a requestIn a response
integer widened to numberfinebreaking
new required fieldbreakingnot a break
field removednot a breakbreaking

Why: the server consumes requests, so a request change breaks clients when the server accepts less than before. The server produces responses, so a response change breaks clients when the server may send more than they were promised or less than they relied on. The rules and their rationale are in SPEC.md.

What it does

Everything listed here is described in the project README and covered by its tests.

Four severities

breaking, warning, safe and info, each change tagged with a rule ID from a documented rule table.

OpenAPI 3.0.x and 3.1.x

Compared against each other. nullable: true and type: [T, "null"] are treated as the same thing.

Recursive references

Resolves every $ref, including recursive schemas, and compares shared schemas once.

Text, JSON, Markdown

Reports with deterministic ordering: no timestamps, sorted by path, method and rule.

Built for CI

Exit codes you can gate on, configurable with --fail-on, plus --ignore and --include-safe.

Honest about limits

Constructs it does not analyze, such as oneOf and anyOf, are reported as info instead of ignored.

A realistic run

Two versions of a small Orders API, compared from the command line. This is the CLI's real output for the example that is preloaded in the Playground; the exit code is 1 because breaking changes exist.

$ specdiff orders-v1.yaml orders-v2.yaml
specdiff 0.1.0
base: orders-v1.yaml (Orders API 1.0.0, OpenAPI 3.0.3)
head: orders-v2.yaml (Orders API 2.0.0, OpenAPI 3.0.3)

GET /orders
  BREAKING  PARAM_BECAME_REQUIRED        query parameter "status" is now required
  BREAKING  RESP_PROPERTY_REMOVED        200 application/json: property "value[].note" removed
  BREAKING  TYPE_CHANGED                 200 application/json: type of "value[].total" changed from integer to number

POST /orders
  BREAKING  REQ_PROPERTY_REQUIRED_ADDED  request body application/json: property "currency" added as required
  BREAKING  REQ_CONSTRAINT_TIGHTENED     request body application/json: maximum of "quantity" tightened from 100 to 50
  BREAKING  RESP_PROPERTY_REMOVED        201 application/json: property "note" removed
  BREAKING  TYPE_CHANGED                 201 application/json: type of "total" changed from integer to number

GET /orders/{orderId}
  BREAKING  RESP_PROPERTY_REMOVED        200 application/json: property "note" removed
  BREAKING  TYPE_CHANGED                 200 application/json: type of "total" changed from integer to number
  WARNING   RESP_STATUS_REMOVED          response status 404 removed

DELETE /orders/{orderId}
  BREAKING  OP_REMOVED                   operation DELETE /orders/{orderId} removed

Summary: 10 breaking, 1 warning, 0 info (2 safe hidden, use --include-safe to list them)

Try this comparison in the Playground

Get started

Install SpecDiff from npm (Node.js 22.19 or newer), or run it once with npx:

npm install --global specdiff
specdiff --version

# or without installing
npx specdiff base.yaml head.yaml
specdiff <base> <head> \
  [--format text|json|markdown] \
  [--fail-on breaking|warning|never] \
  [--include-safe] [--ignore RULE_ID,...]

Exit 0 means nothing at or above the --fail-on threshold, 1 means changes at or above it, and 2 means a usage or input error. The package is on npm; to build from source, see the README. A GitHub Actions recipe is in the README.

CLI and Playground

The CLI is the product and the thing to put in CI. The Playground is a way to try it without installing anything.

Same engine

The Playground imports the CLI's normalization and diff modules and its report renderers unchanged and runs them in your browser. Only the loading step differs: the CLI reads files, the Playground parses editor text. A test suite compares the two on the project's own fixtures.

Differences

Playground documents are limited to 1 MB each, and $refs must point inside the same document: relative file references work in the CLI only. Nothing is uploaded, saved or placed in the URL.