APIs.io · API Governance Rules
APIs.io API Rules
Spectral linting rules defining API design standards and conventions for APIs.io.
71 Rules
error 48
warn 23
Rule Categories
contact
document
get
header
info
license
media
operation
parameter
path
post
property
request
response
schema
security
server
tag
Rules
error
document-openapi-32
APIs.io contracts are published as OpenAPI 3.2.0.
$.openapi
error
document-structure
A contract carries info, servers, tags, paths, and components.
$
error
info-title-required
Info must have a title.
$.info
warn
info-title-format
Titles start with "APIs.io" so every contract names its network.
$.info.title
error
info-version-required
Info must have a version.
$.info
warn
info-version-semver
Versions are numeric semver (1.6.0), not marketing strings.
$.info.version
error
info-description-substantive
The info description tells a reader what the surface is — at least a real paragraph.
$.info
error
info-contact-required
Info must say who runs the API.
$.info
error
info-license-required
Info must say what license the contract is published under.
$.info
warn
contact-complete
The contact carries a name, a URL, and an email.
$.info.contact
warn
license-complete
The license carries a name and a URL.
$.info.license
error
server-url-https
Server URLs must use HTTPS.
$.servers[*].url
warn
server-url-expected-domain
APIs.io contracts serve from the apis.io domain.
$.servers[*].url
warn
server-url-no-trailing-slash
Server URLs carry no trailing slash — paths supply their own.
$.servers[*].url
warn
server-description-required
Every server says which environment it is.
$.servers[*]
error
tag-name-required
Every tag has a name.
$.tags[*]
error
tag-description-required
Every tag describes the surface it groups.
$.tags[*]
warn
tag-name-title-case
Tag names read as titles.
$.tags[*].name
warn
path-kebab-case
Path segments are kebab-case (or a {parameter}).
$.paths[*]~
error
path-no-trailing-slash
Paths carry no trailing slash.
$.paths[*]~
error
path-declares-an-operation
A path item without an operation documents nothing.
$.paths[*]
error
operation-summary-required
Every operation has a summary.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
warn
operation-summary-sentence
Summaries read as sentences — capitalised, 10–120 characters, closed with punctuation.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].summary
error
operation-description-required
Every operation says what it returns and how its filters behave.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
error
operation-id-required
Every operation has an operationId.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
error
operation-id-camel-case
OperationIds are camelCase.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].operationId
error
operation-tags-required
Every operation carries at least one tag.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
error
operation-security-declared
Every operation states its security requirement — an explicit empty requirement for keyless calls, never silence.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
error
operation-tier-declared
Every operation declares its commercial tier via x-tier.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
warn
operation-mcp-tool-declared
Every operation names the MCP tool that serves the same call — the API → MCP experience chain is 1:1.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
error
operation-2xx-response
Every operation documents its success shape.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses
error
operation-get-by-id-documents-404
A GET addressing a named resource documents the 404 a miss returns.
$.paths[?(@property.match(/\{/))][get].responses
error
operation-gated-documents-402
A gated operation documents the 402 the live API answers keyless callers with — measured, 403 is never returned.
$.paths[*][get,put,post,delete,options,head,patch,trace,query]
error
get-operations-have-no-request-body
GET operations carry no request body.
$.paths[*].get
error
post-operations-have-request-body
POST operations document what they accept.
$.paths[*].post
error
parameter-name-required
Every parameter has a name.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[*]$.components.parameters[*]
error
parameter-in-required
Every parameter says where it lives.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[*]$.components.parameters[*]
error
parameter-description-required
Every parameter is described.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[*]$.components.parameters[*]
error
parameter-schema-required
Every parameter carries a schema.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[*]$.components.parameters[*]
error
parameter-name-snake-case
Parameter names are snake_case.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[*].name$.components.parameters[*].name
error
parameter-path-required-true
Path parameters are always required.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[?(@.in=='path')]$.components.parameters[?(@.in=='path')]
warn
parameter-schema-bounded
No parameter is unbounded on the wire — a maxLength, maximum, enum, or maxItems (booleans are closed already).
$.paths[*][get,put,post,delete,options,head,patch,trace,query].parameters[*].schema$.components.parameters[*].schema
error
request-body-description-required
Every request body says what belongs in it.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody
error
request-body-content-required
Every request body defines its content.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody
warn
request-body-required-declared
Every request body states whether it is required, explicitly.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody
error
request-body-json
Request bodies speak application/json.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content
error
media-type-schema-required
Every media type carries a schema — a payload with no schema is undocumented.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].content[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*]$.components.responses[*].content[*]
warn
media-type-schema-typed
A media type schema declares its type or points at a named component.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].content[*].schema$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*].schema$.components.responses[*].content[*].schema
warn
media-type-supported
APIs.io answers in application/json, or application/problem+json for failures — nothing else.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].content$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content$.components.responses[*].content
error
response-description-required
Every response is described.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*]$.components.responses[*]
error
response-code-valid
Response keys are HTTP status codes (or default).
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses
error
response-content-required
Every response except 204 defines its body.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[?(@property != '204')]$.components.responses[*]
warn
response-error-uses-problem-json
Failures are RFC 9457 Problem Details, served as application/problem+json.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[?(@property.match(/^[45]/))]
warn
response-error-carries-example
Every shared failure response shows a real example body.
$.components.responses[*].content[*]
warn
response-rate-limit-headers
Every response documents the four rate-limit headers the live API actually sends — measured on a 200, a 404 and a 402.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*]$.components.responses[*]
error
response-header-described
Every header a response declares is described.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].headers[*]$.components.responses[*].headers[*]
error
response-header-has-schema
Every header a response declares carries a schema.
$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].headers[*]$.components.responses[*].headers[*]
error
header-description-required
Every declared header is described.
$.components.headers[*]
error
header-schema-required
Every declared header carries a schema.
$.components.headers[*]
warn
header-example-recommended
Every declared header shows what it looks like on the wire.
$.components.headers[*]
error
schema-name-pascal-case
Component schema names are PascalCase.
$.components.schemas
error
schema-description-required
Every named schema says what it models.
$.components.schemas[*]
error
schema-type-required
Every named schema declares its type (or composes with allOf/oneOf/anyOf).
$.components.schemas[*]
warn
schema-object-has-shape
An object schema declares properties, or says explicitly that it is open with additionalProperties.
$.components.schemas[?(@ && @.type == 'object')]
error
property-description-required
Every schema property is described — a field a reader has to guess at is not documented.
$.components.schemas[*].properties[*]$.components.schemas[*].properties[*].properties[*]$.components.schemas[*].properties[*].items.properties[*]$.components.schemas[*].properties[*].allOf[*].properties[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*].schema.properties[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].content[*].schema.properties[*]
error
property-type-declared
Every schema property declares its type (or composes / points at a named schema).
$.components.schemas[*].properties[*]$.components.schemas[*].properties[*].properties[*]$.components.schemas[*].properties[*].items.properties[*]$.components.schemas[*].properties[*].allOf[*].properties[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*].schema.properties[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].responses[*].content[*].schema.properties[*]
warn
property-name-snake-case
Schema properties are snake_case. `baseURL` and `humanURL` are exempt by name — they are APIs.json vocabulary, the wire format the catalog is built on, and not this API's to rename.
$.components.schemas[*].properties$.components.schemas[*].properties[*].properties$.components.schemas[*].properties[*].items.properties$.components.schemas[*].properties[*].allOf[*].properties$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*].schema.properties
warn
property-scalar-carries-example
Every scalar property shows a real value — examples are what agents and readers reach for first.
$.components.schemas[*].properties[*]$.components.schemas[*].properties[*].properties[*]$.components.schemas[*].properties[*].items.properties[*]$.components.schemas[*].properties[*].allOf[*].properties[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*].schema.properties[*]
warn
property-string-bounded
Every string property leaves the API bounded — maxLength, an enum, or a closed format.
$.components.schemas[*].properties[*]$.components.schemas[*].properties[*].properties[*]$.components.schemas[*].properties[*].items.properties[*]$.components.schemas[*].properties[*].allOf[*].properties[*]$.paths[*][get,put,post,delete,options,head,patch,trace,query].requestBody.content[*].schema.properties[*]
error
security-scheme-description-required
Every security scheme explains how keys behave — including that gated calls answer 402, not a refusal.
$.components.securitySchemes[*]
error
security-scheme-shape
The APIs.io scheme is an apiKey in the X-API-Key header.
$.components.securitySchemes[*]
Spectral Ruleset
Work with this as data
Every ruleset here is available over the APIs.io API and to AI agents over MCP.