Zuplo · API Governance Rules

Zuplo — rate my openapi rules

Spectral linting rules defining API design standards and conventions for Zuplo.

21 Rules error 1 warn 10 info 10
Published by Zuplo Served by the provider at https://github.com/zuplo/rate-my-openapi/blob/24819fbe7576e2811b3be2750312e2cea64bcf14/rulesets/.spectral.yaml; the copy below was fetched from there.
View Rules File View on GitHub

Rule Categories

apimatic owasp:api1:2019 owasp:api2:2019 owasp:api3:2019 owasp:api4:2019 owasp:api7:2019 redocly speakeasy

Rules

warn
apimatic-security-defined
Apimatic: It is recommended to define at least one authentication scheme globally using the securitySchemes property in the components section and use it globally to authenticate all the endpoints or use it for specific endpoints.
$
info
apimatic-operationId-max-length
Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible.
$.paths..operationId
info
apimatic-parameter-name-max-length
Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible.
$.paths..parameters[*].name
info
apimatic-header-name-max-length
Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible.
$.paths..responses..headers[*]~
info
apimatic-component-name-max-length
Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible.
$.components.[schemas,requestBodies,responses,parameters,examples,headers,securitySchemes][*]~
info
apimatic-no-inline-response-schema
Apimatic: In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline.
$.paths..responses..content..schema
info
apimatic-no-inline-parameter-schema
Apimatic: In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline.
$.paths..parameters[*].schema
info
apimatic-no-inline-request-body-schema
Apimatic: In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline.
$.paths..requestBody.content..schema
warn
owasp:api1:2019-no-numeric-ids
OWASP API1:2019 - Use random IDs that cannot be guessed. UUIDs are preferred.
$.paths..parameters[*][?(@property === "name" && (@ === "id" || @.match(/(_id|Id|-id)$/)))]^.schema
warn
owasp:api2:2019-no-http-basic
Basic authentication credentials transported over network are more susceptible to interception than other forms of authentication, and as they are not encrypted it means passwords and tokens are more easily leaked.
$.components.securitySchemes[*]
warn
owasp:api3:2019-define-error-responses-401
OWASP API Security recommends defining schemas for all responses, even errors. The 401 describes what happens when a request is unauthorized, so its important to define this not just for documentation, but to empower contract testing to make sure the proper JSON structure is being returned instead of leaking implementation details in backtraces.
$.paths..responses
warn
owasp:api3:2019-define-error-responses-500
OWASP API Security recommends defining schemas for all responses, even errors. The 500 describes what happens when a request fails with an internal server error, so its important to define this not just for documentation, but to empower contract testing to make sure the proper JSON structure is being returned instead of leaking implementation details in backtraces.
$.paths..responses
info
owasp:api4:2019-rate-limit
Define proper rate limiting to avoid attackers overloading the API. There are many ways to implement rate-limiting, but most of them involve using HTTP headers, and there are two popular ways to do that: IETF Draft HTTP RateLimit Headers:. https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/ Customer headers like X-Rate-Limit-Limit (Twitter: https://developer.twitter.com/en/docs/twitter-api/rate-limits) or X-RateLimit-Limit (GitHub: https://docs.github.com/en/rest/overview/resources-in-the-rest-api)
$.paths[*]..responses[?(@property.match(/^(2|4)/))]
info
owasp:api4:2019-rate-limit-retry-after
Define proper rate limiting to avoid attackers overloading the API. Part of that involves setting a Retry-After header so well meaning consumers are not polling and potentially exacerbating problems.
$..responses[429].headers
info
owasp:api4:2019-rate-limit-responses-429
OWASP API Security recommends defining schemas for all responses, even errors. A HTTP 429 response signals the API client is making too many requests, and will supply information about when to retry so that the client can back off calmly without everything breaking. Defining this response is important not just for documentation, but to empower contract testing to make sure the proper JSON structure is being returned instead of leaking implementation details in backtraces. It also ensures your API/framework/gateway actually has rate limiting set up.
$.paths..responses
warn
owasp:api7:2019-security-hosts-https-oas3
All server interactions MUST use the https protocol, meaning server URLs should begin `https://`. Learn more about the importance of TLS (over SSL) here: https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html
$.servers..url
warn
redocly-operation-summary
Operation summaries are used to generate API docs. Redocly uses the summary as the header for the operation, as well as the sidebar navigation text. See https://redocly.com/docs/cli/rules/operation-summary/
$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]
error
redocly-components-invalid-map-name
All components MUST use keys that match the regular expression: ^[a-zA-Z0-9.-_]+$ See https://redocly.com/docs/cli/rules/spec-components-invalid-map-name/
$.components.schemas[*]~$.components.parameters[*]~$.components.securitySchemes[*]~$.components.requestBodies[*]~$.components.responses[*]~$.components.headers[*]~$.components.examples[*]~$.components.links[*]~$.components.callbacks[*]~
warn
redocly-no-server-variables-empty-enum
If you use server variables, there are generally two kinds: tenant-driven and environment-driven. In the case of environment-driven variables, you may want to predefine all of the possible values. See https://redocly.com/docs/cli/rules/no-server-variables-empty-enum/
$.servers[*].variables[*]
warn
redocly-no-undefined-server-variable
Document all declared server variables to make it easier to consume the API. See https://redocly.com/docs/cli/rules/no-undefined-server-variable/
$.servers[?(@.url.match(/({)/))]
warn
speakeasy-parameter-name-nonempty
All parameters must have a non-empty name. See https://www.speakeasyapi.dev/docs/openapi/validation
$.paths..parameters..name

Spectral Ruleset

Raw ↑
# harvested from https://github.com/zuplo/rate-my-openapi/blob/24819fbe7576e2811b3be2750312e2cea64bcf14/rulesets/.spectral.yaml on 2026-10-09 — a Spectral ruleset published in the provider's own GitHub repository (zuplo/rate-my-openapi); found by GitHub code search, fetched verbatim
x-method: harvested
x-stamped: 2026-10-09
x-source-url: https://github.com/zuplo/rate-my-openapi/blob/24819fbe7576e2811b3be2750312e2cea64bcf14/rulesets/.spectral.yaml
extends: [[spectral:oas, all]]
rules:
  # ----- This part is in sync with rule.vacuum.yaml
  # paths-kebab-case: false
  # operation-4xx-response: missing
  operation-success-response: warn
  operation-operationId-valid-in-url: warn
  operation-operationId: warn
  contact-properties: info
  info-contact: info
  info-license: info
  license-url: info
  tag-description: info
  operation-tags: hint
  openapi-tags: warn
  operation-singular-tag: false
  # The alphabetical rule seems to fail on Box
  openapi-tags-alphabetical: false
  # description-duplication: missing
  # Covered by oas3-api-servers
  # oas3-host-trailing-slash: missing
  no-$ref-siblings: false
  # ----- End of synced part

  # Copy of .spectral-supplement.yaml
  apimatic-security-defined:
    description: "Apimatic: It is recommended to define at least one authentication scheme globally using the securitySchemes property in the components section and use it globally to authenticate all the endpoints or use it for specific endpoints."
    message: "'security' must be defined and not empty"
    given: "$"
    severity: warn
    then:
      field: "security"
      function: schema
      functionOptions:
        schema:
          type: array
          items:
            type: object
          minItems: 1
  apimatic-operationId-max-length:
    description: "Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible."
    message: "{{property}} must be shorter than 50 characters"
    given: "$.paths..operationId"
    severity: info
    type: validation
    then:
      function: length
      functionOptions:
        max: 50
  apimatic-parameter-name-max-length:
    description: "Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible."
    message: "{{property}} must be shorter than 50 characters"
    given: "$.paths..parameters[*].name"
    severity: info
    type: validation
    then:
      function: length
      functionOptions:
        max: 50
  apimatic-header-name-max-length:
    description: "Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible."
    message: "{{property}} must be shorter than 50 characters"
    given: "$.paths..responses..headers[*]~"
    severity: info
    type: validation
    then:
      function: length
      functionOptions:
        max: 50
  apimatic-component-name-max-length:
    description: "Apimatic: Longer content can lead to issues in the code generation process. It's best to keep the length to a maximum of 50 characters, wherever possible."
    message: "{{property}} must be shorter than 50 characters"
    given: "$.components.[schemas,requestBodies,responses,parameters,examples,headers,securitySchemes][*]~"
    severity: info
    type: validation
    then:
      function: length
      functionOptions:
        max: 50
  apimatic-no-inline-response-schema:
    given: $.paths..responses..content..schema
    description: "Apimatic: In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline."
    message: "Response schema: {{path}} should be moved to components/schemas and referenced"
    severity: info
    resolved: false
    then:
      field: "$ref"
      function: truthy
  apimatic-no-inline-parameter-schema:
    description: "Apimatic: In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline."
    message: "Parameter schema: {{path}} should be moved to components/schemas and referenced"
    given: $.paths..parameters[*].schema
    severity: info
    resolved: false
    then:
      field: "$ref"
      function: truthy
  apimatic-no-inline-request-body-schema:
    description: "Apimatic: In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline."
    message: "Request Body schema: {{path}} should be moved to components/schemas and referenced"
    given: $.paths..requestBody.content..schema
    severity: info
    resolved: false
    then:
      field: "$ref"
      function: truthy
  owasp:api1:2019-no-numeric-ids:
    description: "OWASP API1:2019 - Use random IDs that cannot be guessed. UUIDs are preferred."
    severity: warn
    given: '$.paths..parameters[*][?(@property === "name" && (@ === "id" || @.match(/(_id|Id|-id)$/)))]^.schema'
    then:
      function: schema
      functionOptions:
        schema:
          type: "object"
          not:
            properties:
              type:
                const: "integer"
          properties:
            format:
              const: "uuid"
  owasp:api2:2019-no-http-basic:
    message: "Security scheme uses HTTP Basic. Use a more secure authentication method, like OAuth 2.0."
    description: "Basic authentication credentials transported over network are more susceptible to interception than other forms of authentication, and as they are not encrypted it means passwords and tokens are more easily leaked."
    severity: warn
    given: "$.components.securitySchemes[*]"
    then:
      field: "scheme"
      function: pattern
      functionOptions:
        notMatch: "basic"
  owasp:api3:2019-define-error-responses-401:
    message: "Operation is missing {{property}}."
    description: "OWASP API Security recommends defining schemas for all responses, even errors. The 401 describes what happens when a request is unauthorized, so its important to define this not just for documentation, but to empower contract testing to make sure the proper JSON structure is being returned instead of leaking implementation details in backtraces."
    severity: warn
    given: "$.paths..responses"
    then:
      - field: "401"
        function: truthy
      - field: "401.content"
        function: truthy
  owasp:api3:2019-define-error-responses-500:
    message: "Operation is missing {{property}}."
    description: "OWASP API Security recommends defining schemas for all responses, even errors. The 500 describes what happens when a request fails with an internal server error, so its important to define this not just for documentation, but to empower contract testing to make sure the proper JSON structure is being returned instead of leaking implementation details in backtraces."
    severity: warn
    given: "$.paths..responses"
    then:
      - field: "500"
        function: truthy
      - field: "500.content"
        function: truthy
  owasp:api4:2019-rate-limit:
    message: "All 2XX and 4XX responses should define rate limiting headers."
    description: "Define proper rate limiting to avoid attackers overloading the API. There are many ways to implement rate-limiting, but most of them involve using HTTP headers, and there are two popular ways to do that:\n\nIETF Draft HTTP RateLimit Headers:. https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/\n\nCustomer headers like X-Rate-Limit-Limit (Twitter: https://developer.twitter.com/en/docs/twitter-api/rate-limits) or X-RateLimit-Limit (GitHub: https://docs.github.com/en/rest/overview/resources-in-the-rest-api)"
    severity: info
    formats: [oas3]
    given: "$.paths[*]..responses[?(@property.match(/^(2|4)/))]"
    then:
      field: "headers"
      function: schema
      functionOptions:
        schema:
          type: "object"
          oneOf:
            - required: ["RateLimit-Limit", "RateLimit-Reset"]
            - required: ["X-RateLimit-Limit"]
            - required: ["X-Rate-Limit-Limit"]
  owasp:api4:2019-rate-limit-retry-after:
    message: "A 429 response should define a Retry-After header."
    description: "Define proper rate limiting to avoid attackers overloading the API. Part of that involves setting a Retry-After header so well meaning consumers are not polling and potentially exacerbating problems."
    severity: info
    formats: [oas3]
    given: "$..responses[429].headers"
    then:
      field: "Retry-After"
      function: defined
  owasp:api4:2019-rate-limit-responses-429:
    message: "Operation is missing rate limiting response in {{property}}."
    description: "OWASP API Security recommends defining schemas for all responses, even errors. A HTTP 429 response signals the API client is making too many requests, and will supply information about when to retry so that the client can back off calmly without everything breaking. Defining this response is important not just for documentation, but to empower contract testing to make sure the proper JSON structure is being returned instead of leaking implementation details in backtraces. It also ensures your API/framework/gateway actually has rate limiting set up."
    severity: info
    given: "$.paths..responses"
    then:
      - field: "429"
        function: truthy
      - field: "429.content"
        function: truthy
  owasp:api7:2019-security-hosts-https-oas3:
    message: "Server URLs MUST begin https://, and no other protocol is permitted."
    description: "All server interactions MUST use the https protocol, meaning server URLs should begin `https://`.\n\nLearn more about the importance of TLS (over SSL) here: https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html"
    severity: warn
    formats: [oas3]
    given: "$.servers..url"
    then:
      function: pattern
      functionOptions:
        match: "/^https:/"
  redocly-operation-summary:
    message: Operation should have a summary
    description: "Operation summaries are used to generate API docs. Redocly uses the summary as the header for the operation, as well as the sidebar navigation text. See https://redocly.com/docs/cli/rules/operation-summary/"
    severity: warn
    given: "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]"
    then:
      - field: "summary"
        function: truthy
  redocly-components-invalid-map-name:
    message: All component names MUST consist of the following characters `A..Z a..z 0..9 . _ -`
    description: "All components MUST use keys that match the regular expression: ^[a-zA-Z0-9.-_]+$ See https://redocly.com/docs/cli/rules/spec-components-invalid-map-name/"
    severity: error
    given:
      - "$.components.schemas[*]~"
      - "$.components.parameters[*]~"
      - "$.components.securitySchemes[*]~"
      - "$.components.requestBodies[*]~"
      - "$.components.responses[*]~"
      - "$.components.headers[*]~"
      - "$.components.examples[*]~"
      - "$.components.links[*]~"
      - "$.components.callbacks[*]~"
    then:
      function: pattern
      functionOptions:
        match: ^[a-zA-Z0-9._-]+$
  redocly-no-server-variables-empty-enum:
    message: "Server variables defined, but no enum of options provided"
    description: "If you use server variables, there are generally two kinds: tenant-driven and environment-driven. In the case of environment-driven variables, you may want to predefine all of the possible values. See https://redocly.com/docs/cli/rules/no-server-variables-empty-enum/"
    severity: warn
    given: "$.servers[*].variables[*]"
    then:
      field: "enum"
      function: defined
  redocly-no-undefined-server-variable:
    message: "Variable was declared in server URL but not documented"
    description: "Document all declared server variables to make it easier to consume the API. See https://redocly.com/docs/cli/rules/no-undefined-server-variable/"
    severity: warn
    given: "$.servers[?(@.url.match(/({)/))]"
    then:
      field: "variables"
      function: defined
  speakeasy-parameter-name-nonempty:
    message: "Parameter has an empty name"
    description: "All parameters must have a non-empty name. See https://www.speakeasyapi.dev/docs/openapi/validation"
    severity: warn
    given: "$.paths..parameters..name"
    then:
      function: length
      functionOptions:
        min: 1
  # TODO: Integrate and benchmark custom JS rules
  # speakeasy-duplicate-tag:
  #   message: "Duplicate tags detected"
  #   description: "Tag names must be unique. See https://www.speakeasyapi.dev/docs/openapi/validation"
  #   severity: warn
  #   given: "$.tags"
  #   then:
  #     function: "checkUniqueTags"
  # speakeasy-validate-requests:
  #   message: "Request body content type is not a valid mime type"
  #   description: "Request body content type must be a valid mime type. See https://www.speakeasyapi.dev/docs/openapi/validation"
  #   severity: warn
  #   given: "$.paths..requestBody.content"
  #   then:
  #     function: "validateContentMimeType"
  # speakeasy-validate-responses:
  #   message: "Response content type is not a valid mime type"
  #   description: "Responses content type must be a valid mime type. See https://www.speakeasyapi.dev/docs/openapi/validation"
  #   severity: warn
  #   given: "$.paths..responses..content"
  #   then:
  #     function: "validateContentMimeType"

Work with this as data

Every ruleset here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for spectral rules

4 MCP tools reach this
  • find_rulesBrowse and filter every ruleset in the catalog.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This ruleset
curl "https://apis.io/api/v1/rules/zuplo-rate-my-openapi-spectral-rules"
All spectral rules
curl "https://apis.io/api/v1/rules?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.