GZW Data API · API Governance Rules

GZW Data API API Rules

Spectral linting rules defining API design standards and conventions for GZW Data API.

12 Rules error 2 warn 6 info 4
View Rules File View on GitHub

Rule Categories

gzw

Rules

error
gzw-operation-operationid
Every operation must declare an operationId. As of 4.2.0, 0 of 362 operations do, which is why every derived artifact in this repository binds tools and skills to METHOD + PATH instead of an id, and why generated SDKs get positional names.
$.paths[*][get,post,put,patch,delete]
warn
gzw-operation-description
Every operation should carry a description as well as a summary. As of 4.2.0 all 362 operations have a one-line summary (e.g. "ammo (79 items)") and none has a description.
$.paths[*][get,post,put,patch,delete]
warn
gzw-operation-tags
Every operation should be tagged so the contract can be navigated and split by resource. As of 4.2.0 the document declares no top-level tags array and tags no operation; API Evangelist derives tags from the first path segment to produce the per-resource specs in openapi/.
$.paths[*][get,post,put,patch,delete]
info
gzw-query-parameters-declared
The docs publish a query vocabulary the contract does not — page, per_page, all, search, sort, arbitrary field equality, and on /search the dataset, fields, fuzzy and limit parameters. 176 of 362 operations declare parameters in 4.2.0 and every one is a PATH parameter; zero query parameters are declared anywhere. A code generator therefore emits a client that cannot paginate, filter or sort. This rule flags collection routes that declare no query parameter.
$.paths[?(!@property.match(/\{.*\}$/))].get
warn
gzw-info-contact
info.contact should name a reachable maintainer. The provider publishes support@gzw-data.dev and security@gzw-data.dev on its own site but neither is in the contract.
$.info
warn
gzw-info-license
info.license should be declared. The source repository is MIT-licensed; the contract says nothing.
$.info
info
gzw-info-terms
info.termsOfService should point at https://gzw-data.dev/terms-of-service/, which the provider publishes and the contract omits.
$.info
info
gzw-security-declared
An unauthenticated API should say so EXPLICITLY with a top-level `security: []`, rather than by omitting the key. As written, a reader cannot distinguish "no auth required" from "auth not documented" — the two most consequential opposite meanings in a contract.
$
error
gzw-responses-declared
REGRESSION GUARD. The 4.0.0 document declared no responses at all on any of its 352 operations. 4.2.0 declares 200/400/404/429 on all 362. Do not lose that.
$.paths[*][get,post,put,patch,delete]
warn
gzw-error-responses-declared
REGRESSION GUARD. Every operation should keep declaring its 404, which is the error a consumer of a slug-keyed read API hits most.
$.paths[*][get,post,put,patch,delete].responses
warn
gzw-rate-limit-retry-after
REGRESSION GUARD. The 429 response declares a Retry-After header in 4.2.0. That header is the only runtime backoff signal an agent gets against the 100 req/min/IP limit — keep it in the contract.
$.paths[*][get,post,put,patch,delete].responses.429
info
gzw-components-reuse
REGRESSION GUARD. Responses should keep $ref-ing the shared envelope schemas rather than inlining them.
$.paths[*][*].responses[*].content['application/json'].schema

Spectral Ruleset

