FRED · API Governance Rules

FRED API Rules

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

44 Rules error 20 warn 18 info 6
View Rules File View on GitHub

Rule Categories

description examples external global http info no openapi operation parameter paths response schema security servers

Rules

error
info-title-fred-prefix
API title must start with "FRED".
$.info.title
error
info-description-required
Info description is required and should be substantive.
$.info
warn
info-description-minlength
Info description should be at least 120 characters.
$.info.description
error
info-version-required
Info version is required.
$.info
warn
info-contact-required
Info contact block is required (St. Louis Fed Research Division).
$.info
warn
info-license-required
Info license block is required.
$.info
error
openapi-version-3
Must use OpenAPI 3.x.
$.openapi
error
servers-required
At least one server must be defined.
$.servers
error
servers-https-only
FRED endpoints are HTTPS only.
$.servers[*].url
warn
servers-stlouisfed-host
Server URL must be on api.stlouisfed.org.
$.servers[*].url
warn
paths-snake-case
FRED paths use snake_case segments (e.g. /release/related_tags).
$.paths.*~
error
paths-no-trailing-slash
No trailing slashes on path keys.
$.paths.*~
error
paths-no-query-string
Paths must not embed query strings.
$.paths.*~
error
operation-operationid-required
Every operation must have an operationId.
$.paths.*.*
warn
operation-operationid-camelcase
operationId must be lowerCamelCase, verb-first.
$.paths.*.*.operationId
error
operation-summary-required
Every operation must have a summary.
$.paths.*.*
warn
operation-summary-fred-prefix
Summaries should start with "FRED" so they read well in catalogs.
$.paths.*.*.summary
warn
operation-description-required
Every operation must have a description.
$.paths.*.*
error
operation-tags-required
Every operation must have at least one tag.
$.paths.*.*
info
operation-microcks-extension
Operations should carry x-microcks-operation for mock-server compatibility.
$.paths.*.*
warn
global-tags-defined
Top-level tags array must be defined and non-empty.
$
warn
global-tag-description
Each tag should have a description.
$.tags[*]
warn
global-tag-title-case
Tag names use Title Case (Categories, Releases, Series, Sources, Tags, Observations, Series Group, Regional Data).
$.tags[*].name
warn
parameter-description-required
Every parameter needs a description.
$.paths.*.*.parameters[*]
error
parameter-snake-case
FRED parameters use snake_case (api_key, series_id, file_type, realtime_start).
$.paths.*.*.parameters[?(@.name)]
error
parameter-api-key-required-on-every-operation
Every operation must include api_key (directly or via $ref).
$.paths.*.*
warn
parameter-pagination-limit-offset
FRED paginates with limit + offset. Don't introduce page/size, cursor, or next_token.
$.paths.*.*.parameters[?(@.name)].name
error
parameter-realtime-period-snake
Real-time period parameters must be realtime_start / realtime_end (not realTimeStart, real_time_start).
$.paths.*.*.parameters[?(@.name=='realTimeStart' || @.name=='real_time_start' || @.name=='realTimeEnd' || @.name=='real_time_end')]
error
no-request-body-on-get
GET operations must not declare a requestBody (FRED is GET-only with query parameters).
$.paths.*.get
error
response-200-required
Every operation must declare a 200 success response.
$.paths.*.*.responses
warn
response-content-json
2xx responses must offer application/json.
$.paths.*.*.responses.200.content
warn
response-schema-required
2xx JSON responses must declare a schema.
$.paths.*.*.responses.200.content.application/json
info
response-400-recommended
Operations should declare a 400 Bad Request response.
$.paths.*.*.responses
info
response-403-recommended
Operations should declare a 403 Forbidden response for missing/invalid api_key.
$.paths.*.*.responses
warn
schema-property-snake-case
Schema properties use snake_case (series_id, realtime_start, last_updated).
$.components.schemas[*].properties.*~
warn
schema-top-level-description
Top-level schemas should carry a description.
$.components.schemas[*]
error
schema-type-defined
Top-level schemas must declare a type.
$.components.schemas[*]
info
schema-seriess-wrapper-allowed
FRED intentionally uses the "seriess" property name on series-list responses. Documenting this for downstream tools so they don't auto-correct.
$.components.schemas.SeriesList.properties
error
security-global-required
Global security must be declared.
$
error
security-apikey-scheme
Security scheme must be apiKey in query (FRED uses ?api_key=...).
$.components.securitySchemes.ApiKeyAuth
error
http-method-get-only
FRED is read-only. Only GET methods are allowed.
$.paths.*
warn
description-no-empty
Descriptions must not be empty strings.
$..description
info
examples-on-responses
2xx JSON responses should carry at least one named example.
$.paths.*.*.responses.200.content.application/json
info
external-docs-encouraged
An externalDocs block helps consumers find the canonical reference page.
$

