Self-hosted service
OpenAPITools/openapi-diff avatar
OpenAPITools/openapi-diff

OpenAPI-diff decides whether a spec change breaks the clients that read it

Utility for comparing two OpenAPI specifications.

1,098 stars189 forksJavaApache-2.0

At a glance

What is it?
OpenAPI-diff compares two OpenAPI 3.x specifications and tells you what changed, classifying the change as compatible or incompatible so a CI job can fail on it. It ships as a Java library, a command line tool, a Maven plugin and a Docker image, and it can render the result as HTML, Markdown, Asciidoc, plain text or JSON.
Who is it for?
OpenAPI-diff fits a team with a published API contract and generated clients, where the question is not what changed but whether the change is safe for the consumers. It is the wrong tool for a service with no external contract, and it is not a substitute for a contract test that exercises the running server.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 50 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The output is a compatibility verdict, not a text diff

OpenAPI-diff is a utility for comparing two OpenAPI specifications, and it renders the difference to HTML, plain text, Markdown files, or JSON files.

The framing matters, because a specification diff is not a text diff. Two documents that differ character by character can describe the same API, and two documents that look almost identical can break every client. The tool compares the API as a structure: parameters, responses, endpoints and HTTP methods such as GET, POST, PUT and DELETE.

The result is classified, and the classification is the reason the tool exists. The --state option outputs only the diff state, and the possible values are no_changes, incompatible and compatible. That is a three way answer a build can act on, rather than a document a human has to read.

The other two flags turn that state into a process. --fail-on-changed fails if the API changed but is backward compatible, and --fail-on-incompatible fails only if the change broke backward compatibility. One is for a team that wants to review every change, the other for a team that only wants to stop breaking changes. The README describes the Maven plugin route in the same terms, where you may opt to throw an error if you have broken backwards compatibility or if your API has changed.

Three distribution routes for the same engine

The tool is available in three shapes, and they suit different situations.

The first is a Maven dependency on the core artifact, which is what you want if you are writing Java and want to diff programmatically.

xml
<dependency>
  <groupId>org.openapitools.openapidiff</groupId>
  <artifactId>openapi-diff-core</artifactId>
  <version>${openapi-diff-version}</version>
</dependency>

The second is Homebrew, for Mac users.

bash
brew install openapi-diff

The third is Docker, published on Docker Hub as openapitools/openapi-diff. The README also documents how to build the image yourself, which it says is only required if you want to try new changes in the Dockerfile.

bash
docker build -t local-openapi-diff .

Running an instance mounts a directory of specs into the container.

bash
docker run --rm -t \
  -v $(pwd)/core/src/test/resources:/specs:ro \
  openapitools/openapi-diff:latest /specs/path_1.yaml /specs/path_2.yaml

The two paths are the old and the new specification, and the container is given them in that order. The README notes that the remote name can be replaced with local-openapi-diff or whatever name you gave your own image, which is how you test a change to the Dockerfile before it lands.

One capability sits under all three routes. openapi-diff can read OpenAPI specs from JSON files or from HTTP URLs, so the older spec does not have to be checked into the repository to be compared.

The flag set is where the integration details live

The command line help in the README is long, and the flags that matter fall into a few groups.

Output flags are one per format: --html, --markdown, --asciidoc, --json and --text each take a file to write to. The --state flag replaces the report with the single word classification, which is the one to use in a script.

Configuration comes in two forms. --config-file takes a YAML file to override default behaviour, and --config-prop takes a key:value pair on the command line, for example my.prop:true. The second is the one that makes the tool usable in a pipeline, because the behaviour can be adjusted per invocation without maintaining a file.

Authentication is handled for the case where a specification lives behind a URL that needs credentials. --header takes a property=value pair to use a given header for authorisation, and --query does the same through a query parameter. Both matter when the old or new specification is fetched from an internal host rather than a file.

The rest is diagnostic. There is a logging level flag with TRACE, DEBUG, INFO, WARN, ERROR and OFF, defaulting to ERROR, and separate switches for debugging information, error information, additional information, warnings and extra verbosity, plus --off for no information printed and --version to print the version and exit.

One property is unusual enough to notice. The feature list says the tool renders the difference of a property with Expression Language, which means the comparison of a property value is not a string equality check but an expression that is evaluated against the two versions. That is what lets a rule such as a renamed field or a loosened constraint be classified correctly rather than merely flagged as changed.

The Maven plugin puts the diff inside your build

The Maven plugin route is the most interesting of the three, because it changes where the check happens.