Raw ↑
# GZW Data API — Spectral ruleset
#
# generated: '2026-09-03'
# method: generated
# source: >-
#   Written by API Evangelist against the provider's own published contract
#   (openapi/_original/gzw-data-openapi.json, implementation 4.2.0, fetched 2026-09-03 from
#   https://gzw-data.dev/api/v1/spec). Every rule below fires on a gap that document ACTUALLY has,
#   or guards a property it ACTUALLY gets right and should not lose. Nothing here is a generic
#   style preference imported from another API. Run with:
#     spectral lint openapi/_original/gzw-data-openapi.json --ruleset rules/gzw-data-spectral.yaml
extends: [[spectral:oas, recommended]]
rules:
  # ---- gaps the 4.2.0 contract currently has -------------------------------
  gzw-operation-operationid:
    description: >-
      Every operation must declare an operationId. As of 4.2.0, 0 of 362 operations do, which is why
      every derived artifact in this repository binds tools and skills to METHOD + PATH instead of an
      id, and why generated SDKs get positional names.
    message: "{{path}} has no operationId"
    given: $.paths[*][get,post,put,patch,delete]
    severity: error
    then: { field: operationId, function: truthy }

  gzw-operation-description:
    description: >-
      Every operation should carry a description as well as a summary. As of 4.2.0 all 362 operations
      have a one-line summary (e.g. "ammo (79 items)") and none has a description.
    message: "{{path}} has a summary but no description"
    given: $.paths[*][get,post,put,patch,delete]
    severity: warn
    then: { field: description, function: truthy }

  gzw-operation-tags:
    description: >-
      Every operation should be tagged so the contract can be navigated and split by resource. As of
      4.2.0 the document declares no top-level tags array and tags no operation; API Evangelist
      derives tags from the first path segment to produce the per-resource specs in openapi/.
    message: "{{path}} is untagged"
    given: $.paths[*][get,post,put,patch,delete]
    severity: warn
    then: { field: tags, function: truthy }

  gzw-query-parameters-declared:
    description: >-
      The docs publish a query vocabulary the contract does not — page, per_page, all, search, sort,
      arbitrary field equality, and on /search the dataset, fields, fuzzy and limit parameters. 176 of
      362 operations declare parameters in 4.2.0 and every one is a PATH parameter; zero query
      parameters are declared anywhere. A code generator therefore emits a client that cannot
      paginate, filter or sort. This rule flags collection routes that declare no query parameter.
    message: "{{path}} declares no query parameters, but the docs document page/per_page/all/search/sort on collection routes"
    given: $.paths[?(!@property.match(/\{.*\}$/))].get
    severity: info
    then:
      field: parameters
      function: truthy

  gzw-info-contact:
    description: info.contact should name a reachable maintainer. The provider publishes support@gzw-data.dev and security@gzw-data.dev on its own site but neither is in the contract.
    message: info.contact is missing
    given: $.info
    severity: warn
    then: { field: contact, function: truthy }

  gzw-info-license:
    description: info.license should be declared. The source repository is MIT-licensed; the contract says nothing.
    message: info.license is missing
    given: $.info
    severity: warn
    then: { field: license, function: truthy }

  gzw-info-terms:
    description: info.termsOfService should point at https://gzw-data.dev/terms-of-service/, which the provider publishes and the contract omits.
    message: info.termsOfService is missing
    given: $.info
    severity: info
    then: { field: termsOfService, function: truthy }

  gzw-security-declared:
    description: >-
      An unauthenticated API should say so EXPLICITLY with a top-level `security: []`, rather than by
      omitting the key. As written, a reader cannot distinguish "no auth required" from "auth not
      documented" — the two most consequential opposite meanings in a contract.
    message: "No top-level security requirement is declared; an intentionally public API should declare an empty security array"
    given: $
    severity: info
    then: { field: security, function: defined }

  # ---- properties 4.2.0 got right; these guard against regression ----------
  gzw-responses-declared:
    description: >-
      REGRESSION GUARD. The 4.0.0 document declared no responses at all on any of its 352 operations.
      4.2.0 declares 200/400/404/429 on all 362. Do not lose that.
    message: "{{path}} declares no responses"
    given: $.paths[*][get,post,put,patch,delete]
    severity: error
    then: { field: responses, function: truthy }

  gzw-error-responses-declared:
    description: REGRESSION GUARD. Every operation should keep declaring its 404, which is the error a consumer of a slug-keyed read API hits most.
    message: "{{path}} declares no 404 response"
    given: $.paths[*][get,post,put,patch,delete].responses
    severity: warn
    then: { field: "404", function: truthy }

  gzw-rate-limit-retry-after:
    description: >-
      REGRESSION GUARD. The 429 response declares a Retry-After header in 4.2.0. That header is the
      only runtime backoff signal an agent gets against the 100 req/min/IP limit — keep it in the contract.
    message: The 429 response does not declare a Retry-After header
    given: $.paths[*][get,post,put,patch,delete].responses.429
    severity: warn
    then: { field: headers.Retry-After, function: truthy }

  gzw-components-reuse:
    description: REGRESSION GUARD. Responses should keep $ref-ing the shared envelope schemas rather than inlining them.
    message: Response content schema is inlined rather than $ref-ed to components.schemas
    given: $.paths[*][*].responses[*].content['application/json'].schema
    severity: info
    then: { field: "$ref", function: truthy }

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/gzw-data-spectral"
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.