Skip to content

OpenAPI Diff

Compares two OpenAPI documents and separates breaking changes from additive ones.

Compares two versions of an OpenAPI document and separates the changes into breaking and additive. The rule it uses is written on the page: a change is breaking if a request the old document accepted might now be rejected, or a response it promised is one an existing client can no longer handle.

🔒 Runs entirely in your browseropenapidiffapibreaking-change

Previous document

Drop the previous version

Read in this tab only.

New document

Drop the new version

Read in this tab only.

Breaking

8

Additive

7

Verdict

Needs a major version

View

GET /pets

  • breakingThe `in: query` parameter `limit` is now required.
  • addedAn optional `in: query` parameter `sort` was added.
  • changedThe summary or description changed.

GET /pets · responses["200"].content["application/json"]

  • breaking`[].id` changed type from integer to string.
  • breakingThe property `[].name` is now optional.
  • breaking`[].status` gained the enum value "pending".
  • addedAn optional property `[].owner` was added.

GET /pets/{petId} · responses["200"].content["application/json"]

  • breaking`id` changed type from integer to string.
  • breakingThe property `name` is now optional.
  • breaking`status` gained the enum value "pending".
  • addedAn optional property `owner` was added.

GET /pets/{petId}

  • breakingThe `404` response is gone.

DELETE /pets/{petId}

  • addedThe operation was added.

components.schemas.Owner

  • addedThe schema was added.

info.version

  • changed`1.0.0` → `2.0.0`

What counts as breaking

In one sentence: a request the previous document accepted might now be rejected, or a response it promised is one an existing client can no longer handle. So: removing a path, an operation, a response code, a media type or a response property; adding a required parameter or required request property; optional becoming required; a type change; removing a request enum value; adding a response enum value (the client’s switch has no case for it); and a response property going from required to optional. The opposite directions, and changes to descriptions, tags, operationId and deprecated flags, are compatible.

  • · Schemas are compared after following $ref within the document, so renaming a component is not a change. Path parameters are matched by position rather than by name.
  • · The insides of oneOf·anyOf·not are compared as a whole and reported as breaking if they differ — this does not reason about them.
  • · A $ref pointing at another file is not followed. Bundle the files into one first.

To check a single document, use OpenAPI lint.

How to use it

  1. Put the old and new documents side by side

    JSON or YAML for either. Both are parsed in this tab, so an unreleased spec does not leave your machine.

  2. Read the verdict

    Breaking count, additive count, and what that means for the version number: a major bump, a minor one, or nothing at all because the contract is unchanged.

  3. Filter to the breaking ones and read them

    Grouped by operation, each with what changed. The Markdown copy is shaped for a pull-request description.

Common questions

Why is adding a response enum value breaking?

Because an existing client has a switch with no case for it. Adding to a *request* enum is safe — the server accepts more than before; adding to a response enum sends clients something they were never told about. The direction is what decides it.

I renamed a component and it reports no change.

Correct. Schemas are compared after following `$ref`, so a renamed component with the same shape is the same contract. Names in `components` are internal; clients see the resolved shape.

Is a required response property becoming optional really breaking?

Yes, from the client’s side. It was promised the field would always be there and its code reads it unconditionally; making it optional means the server may now omit it. Required and optional swap directions between request and response, which is the part people get backwards.

How does it handle `oneOf` and `anyOf`?

They are compared as a whole and reported as breaking if they differ at all. Reasoning about whether one union is a superset of another is a decision procedure this deliberately does not attempt; a false "breaking" you can dismiss is better than a false "safe" you cannot.

Feed it from

Next

These accept what this tool produces, so the result can go straight in.