Add openapi-diff to your POM to show diffs when you test your Maven project, and you may opt to throw an error if you have broken backwards compatibility or if your API has changed. The plugin artifact is openapi-diff-maven, and the execution binds the diff goal.

xml
<plugin>
  <artifactId>openapi-diff-maven</artifactId>
  <groupId>org.openapitools.openapidiff</groupId>
  <version>${openapi-diff-version}</version>

The configuration carries an oldSpec reference, described in the comment as perhaps your production schema, and a new spec. The example points at the public petstore3 specification, which is a test fixture rather than a real contract, but it shows the shape: a reference specification on one side and the candidate on the other.

The difference in approach from running the CLI in a pipeline is where the check lives. The CLI is a step you add to a build script and interpret yourself. The plugin is bound to a lifecycle phase, so the diff runs as part of the Maven build and the build fails in the place the team already looks when something breaks.

There is a maven-example/ directory in the repository for exactly this, alongside the maven/ module that holds the plugin, and the core/ and cli/ modules that hold the engine and the command line front end.

The Docker image ships a class data sharing archive

The Dockerfile in the repository is short, and the second half is the interesting part.

The build stage is eclipse-temurin:21-jdk-alpine, it copies the whole tree into /build and runs the Maven wrapper with a docker profile to package the project. The runtime stage is eclipse-temurin:21-jre-alpine, which copies the openapi-diff.jar out of the build and also copies the petstore_v2 test specifications into /tmp.

Then it runs the jar once, at build time, with the ArchiveClassesAtExit option, passing the two petstore specifications.

dockerfile
RUN java -XX:ArchiveClassesAtExit=/app/appcds.jsa \
    -jar /app/openapi-diff.jar \
    /tmp/petstore_v2_1.yaml \
    /tmp/petstore_v2_2.yaml \
    && rm -f /tmp/petstore_v2_*

What that produces is a class data sharing archive, and the entrypoint then runs the jar with -Xshare:on and points at that archive, so the JVM starts with the classes already mapped and shared. The startup cost of a Java tool is paid once, at image build time, rather than on every invocation of the container. It is a small optimisation, and it is the kind of detail that only shows up in a tool meant to be run in CI on every pull request.

The CMD is --help, so a container started with no arguments prints the usage text rather than failing, and the last push to the repository was on 2026-09-04 with the project not archived and the newest listed release being 2.1.7 from 2026-01-26. The licence is Apache 2.0, and there is a SonarCloud quality gate badge next to the build badge.

Editorial conclusion

OpenAPI-diff fits a team with a published API contract and generated clients, where the question is not what changed but whether the change is safe for the consumers. It is the wrong tool for a service with no external contract, and it is not a substitute for a contract test that exercises the running server. Verify first that your specification version is one the tool supports, since the README lists OpenAPI v3.0, and try the --state flag on a pair of specs before you wire the --fail-on-incompatible option into a build that blocks merges.

Frequently asked questions

What does OpenAPI-diff compare and how is the result reported?

It compares two OpenAPI specifications in depth, covering parameters, responses, endpoints and HTTP methods, and renders the difference as HTML, plain text, Markdown, Asciidoc or JSON. The --state option reports only one of no_changes, incompatible or compatible.

How do I use OpenAPI-diff in a CI pipeline?

Use --fail-on-incompatible to fail only when a change breaks backward compatibility, or --fail-on-changed to fail on any change at all. You can also use --state to print just the classification, and --config-prop to override default behaviour with a key:value pair.

How do I install OpenAPI-diff?

It is on Maven Central as openapi-diff-core for Java projects, on Homebrew as brew install openapi-diff, and on Docker Hub as openapitools/openapi-diff. You can also build the image locally with docker build -t local-openapi-diff . and run it against two specification paths.

Can OpenAPI-diff read a specification from a URL?

Yes, it can read OpenAPI specs from JSON files or HTTP URLs. Where a specification needs credentials, --header and --query pass authorisation values, and --config-file with a YAML file or --config-prop with a key:value pair override default behaviour.

Which OpenAPI versions does OpenAPI-diff support?

The README lists support for OpenAPI spec v3.0. The Docker image copies petstore_v2 test specifications into /tmp in the runtime stage, so the boundary for version 2 documents is not spelled out in the README, and the stated requirement is Java 8 while the container is built on a Java 21 image.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. OpenAPITools/openapi-diff on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/openapitools-openapi-diff.svg)](https://hysenlabs.com/projects/openapitools-openapi-diff)

Was this useful?