# OpenAPI Change Preflight | API Contract Checks (`fluffy_ingot/openapi-change-preflight`) Actor

Compare two OpenAPI JSON specs for removed operations, required inputs and selected type or enum changes. Download HTML, CSV and JSON review reports.

- **URL**: https://apify.com/fluffy\_ingot/openapi-change-preflight.md
- **Developed by:** [yipee Gameplay](https://apify.com/fluffy_ingot) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $500.00 / 1,000 completed api spec comparisons

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## OpenAPI Change Preflight

Compare two OpenAPI JSON specifications before an API release. Get a readable report of removed operations, newly required request inputs, selected primitive type/enum changes and other contract changes that need review.

This is a bounded preflight for a **selected rule subset**. It does not establish backward compatibility or replace a full OpenAPI validator or comprehensive compatibility checker.

### Quick start

1. Paste the previous specification into **Previous OpenAPI JSON**.
2. Paste the proposed specification into **Current OpenAPI JSON**.
3. Run the Actor and open **Review report**, **JSON report**, or **CSV findings**.

Both inputs are JSON **strings**, including when calling the Actor through an API. The default example is synthetic: it removes `GET /legacy`, requires the `state` query parameter and removes the accepted request enum value `closed`. It produces **three potential-breaking findings**.

```json
{
  "previousSpec": "{\"openapi\":\"3.1.2\",\"info\":{\"title\":\"Demo\",\"version\":\"1\"},\"paths\":{}}",
  "currentSpec": "{\"openapi\":\"3.1.2\",\"info\":{\"title\":\"Demo\",\"version\":\"2\"},\"paths\":{}}"
}
```

### Checks

| Change | Result |
|---|---|
| An existing exact path/method disappears | Potential breaking change |
| A request parameter or request body becomes required | Potential breaking change |
| A direct primitive request type/enum becomes narrower | Potential breaking change |
| A direct primitive response type/enum becomes wider | Potential breaking change |
| An operation is added | Informational |
| Response statuses or media-type entries disappear; response statuses appear | Review required; wildcard/default coverage and consumer behavior matter |
| Security requirements, servers, response metadata or parameter serialization change | Review required |
| Complex schemas, unsupported or unresolved references, callbacks/webhooks | Explicit coverage warning |

Parameter identity uses its location and name; header names are case-insensitive. Operation-level parameters override inherited path parameters. Direct local JSON-pointer references are resolved with a maximum chain of 16 links. External documents are never fetched.

Primitive schema checks support direct `type`, primitive `enum`, and OpenAPI 3.0 `nullable`. They account for the fact that integers are numbers. Type/enum narrowing matters for requests; widening matters for responses. Other schema constraints are not inferred to be safe.

### Outputs and automation

The default key-value store contains `REPORT` (JSON), `report.html` and `findings.csv`. One dataset summary is written only after these reports have been saved. The summary includes `status`, `countsByCode`, `countsBySeverity`, operation counts, complete finding counts, and an explicit truncation flag. `compatibilityEstablished` is always `false`.

Detailed findings contain `severity`, `code`, `location`, `oldValue`, `newValue` and `guidance`. At most 500 findings are returned; counts remain complete for the selected checks. Long displayed values are shortened to 400 characters and locations to 1,000. The HTML escapes input text and has no scripts or external assets. CSV fields are quoted and formula-like cell content is escaped.

For a release workflow, submit the accepted baseline and proposed JSON specs, poll the run to completion, then retrieve `REPORT`. Route potential-breaking or review findings to the API owner. Store a new baseline only after your own acceptance process; this Actor does not deploy APIs or modify specifications. Scheduling the same two unchanged specifications repeatedly does not add information.

### Limits and coverage

- OpenAPI **3.0.x and 3.1.x** supplied as JSON text only. Swagger 2, OpenAPI 3.2, YAML and URL inputs are unsupported.
- Each input: 2 million characters, 100,000 JSON value nodes, nesting depth 40, and 1,000 detected operations. Each path/operation parameter list is capped at 100 entries; each content map at 100 media types; primitive enums at 500 values. A 200,000-step work cap also applies.
- Duplicate JSON keys, non-finite numbers and selected malformed structures fail the run. This is not complete specification validation.
- Objects, arrays, nested property requirements, `allOf`/`oneOf`/`anyOf`, schema formats and constraints, read/write flags, anchors, schema dialect/base-URI changes and reference siblings need separate review. Complex schemas generate coverage warnings even if unchanged.
- Exact path and media-type keys are compared. Renaming a template variable may appear as an operation removal and addition. Response-code removal alone is not classified as definitely breaking.
- Links, response headers, security-scheme definitions, callbacks and webhooks are not semantically compared. No result proves an API will work with a particular client.
- Specs are processed inside the Actor run without URL fetching. Inputs and reports are stored by Apify under your account's retention and access settings; submit only documents you are authorized to process.

### Pricing and run budget

Launch pricing is **$0.50 per completed comparison plus $0.00005 per Actor start**, with platform usage included. One comparison produces one chargeable default dataset summary item, regardless of the number of findings. There is no custom or per-finding charge. The start event can still be charged when input validation or execution fails; the comparison event is produced only after all three reports have been saved.

Set **Maximum total charge to at least $0.50005**, or **$0.51** for a convenient example. A $0.05 budget cannot cover a comparison at the launch price. The Actor checks the effective pay-per-event budget before preparing reports and fails clearly if no comparison event fits. Apify's current Store pricing panel is authoritative; review it before running.

### Background

The rules use a deliberately small subset of the [OpenAPI 3.1.2 specification](https://spec.openapis.org/oas/v3.1.2.html) and [OpenAPI 3.0.4 specification](https://spec.openapis.org/oas/v3.0.4.html). For broader compatibility analysis, the independent [oasdiff project](https://github.com/oasdiff/oasdiff) offers many more checks. This Actor is independently implemented and is not affiliated with the OpenAPI Initiative or oasdiff.

# Actor input Schema

## `previousSpec` (type: `string`):

Paste the complete OpenAPI 3.0.x or 3.1.x document as JSON text. No URLs or YAML. Maximum 2 million characters, 100,000 nodes, nesting 40, and 1,000 operations.

## `currentSpec` (type: `string`):

Paste the complete OpenAPI 3.0.x or 3.1.x document as JSON text. No URLs or YAML. Maximum 2 million characters, 100,000 nodes, nesting 40, and 1,000 operations.

## Actor input object example

```json
{
  "previousSpec": "{\n  \"openapi\": \"3.1.2\",\n  \"info\": {\n    \"title\": \"Synthetic Orders API\",\n    \"version\": \"1.0\"\n  },\n  \"paths\": {\n    \"/orders\": {\n      \"get\": {\n        \"parameters\": [\n          {\n            \"name\": \"state\",\n            \"in\": \"query\",\n            \"required\": false,\n            \"schema\": {\n              \"type\": \"string\",\n              \"enum\": [\n                \"open\",\n                \"closed\"\n              ]\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"Order count\",\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"type\": \"integer\"\n                }\n              }\n            }\n          }\n        }\n      }\n    },\n    \"/legacy\": {\n      \"get\": {\n        \"responses\": {\n          \"200\": {\n            \"description\": \"Legacy\"\n          }\n        }\n      }\n    }\n  }\n}",
  "currentSpec": "{\n  \"openapi\": \"3.1.2\",\n  \"info\": {\n    \"title\": \"Synthetic Orders API\",\n    \"version\": \"2.0\"\n  },\n  \"paths\": {\n    \"/orders\": {\n      \"get\": {\n        \"parameters\": [\n          {\n            \"name\": \"state\",\n            \"in\": \"query\",\n            \"required\": true,\n            \"schema\": {\n              \"type\": \"string\",\n              \"enum\": [\n                \"open\"\n              ]\n            }\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"Order count\",\n            \"content\": {\n              \"application/json\": {\n                \"schema\": {\n                  \"type\": \"integer\"\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
}
```

# Actor output Schema

## `report` (type: `string`):

Readable selected-rule findings, severity and coverage limitations.

## `json` (type: `string`):

Complete counts and up to 500 detailed findings. No compatibility guarantee.

## `csv` (type: `string`):

Quoted, formula-escaped summary and returned findings.

## `summary` (type: `string`):

One summary item per completed comparison.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "previousSpec": `{
  "openapi": "3.1.2",
  "info": {
    "title": "Synthetic Orders API",
    "version": "1.0"
  },
  "paths": {
    "/orders": {
      "get": {
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    },
    "/legacy": {
      "get": {
        "responses": {
          "200": {
            "description": "Legacy"
          }
        }
      }
    }
  }
}`,
    "currentSpec": `{
  "openapi": "3.1.2",
  "info": {
    "title": "Synthetic Orders API",
    "version": "2.0"
  },
  "paths": {
    "/orders": {
      "get": {
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "open"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order count",
            "content": {
              "application/json": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}`
};

// Run the Actor and wait for it to finish
const run = await client.actor("fluffy_ingot/openapi-change-preflight").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "previousSpec": """{
  \"openapi\": \"3.1.2\",
  \"info\": {
    \"title\": \"Synthetic Orders API\",
    \"version\": \"1.0\"
  },
  \"paths\": {
    \"/orders\": {
      \"get\": {
        \"parameters\": [
          {
            \"name\": \"state\",
            \"in\": \"query\",
            \"required\": false,
            \"schema\": {
              \"type\": \"string\",
              \"enum\": [
                \"open\",
                \"closed\"
              ]
            }
          }
        ],
        \"responses\": {
          \"200\": {
            \"description\": \"Order count\",
            \"content\": {
              \"application/json\": {
                \"schema\": {
                  \"type\": \"integer\"
                }
              }
            }
          }
        }
      }
    },
    \"/legacy\": {
      \"get\": {
        \"responses\": {
          \"200\": {
            \"description\": \"Legacy\"
          }
        }
      }
    }
  }
}""",
    "currentSpec": """{
  \"openapi\": \"3.1.2\",
  \"info\": {
    \"title\": \"Synthetic Orders API\",
    \"version\": \"2.0\"
  },
  \"paths\": {
    \"/orders\": {
      \"get\": {
        \"parameters\": [
          {
            \"name\": \"state\",
            \"in\": \"query\",
            \"required\": true,
            \"schema\": {
              \"type\": \"string\",
              \"enum\": [
                \"open\"
              ]
            }
          }
        ],
        \"responses\": {
          \"200\": {
            \"description\": \"Order count\",
            \"content\": {
              \"application/json\": {
                \"schema\": {
                  \"type\": \"integer\"
                }
              }
            }
          }
        }
      }
    }
  }
}""",
}

