free_tool
Is your API contract production-ready?
An OpenAPI spec that renders fine in the docs viewer can still leave the API open by default, undocument every error, or hand clients an unbounded list. Paste yours and get a graded report on the design gaps that bite consumers, with the specific change to make for each one.
Runs entirely in your browser. Nothing is uploaded, sent to a server, or stored.
Spec quality
23/100
3 to fix · 6 warnings · 0 passed · 1 note · 3 operations
Grade F, score 23 out of 100, 3 to fix, 6 warnings, 0 passed.Authentication is defined and applied
highNo security schemes, no global security requirement, and no per-operation security. The spec describes an unauthenticated API. If that is intentional (a public read-only API) you can ignore this, otherwise define a scheme and apply it.
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
security:
- bearerAuth: []Operations document error responses
medium2 operations document only success responses: GET /orders, POST /orders. A consumer then has no documented shape for a 4xx or 5xx and has to guess what an error looks like. Add at least one error response, or a "default".
responses:
"200":
description: OK
"400":
description: Bad request
default:
description: Unexpected errorSuccess responses define a content schema
medium1 operation return a 2xx with a body but no content/schema: POST /orders. Without a media type and schema, generated clients and docs can't describe the payload. Add a content block with a schema.
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/User"Collection endpoints support pagination
medium1 collection GET return an array but expose no pagination parameter (limit, offset, page or cursor): GET /orders. As the collection grows, an unbounded list response gets slower and heavier for every caller. Add pagination parameters.
parameters:
- name: limit
in: query
schema: { type: integer, default: 20, maximum: 100 }
- name: cursor
in: query
schema: { type: string }Declares a real server URL
lowEvery declared server points at a local or placeholder host (http://localhost:8080). Published consumers can't reach it. Add the real production server URL.
servers:
- url: https://api.yourdomain.com/v1Operations have a unique operationId
low2 operations have no operationId: GET /orders, POST /orders. Code generators fall back to an awkward auto-name (like getUsersById_1), so client method names get unstable. Give each operation a stable, unique operationId.
get:
operationId: listUsersOperations have a summary or description
low1 operation have neither a summary nor a description: POST /orders. The rendered docs then show a bare method and path with no explanation of what it does. Add a one-line summary.
get:
summary: List all users
description: Returns a paginated list of users.info.title and info.version are present
lowMissing required info field: info.version. Both are required by the spec and are what docs and registries display as the API name and version. Set them.
info:
title: My API
version: 1.0.0Array responses declare a maxItems bound
low1 array response declare no maxItems, so the documented response size is unbounded: GET /orders. Pair the array with pagination, and set maxItems so consumers know the ceiling.
schema:
type: array
maxItems: 100
items:
$ref: "#/components/schemas/User"OpenAPI version notes
Detected OpenAPI 3.0.3. This is a 3.0 document, where "nullable: true" and a single "example" are the idioms. If you move to 3.1, switch to JSON Schema 2020-12 nullability.
A clean spec is the contract, not the system. The auth model, the rate limits, the error envelope, the versioning and the backward-compatibility story are where an API ages well or breaks its consumers. That's the kind of review I do.
Get your API design reviewed: book a callJSON specs are fully supported. YAML is parsed best-effort by extracting the fields a few rules need, so the response-schema, pagination and array-bound checks are skipped on YAML. For a complete lint, paste JSON. It runs entirely in your browser and uploads nothing.
why_it_matters
The spec is the contract every consumer codes against
A spec with no security block describes an open API, an operation with no 4xx or 5xx response hands clients no shape for failure, and a collection GET with no pagination parameter returns an unbounded list that gets slower for everyone as the data grows. None of these break the docs viewer, so they ship.
This linter reads the paths, operations, responses and servers and grades them against those design rules, plus the housekeeping that code generators rely on (a unique operationId, a real server URL, info.title and info.version). It is conservative: it only fails on what it can read in the spec, and warns for the context-dependent calls, so the obvious gaps get caught before a consumer files the bug.
faq
Questions & answers
- What does the OpenAPI Spec Linter check?
- It parses your OpenAPI 3.x document and grades it across security (a global or per-operation security requirement is defined and applied), responses (every operation documents an error response, and success responses define a content schema), design (collection GETs accept pagination parameters, operations have a unique operationId and a summary, array responses cap maxItems), and metadata (a real server URL, info.title and info.version). Each finding cites the path and method and gives the exact change to make.
- Does it support YAML specs?
- JSON is fully supported. YAML is parsed on a best-effort basis: the linter extracts the fields each rule needs with targeted, indentation-aware matching, so it can still check auth, error responses, pagination, operationIds, summaries, servers and info. The two checks that need to read deep into response schemas, the content-schema check and the array maxItems check, are skipped on YAML and marked n/a. For a complete lint, paste the spec as JSON.
- Why does it flag my API as having no authentication?
- Because the spec has no top-level security requirement, no per-operation security, and no securitySchemes under components, which describes an unauthenticated API. If the API really is public and read-only, that finding is safe to ignore. If schemes are defined but never referenced by a security block, the linter warns instead of failing, since defining a scheme does not apply it: an API with schemes but no security requirement is still open by default.
- How does it decide an endpoint should support pagination?
- It looks for GET operations whose path reads like a collection rather than a single item. A path ending in a parameter, like /users/{id}, is treated as an item and skipped; a path like /users or /users/{id}/orders is treated as a list. If such an operation declares no limit, offset, page, cursor or similar parameter, it warns, because an unbounded list response gets slower and heavier for every caller as the data grows. It is a heuristic from the path shape, so if a flagged GET actually returns a single object you can ignore that one.
- Does it validate that my spec is syntactically valid OpenAPI?
- No. It is a design-quality linter, not a full schema validator. It confirms the document is parseable and is OpenAPI 3.x rather than Swagger 2.0, then grades design and documentation gaps. It does not verify every $ref resolves, that schemas are well-formed, or that the document passes the full OpenAPI meta-schema. Run it alongside a structural validator, not instead of one.
- Is my spec uploaded or stored anywhere?
- No. The whole analysis runs in your browser. Nothing you paste is sent to a server or stored, so it is safe to lint a spec for an internal or unreleased API.
Want the API design looked at, not just the spec?
The spec is the contract. I'll review the auth model, rate limits, error envelope, versioning and the backward-compatibility story that decide whether the API ages well or breaks its consumers. Book a call, or leave your email.
Prefer proof first? See how this plays out in real case studies →