Spectral Ruleset

Raw ↑
# Spectral ruleset enforcing FRED (Federal Reserve Economic Data) API conventions.
# Derived from the canonical FRED OpenAPI specs in this repo (Categories,
# Releases, Series, Sources, Tags, Observations + GeoFRED Maps).

extends:
  - spectral:oas

rules:

  # ─────────────────────────────────────────────────────────────────────
  # INFO / METADATA
  # ─────────────────────────────────────────────────────────────────────
  info-title-fred-prefix:
    description: API title must start with "FRED".
    message: Info title should begin with "FRED" (e.g. "FRED API", "FRED Maps API (GeoFRED)").
    severity: error
    given: $.info.title
    then:
      function: pattern
      functionOptions:
        match: '^FRED'

  info-description-required:
    description: Info description is required and should be substantive.
    message: Provide a multi-sentence description that explains the API surface.
    severity: error
    given: $.info
    then:
      field: description
      function: truthy

  info-description-minlength:
    description: Info description should be at least 120 characters.
    severity: warn
    given: $.info.description
    then:
      function: length
      functionOptions:
        min: 120

  info-version-required:
    description: Info version is required.
    severity: error
    given: $.info
    then:
      field: version
      function: truthy

  info-contact-required:
    description: Info contact block is required (St. Louis Fed Research Division).
    severity: warn
    given: $.info
    then:
      field: contact
      function: truthy

  info-license-required:
    description: Info license block is required.
    severity: warn
    given: $.info
    then:
      field: license
      function: truthy

  # ─────────────────────────────────────────────────────────────────────
  # OPENAPI VERSION
  # ─────────────────────────────────────────────────────────────────────
  openapi-version-3:
    description: Must use OpenAPI 3.x.
    severity: error
    given: $.openapi
    then:
      function: pattern
      functionOptions:
        match: '^3\.'

  # ─────────────────────────────────────────────────────────────────────
  # SERVERS
  # ─────────────────────────────────────────────────────────────────────
  servers-required:
    description: At least one server must be defined.
    severity: error
    given: $.servers
    then:
      function: schema
      functionOptions:
        schema:
          type: array
          minItems: 1

  servers-https-only:
    description: FRED endpoints are HTTPS only.
    severity: error
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: '^https://'

  servers-stlouisfed-host:
    description: Server URL must be on api.stlouisfed.org.
    severity: warn
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: 'api\.stlouisfed\.org'

  # ─────────────────────────────────────────────────────────────────────
  # PATHS — NAMING CONVENTIONS
  # ─────────────────────────────────────────────────────────────────────
  paths-snake-case:
    description: FRED paths use snake_case segments (e.g. /release/related_tags).
    message: Path segments should be snake_case, lowercase.
    severity: warn
    given: $.paths.*~
    then:
      function: pattern
      functionOptions:
        match: '^/[a-z0-9_/]+$'

  paths-no-trailing-slash:
    description: No trailing slashes on path keys.
    severity: error
    given: $.paths.*~
    then:
      function: pattern
      functionOptions:
        notMatch: '/$'

  paths-no-query-string:
    description: Paths must not embed query strings.
    severity: error
    given: $.paths.*~
    then:
      function: pattern
      functionOptions:
        notMatch: '\?'

  # ─────────────────────────────────────────────────────────────────────
  # OPERATIONS
  # ─────────────────────────────────────────────────────────────────────
  operation-operationid-required:
    description: Every operation must have an operationId.
    severity: error
    given: $.paths.*.*
    then:
      field: operationId
      function: truthy

  operation-operationid-camelcase:
    description: operationId must be lowerCamelCase, verb-first.
    severity: warn
    given: $.paths.*.*.operationId
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-zA-Z0-9]*$'

  operation-summary-required:
    description: Every operation must have a summary.
    severity: error
    given: $.paths.*.*
    then:
      field: summary
      function: truthy

  operation-summary-fred-prefix:
    description: Summaries should start with "FRED" so they read well in catalogs.
    severity: warn
    given: $.paths.*.*.summary
    then:
      function: pattern
      functionOptions:
        match: '^FRED'

  operation-description-required:
    description: Every operation must have a description.
    severity: warn
    given: $.paths.*.*
    then:
      field: description
      function: truthy

  operation-tags-required:
    description: Every operation must have at least one tag.
    severity: error
    given: $.paths.*.*
    then:
      field: tags
      function: schema
      functionOptions:
        schema:
          type: array
          minItems: 1

  operation-microcks-extension:
    description: Operations should carry x-microcks-operation for mock-server compatibility.
    severity: info
    given: $.paths.*.*
    then:
      field: x-microcks-operation
      function: truthy

  # ─────────────────────────────────────────────────────────────────────
  # TAGS
  # ─────────────────────────────────────────────────────────────────────
  global-tags-defined:
    description: Top-level tags array must be defined and non-empty.
    severity: warn
    given: $
    then:
      field: tags
      function: schema
      functionOptions:
        schema:
          type: array
          minItems: 1

  global-tag-description:
    description: Each tag should have a description.
    severity: warn
    given: $.tags[*]
    then:
      field: description
      function: truthy

  global-tag-title-case:
    description: Tag names use Title Case (Categories, Releases, Series, Sources, Tags, Observations, Series Group, Regional Data).
    severity: warn
    given: $.tags[*].name
    then:
      function: pattern
      functionOptions:
        match: '^[A-Z][A-Za-z ]+$'

  # ─────────────────────────────────────────────────────────────────────
  # PARAMETERS
  # ─────────────────────────────────────────────────────────────────────
  parameter-description-required:
    description: Every parameter needs a description.
    severity: warn
    given: $.paths.*.*.parameters[*]
    then:
      field: description
      function: truthy

  parameter-snake-case:
    description: FRED parameters use snake_case (api_key, series_id, file_type, realtime_start).
    severity: error
    given: $.paths.*.*.parameters[?(@.name)]
    then:
      field: name
      function: pattern
      functionOptions:
        match: '^[a-z][a-z0-9_]*$'

  parameter-api-key-required-on-every-operation:
    description: Every operation must include api_key (directly or via $ref).
    message: Each operation must accept the api_key query parameter or reference the shared ApiKey parameter.
    severity: error
    given: $.paths.*.*
    then:
      field: parameters
      function: truthy

  parameter-pagination-limit-offset:
    description: FRED paginates with limit + offset. Don't introduce page/size, cursor, or next_token.
    severity: warn
    given: $.paths.*.*.parameters[?(@.name)].name
    then:
      function: pattern
      functionOptions:
        notMatch: '^(page|page_size|cursor|next_token|page_number)$'

  parameter-realtime-period-snake:
    description: Real-time period parameters must be realtime_start / realtime_end (not realTimeStart, real_time_start).
    severity: error
    given: $.paths.*.*.parameters[?(@.name=='realTimeStart' || @.name=='real_time_start' || @.name=='realTimeEnd' || @.name=='real_time_end')]
    then:
      function: falsy

  # ─────────────────────────────────────────────────────────────────────
  # REQUEST BODIES
  # ─────────────────────────────────────────────────────────────────────
  no-request-body-on-get:
    description: GET operations must not declare a requestBody (FRED is GET-only with query parameters).
    severity: error
    given: $.paths.*.get
    then:
      field: requestBody
      function: falsy

  # ─────────────────────────────────────────────────────────────────────
  # RESPONSES
  # ─────────────────────────────────────────────────────────────────────
  response-200-required:
    description: Every operation must declare a 200 success response.
    severity: error
    given: $.paths.*.*.responses
    then:
      field: '200'
      function: truthy

  response-content-json:
    description: 2xx responses must offer application/json.
    severity: warn
    given: $.paths.*.*.responses.200.content
    then:
      field: application/json
      function: truthy

  response-schema-required:
    description: 2xx JSON responses must declare a schema.
    severity: warn
    given: $.paths.*.*.responses.200.content.application/json
    then:
      field: schema
      function: truthy

  response-400-recommended:
    description: Operations should declare a 400 Bad Request response.
    severity: info
    given: $.paths.*.*.responses
    then:
      field: '400'
      function: truthy

  response-403-recommended:
    description: Operations should declare a 403 Forbidden response for missing/invalid api_key.
    severity: info
    given: $.paths.*.*.responses
    then:
      field: '403'
      function: truthy

  # ─────────────────────────────────────────────────────────────────────
  # SCHEMAS — PROPERTY NAMING
  # ─────────────────────────────────────────────────────────────────────
  schema-property-snake-case:
    description: Schema properties use snake_case (series_id, realtime_start, last_updated).
    severity: warn
    given: $.components.schemas[*].properties.*~
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-z0-9_]*$'

  schema-top-level-description:
    description: Top-level schemas should carry a description.
    severity: warn
    given: $.components.schemas[*]
    then:
      field: description
      function: truthy

  schema-type-defined:
    description: Top-level schemas must declare a type.
    severity: error
    given: $.components.schemas[*]
    then:
      field: type
      function: truthy

  # FRED uses the legacy "seriess" wrapper for series lists. Allow it.
  schema-seriess-wrapper-allowed:
    description: FRED intentionally uses the "seriess" property name on series-list responses. Documenting this for downstream tools so they don't auto-correct.
    severity: info
    given: $.components.schemas.SeriesList.properties
    then:
      field: seriess
      function: truthy

  # ─────────────────────────────────────────────────────────────────────
  # SECURITY
  # ─────────────────────────────────────────────────────────────────────
  security-global-required:
    description: Global security must be declared.
    severity: error
    given: $
    then:
      field: security
      function: truthy

  security-apikey-scheme:
    description: Security scheme must be apiKey in query (FRED uses ?api_key=...).
    severity: error
    given: $.components.securitySchemes.ApiKeyAuth
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          required: [type, in, name]
          properties:
            type: {const: apiKey}
            in: {const: query}
            name: {const: api_key}

  # ─────────────────────────────────────────────────────────────────────
  # HTTP METHOD CONVENTIONS
  # ─────────────────────────────────────────────────────────────────────
  http-method-get-only:
    description: FRED is read-only. Only GET methods are allowed.
    severity: error
    given: $.paths.*
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          additionalProperties: false
          properties:
            get: {}
            parameters: {}
            summary: {}
            description: {}
            servers: {}

  # ─────────────────────────────────────────────────────────────────────
  # GENERAL QUALITY
  # ─────────────────────────────────────────────────────────────────────
  description-no-empty:
    description: Descriptions must not be empty strings.
    severity: warn
    given: '$..description'
    then:
      function: truthy

  examples-on-responses:
    description: 2xx JSON responses should carry at least one named example.
    severity: info
    given: $.paths.*.*.responses.200.content.application/json
    then:
      field: examples
      function: truthy

  external-docs-encouraged:
    description: An externalDocs block helps consumers find the canonical reference page.
    severity: info
    given: $
    then:
      field: externalDocs
      function: truthy