# Run the Actor and wait for it to finish
run = client.actor("fluffy_ingot/openapi-change-preflight").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "previousSpec": "{\\n  \\"openapi\\": \\"3.1.2\\",\\n  \\"info\\": {\\n    \\"title\\": \\"Synthetic Orders API\\",\\n    \\"version\\": \\"1.0\\"\\n  },\\n  \\"paths\\": {\\n    \\"/orders\\": {\\n      \\"get\\": {\\n        \\"parameters\\": [\\n          {\\n            \\"name\\": \\"state\\",\\n            \\"in\\": \\"query\\",\\n            \\"required\\": false,\\n            \\"schema\\": {\\n              \\"type\\": \\"string\\",\\n              \\"enum\\": [\\n                \\"open\\",\\n                \\"closed\\"\\n              ]\\n            }\\n          }\\n        ],\\n        \\"responses\\": {\\n          \\"200\\": {\\n            \\"description\\": \\"Order count\\",\\n            \\"content\\": {\\n              \\"application/json\\": {\\n                \\"schema\\": {\\n                  \\"type\\": \\"integer\\"\\n                }\\n              }\\n            }\\n          }\\n        }\\n      }\\n    },\\n    \\"/legacy\\": {\\n      \\"get\\": {\\n        \\"responses\\": {\\n          \\"200\\": {\\n            \\"description\\": \\"Legacy\\"\\n          }\\n        }\\n      }\\n    }\\n  }\\n}",
  "currentSpec": "{\\n  \\"openapi\\": \\"3.1.2\\",\\n  \\"info\\": {\\n    \\"title\\": \\"Synthetic Orders API\\",\\n    \\"version\\": \\"2.0\\"\\n  },\\n  \\"paths\\": {\\n    \\"/orders\\": {\\n      \\"get\\": {\\n        \\"parameters\\": [\\n          {\\n            \\"name\\": \\"state\\",\\n            \\"in\\": \\"query\\",\\n            \\"required\\": true,\\n            \\"schema\\": {\\n              \\"type\\": \\"string\\",\\n              \\"enum\\": [\\n                \\"open\\"\\n              ]\\n            }\\n          }\\n        ],\\n        \\"responses\\": {\\n          \\"200\\": {\\n            \\"description\\": \\"Order count\\",\\n            \\"content\\": {\\n              \\"application/json\\": {\\n                \\"schema\\": {\\n                  \\"type\\": \\"integer\\"\\n                }\\n              }\\n            }\\n          }\\n        }\\n      }\\n    }\\n  }\\n}"
}' |
apify call fluffy_ingot/openapi-change-preflight --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fluffy_ingot/openapi-change-preflight"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/pJ7mFaTIM6gyzlVzR/builds/mPnmEmvaoBuzoe8OE/openapi.json
