Tool comparison
OpenAPI diff tools compared: oasdiff, openapi-diff and more
Four maintained, open-source ways to diff two OpenAPI documents and fail a pull request on a breaking change. What each one calls breaking, how it exits, and where they disagree.
Published 31 October 2026 · 7 min read
An OpenAPI diff tool compares two versions of a spec and tells you which changes can break a client. In CI, that becomes a pull-request check: compare the spec on the branch with the spec on main and fail the job on a breaking change. This article compares four open-source tools that do this and are still maintained, using each project's own README and docs as read on 31 October 2026. One of the four, apibreak diff, is made by the publisher of this site; it is described from its README and source, with the same scrutiny.
oasdiff
oasdiff is a Go command-line tool under the Apache 2.0 licence. Its main commands are breaking (only the changes that break existing clients), changelog (breaking and non-breaking changes, human-readable), diff (the full structural diff, including documentation-only edits) and summary. Every check has a level: ERR for definite breaking changes, WARN for potential breaking changes that cannot be confirmed from the spec alone, and INFO for non-breaking ones. The breaking command reports ERR and WARN. Adding --fail-on ERR makes it exit 1 on errors, --fail-on WARN also on warnings, and --fail-on INFO on any change. You can change the level of individual checks with a severity-levels file.
oasdiff reads specs straight from git with the ref:path syntax, so oasdiff breaking origin/main:openapi.yaml openapi.yaml works without a temporary file. Its docs state its policy plainly: a change is breaking if a consumer that followed the old contract can stop working under the new one, even if your server would still accept the old request. It supports OpenAPI 3.0 and 3.1 and reads 3.2. A response enum that gains a value is an ERR by default, unless the enum is declared with the x-extensible-enum extension, which says new values may be added.
For CI there is oasdiff-action (Apache 2.0). Its breaking action writes inline ::error:: annotations on the pull request and fails at or above the fail-on level. One default deserves attention: when changes are found, the free actions upload the comparison to oasdiff.com to produce a side-by-side review link. The README says both specs are encrypted in CI before upload and the key stays in the URL fragment. Set review: false if no spec should leave CI. A paid Pro tier adds approve and reject buttons and a commit status that blocks the merge until each breaking change is approved.
openapi-diff (OpenAPITools)
openapi-diff is a Java library and CLI under Apache 2.0, distributed through Maven Central, a Maven plugin and the openapitools/openapi-diff Docker image. Its README describes it as comparing OpenAPI 3.x specifications; the core module also depends on swagger-parser's v2 converter and has a Swagger 2.0 compatibility test, so Swagger 2.0 documents are read too. It renders the result as HTML, Markdown, Asciidoc, JSON or plain text. For CI, --fail-on-incompatible fails only when the change broke backward compatibility, --fail-on-changed fails on any change, and --state prints just no_changes, incompatible or compatible.
Its rules are individual properties you can switch off with --config-file or --config-prop. The defaults are worth reading: in the source, incompatible.response.enum.increased is on, so a response enum that gains a value counts as an incompatible change unless you turn that rule off. That is a defensible choice for strict clients, and a noisy one for a vendor whose policy is that new enum values are additive. By default it matches paths regardless of parameter names, so /users/{id} pairs with /users/{userId}.
openapi-changes (pb33f)
openapi-changes is a Go tool under Apache 2.0 that supports OpenAPI 3.0, 3.1 and 3.2. Its focus is reading change: an interactive terminal UI (console), a terminal summary, a JSON report, a Markdown report and a self-contained HTML report with a timeline and side-by-side diffs, which runs fully offline. Besides two files, it can compare git revisions (main:api/openapi.yaml against feature-branch:api/openapi.yaml) or walk one spec's history. Which changes count as breaking is configurable in a changes-rules.yaml file.
Its GitHub Action takes a spec path and the base and head commits, writes a job summary and a report artifact, and accepts fail-on: breaking (the default), changes or none. The rules file is read from the base revision, so a pull request cannot relax its own policy, and a missing or invalid spec is an analysis error that always fails, never an empty report.
apibreak diff
apibreak diff is one of three commands in the apibreak npm package (MIT, version 0.3.2, labelled early access, no dependencies, Node 20 or newer). It compares two OpenAPI 3.x or Swagger 2.0 documents, local files or URLs, JSON or YAML. With --base-ref <git-ref> <path> it reads the same path from the git ref with git show, which is the pull-request case. --fail-on breaking (the default) exits 1 on a breaking finding, any also exits 1 on deprecations, advisories and unknowns, and none always exits 0. A usage error or a document that could not be fetched or parsed exits 2, so a script can tell an unreadable spec from a breaking change.
On one judgement call it is the deliberate outlier: a response enum that gains a value is an advisory finding, where oasdiff and openapi-diff both treat it as breaking by default. It also refuses to guess on unions: every anyOf and oneOf schema is reported as not compared, and so is an allOf whose branches define the same field differently; other allOf schemas are merged into one shape. Not compared findings never fail a run, but they are listed in every report. The same engine powers the apibreak check command, which watches the GitHub and Stripe specs for the endpoints you declare.
Side by side
- Language and licence: oasdiff Go, Apache 2.0; openapi-diff Java, Apache 2.0; openapi-changes Go, Apache 2.0; apibreak diff Node, MIT.
- Spec versions, per each README: oasdiff 3.0 and 3.1 (reads 3.2); openapi-diff 3.x, plus Swagger 2.0 through swagger-parser's converter; openapi-changes 3.0, 3.1 and 3.2; apibreak diff 3.x and Swagger 2.0.
- Severity model: oasdiff ERR, WARN and INFO with per-check overrides; openapi-diff incompatible or compatible with per-rule switches; openapi-changes breaking or not, with a rules file; apibreak diff breaking, deprecation, advisory, unknown and not compared.
- Comparing against main: oasdiff and openapi-changes take ref:path; apibreak diff takes --base-ref; openapi-diff takes two files or URLs, so you extract the base version yourself.
- Output for reviewers: openapi-changes has the richest offline HTML report; openapi-diff renders HTML and Markdown; oasdiff has Markdown and HTML changelogs plus the hosted review link; apibreak diff prints Markdown or JSON.
- GitHub Action: oasdiff-action and the openapi-changes action are official; openapi-diff and apibreak diff run as a plain step. apibreak also ships its own Action (Skyblueballykid/apibreak@v1), but that one runs apibreak check against vendor specs; it is not a two-file diff action.
Where the tools disagree
The tools agree on the clear cases: removing an operation, removing a response field, adding a required parameter, changing a type. They differ on the judgement calls, and those are the findings that make a gate noisy or quiet. The cleanest example is a response enum that gains a value. oasdiff reports it as ERR and openapi-diff as incompatible, both by default; oasdiff lets you declare the enum open with x-extensible-enum, and openapi-diff lets you switch the rule off. apibreak diff is the deliberate outlier and reports it as an advisory. Vendors lean the other way: GitHub's API versioning page lists adding enum values as an additive change, and Stripe's versioning page tells integrations to ignore unfamiliar fields and event types; treat new enum values the same way. None of the tools is wrong. Pick the one whose defaults match the promise your API makes, or set the rule explicitly.
The second place to look is ambiguity: oneOf and anyOf unions, allOf, nullable, additionalProperties. Before trusting a tool as a gate, diff two versions of your own spec that touch a union and read what it reports.
Which one to pick
- You want the most checks and a maintained GitHub Action with inline annotations: oasdiff with oasdiff-action, with review: false if specs must stay in CI.
- You are on the JVM and want the check inside the Maven build: openapi-diff's Maven plugin with failOnIncompatible.
- Reviewers need to read the change, not only pass or fail: openapi-changes and its HTML report.
- You want one npm command with no dependencies, explicit not compared findings and enum additions treated as advisories: apibreak diff --base-ref origin/main openapi.yaml.
Optic, once a common answer to this question, is archived on GitHub; its README says Optic Labs is now part of Atlassian. Whichever tool you choose, every one of them only sees the document. A server that stops honouring its spec without a spec change is invisible to all four, which is why a diff gate is one check among several in CI rather than the whole answer.
Check the endpoints you actually call
APIBreak reads the endpoints you declare, compares the vendor's own OpenAPI specification against the revision you pinned, and fails the build only when a change touches one of yours. It runs in your CI with no credentials and nothing uploaded, and the check is free.
Install the check · read a real report · what changed at each vendor