Noun Project · API Governance Rules

Noun Project API Rules

Spectral linting rules defining API design standards and conventions for Noun Project.

49 Rules error 18 warn 24 info 7
Authorship not recorded No authorship marker is recorded for this file. It is not presented as the provider's; it was most likely written by an API Evangelist pass before outputs were stamped.
View Rules File View on GitHub

Rule Categories

example external info no openapi operation parameter paths post request response schema security servers tag tags

Rules

error
info-title-noun-project
API title must begin with "Noun Project".
$.info
error
info-description-required
API info description is required and must be substantive.
$.info
warn
info-description-length
API info description should be at least 100 characters.
$.info.description
error
info-version-required
API info version is required.
$.info
warn
info-contact-required
API info contact block is required.
$.info
warn
info-license-required
API info license block is required.
$.info
error
openapi-version-3
OpenAPI version must be 3.0.x or higher.
$.openapi
error
servers-required
At least one server must be defined.
$
error
servers-https-only
Servers must use HTTPS.
$.servers[*].url
warn
servers-noun-project-host
Servers should point to api.thenounproject.com.
$.servers[*].url
error
paths-v2-prefix
All paths must be under the /v2 version prefix.
$.paths
warn
paths-kebab-case
Path segments (excluding parameters) must be kebab-case.
$.paths
error
paths-no-trailing-slash
Paths must not end with a trailing slash.
$.paths
error
paths-no-query-string
Paths must not include query strings.
$.paths
error
operation-operationid-required
Each operation must declare an operationId.
$.paths[*][get,post,put,patch,delete]
warn
operation-operationid-camel-case
operationId must be camelCase.
$.paths[*][get,post,put,patch,delete].operationId
info
operation-operationid-verb-prefix
operationId should start with a recognized verb (get/list/search/add/update/delete/download/autocomplete).
$.paths[*][get,post,put,patch,delete].operationId
error
operation-summary-required
Each operation must declare a summary.
$.paths[*][get,post,put,patch,delete]
warn
operation-summary-title-case-prefix
Operation summaries should start with a capital letter.
$.paths[*][get,post,put,patch,delete].summary
error
operation-description-required
Each operation must declare a description.
$.paths[*][get,post,put,patch,delete]
error
operation-tags-required
Each operation must declare at least one tag.
$.paths[*][get,post,put,patch,delete]
warn
operation-security-required
Each operation must declare a security block (OAuth 1.0a).
$.paths[*][get,post,put,patch,delete]
warn
tags-global-defined
Global tags array must be defined.
$
warn
tag-description-required
Each global tag must have a description.
$.tags[*]
warn
tag-title-case
Tag names must be Title Case (e.g., "Icon", "Collection", "Autocomplete").
$.tags[*].name
warn
parameter-description-required
Each parameter must have a description.
$.paths[*][get,post,put,patch,delete].parameters[*]
warn
parameter-snake-case
Query and path parameter names must be snake_case.
$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query' || @.in == 'path')].name
error
parameter-schema-required
Each parameter must declare a schema with a type.
$.paths[*][get,post,put,patch,delete].parameters[*]
info
parameter-pagination-cursor
Pagination parameters must use prev_page / next_page cursor tokens.
$.paths[*][get].parameters[?(@.in == 'query' && (@.name == 'page' || @.name == 'offset'))]
warn
parameter-thumbnail-size-enum
thumbnail_size parameter must constrain to enum [42, 84, 200].
$.paths[*][get,post,put,patch,delete].parameters[?(@.name == 'thumbnail_size')].schema
warn
request-body-json
Request bodies should use application/json content.
$.paths[*][post,put,patch].requestBody.content
info
request-body-description
Request bodies should have a description.
$.paths[*][post,put,patch].requestBody
error
response-success-required
Each operation must declare a 2xx response.
$.paths[*][get,post,put,patch,delete].responses
warn
response-401-required
Each authenticated operation must declare a 401 response.
$.paths[*][get,post,put,patch,delete].responses
warn
response-429-required
Operations subject to rate limits must declare a 429 response.
$.paths[*][get,post,put,patch,delete].responses
error
response-description-required
Each response must have a description.
$.paths[*][get,post,put,patch,delete].responses[*]
warn
response-json-content
2xx JSON responses must use application/json content type.
$.paths[*][get,post,put,patch,delete].responses[?(@property.match(/^2\\d{2}$/))].content
warn
schema-property-snake-case
Schema property names must be snake_case.
$.components.schemas[*].properties
warn
schema-description-required
Each component schema must have a description.
$.components.schemas[*]
error
schema-type-required
Each component schema must declare a type.
$.components.schemas[*]
info
schema-id-integer
id properties should be typed as integer for Noun Project resources.
$.components.schemas[*].properties.id
info
schema-permalink-uri
permalink properties should use format uri.
$.components.schemas[*].properties.permalink
warn
security-global-defined
Global security must be declared.
$
warn
security-scheme-oauth1
Security schemes must include the oauth1 scheme.
$.components.securitySchemes
warn
security-scheme-description
Each security scheme must have a description.
$.components.securitySchemes[*]
error
no-get-request-body
GET operations must not declare a request body.
$.paths[*][get]
warn
post-request-body-required
POST operations should declare a request body.
$.paths[*][post]
info
example-encouraged
Schema properties should include example values.
$.components.schemas[*].properties[*]
info
external-docs-encouraged
The spec should reference external documentation.
$

Spectral Ruleset

Raw ↑
# Spectral ruleset for Noun Project API
#
# Enforces conventions observed in the Noun Project API V2:
#  - paths use kebab-case under /v2
#  - operation summaries are Title Case prefixed with "Noun Project"
#  - schema and parameter properties use snake_case
#  - OAuth 1.0a is the documented auth scheme
#  - pagination via prev_page / next_page cursor tokens
#  - thumbnail_size enum [42, 84, 200]

extends:
  - spectral:oas

rules:

  # ── INFO / METADATA ────────────────────────────────────────────────────
  info-title-noun-project:
    description: API title must begin with "Noun Project".
    severity: error
    given: $.info
    then:
      field: title
      function: pattern
      functionOptions:
        match: '^Noun Project'

  info-description-required:
    description: API info description is required and must be substantive.
    severity: error
    given: $.info
    then:
      field: description
      function: truthy

  info-description-length:
    description: API info description should be at least 100 characters.
    severity: warn
    given: $.info.description
    then:
      function: length
      functionOptions:
        min: 100

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

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

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

  # ── OPENAPI VERSION ───────────────────────────────────────────────────
  openapi-version-3:
    description: OpenAPI version must be 3.0.x or higher.
    severity: error
    given: $.openapi
    then:
      function: pattern
      functionOptions:
        match: '^3\.'

  # ── SERVERS ───────────────────────────────────────────────────────────
  servers-required:
    description: At least one server must be defined.
    severity: error
    given: $
    then:
      field: servers
      function: truthy

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

  servers-noun-project-host:
    description: Servers should point to api.thenounproject.com.
    severity: warn
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: 'api\.thenounproject\.com'

  # ── PATHS — NAMING CONVENTIONS ────────────────────────────────────────
  paths-v2-prefix:
    description: All paths must be under the /v2 version prefix.
    severity: error
    given: $.paths
    then:
      field: '@key'
      function: pattern
      functionOptions:
        match: '^/v2(/|$)'

  paths-kebab-case:
    description: Path segments (excluding parameters) must be kebab-case.
    severity: warn
    given: $.paths
    then:
      field: '@key'
      function: pattern
      functionOptions:
        match: '^(/[a-z0-9-]+|/\{[a-z_][a-z0-9_]*\})+$'

  paths-no-trailing-slash:
    description: Paths must not end with a trailing slash.
    severity: error
    given: $.paths
    then:
      field: '@key'
      function: pattern
      functionOptions:
        notMatch: '/$'

  paths-no-query-string:
    description: Paths must not include query strings.
    severity: error
    given: $.paths
    then:
      field: '@key'
      function: pattern
      functionOptions:
        notMatch: '\?'

  # ── OPERATIONS ────────────────────────────────────────────────────────
  operation-operationid-required:
    description: Each operation must declare an operationId.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: operationId
      function: truthy

  operation-operationid-camel-case:
    description: operationId must be camelCase.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].operationId
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-zA-Z0-9]*$'

  operation-operationid-verb-prefix:
    description: operationId should start with a recognized verb (get/list/search/add/update/delete/download/autocomplete).
    severity: info
    given: $.paths[*][get,post,put,patch,delete].operationId
    then:
      function: pattern
      functionOptions:
        match: '^(get|list|search|add|update|delete|download|autocomplete|create)'

  operation-summary-required:
    description: Each operation must declare a summary.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: summary
      function: truthy

  operation-summary-title-case-prefix:
    description: Operation summaries should start with a capital letter.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].summary
    then:
      function: pattern
      functionOptions:
        match: '^[A-Z]'

  operation-description-required:
    description: Each operation must declare a description.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: description
      function: truthy

  operation-tags-required:
    description: Each operation must declare at least one tag.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: tags
      function: truthy

  operation-security-required:
    description: Each operation must declare a security block (OAuth 1.0a).
    severity: warn
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: security
      function: truthy

  # ── TAGS ──────────────────────────────────────────────────────────────
  tags-global-defined:
    description: Global tags array must be defined.
    severity: warn
    given: $
    then:
      field: tags
      function: truthy

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

  tag-title-case:
    description: Tag names must be Title Case (e.g., "Icon", "Collection", "Autocomplete").
    severity: warn
    given: $.tags[*].name
    then:
      function: pattern
      functionOptions:
        match: '^[A-Z][a-zA-Z0-9]*$'

  # ── PARAMETERS ────────────────────────────────────────────────────────
  parameter-description-required:
    description: Each parameter must have a description.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[*]
    then:
      field: description
      function: truthy

  parameter-snake-case:
    description: Query and path parameter names must be snake_case.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query' || @.in == 'path')].name
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-z0-9_]*$'

  parameter-schema-required:
    description: Each parameter must declare a schema with a type.
    severity: error
    given: $.paths[*][get,post,put,patch,delete].parameters[*]
    then:
      field: schema
      function: truthy

  parameter-pagination-cursor:
    description: Pagination parameters must use prev_page / next_page cursor tokens.
    severity: info
    given: $.paths[*][get].parameters[?(@.in == 'query' && (@.name == 'page' || @.name == 'offset'))]
    then:
      function: falsy

  parameter-thumbnail-size-enum:
    description: thumbnail_size parameter must constrain to enum [42, 84, 200].
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[?(@.name == 'thumbnail_size')].schema
    then:
      field: enum
      function: truthy

  # ── REQUEST BODIES ────────────────────────────────────────────────────
  request-body-json:
    description: Request bodies should use application/json content.
    severity: warn
    given: $.paths[*][post,put,patch].requestBody.content
    then:
      field: application/json
      function: truthy

  request-body-description:
    description: Request bodies should have a description.
    severity: info
    given: $.paths[*][post,put,patch].requestBody
    then:
      field: description
      function: truthy

  # ── RESPONSES ─────────────────────────────────────────────────────────
  response-success-required:
    description: Each operation must declare a 2xx response.
    severity: error
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          patternProperties:
            '^2\d{2}$':
              type: object
          minProperties: 1

  response-401-required:
    description: Each authenticated operation must declare a 401 response.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      field: '401'
      function: truthy

  response-429-required:
    description: Operations subject to rate limits must declare a 429 response.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      field: '429'
      function: truthy

  response-description-required:
    description: Each response must have a description.
    severity: error
    given: $.paths[*][get,post,put,patch,delete].responses[*]
    then:
      field: description
      function: truthy

  response-json-content:
    description: 2xx JSON responses must use application/json content type.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].responses[?(@property.match(/^2\\d{2}$/))].content
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          minProperties: 1

  # ── SCHEMAS — PROPERTY NAMING ────────────────────────────────────────
  schema-property-snake-case:
    description: Schema property names must be snake_case.
    severity: warn
    given: $.components.schemas[*].properties
    then:
      field: '@key'
      function: pattern
      functionOptions:
        match: '^[a-z_][a-z0-9_]*$'

  schema-description-required:
    description: Each component schema must have a description.
    severity: warn
    given: $.components.schemas[*]
    then:
      field: description
      function: truthy

  schema-type-required:
    description: Each component schema must declare a type.
    severity: error
    given: $.components.schemas[*]
    then:
      field: type
      function: truthy

  schema-id-integer:
    description: id properties should be typed as integer for Noun Project resources.
    severity: info
    given: $.components.schemas[*].properties.id
    then:
      field: type
      function: pattern
      functionOptions:
        match: '^integer$'

  schema-permalink-uri:
    description: permalink properties should use format uri.
    severity: info
    given: $.components.schemas[*].properties.permalink
    then:
      field: format
      function: pattern
      functionOptions:
        match: '^uri$'

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

  security-scheme-oauth1:
    description: Security schemes must include the oauth1 scheme.
    severity: warn
    given: $.components.securitySchemes
    then:
      field: oauth1
      function: truthy

  security-scheme-description:
    description: Each security scheme must have a description.
    severity: warn
    given: $.components.securitySchemes[*]
    then:
      field: description
      function: truthy

  # ── HTTP METHOD CONVENTIONS ──────────────────────────────────────────
  no-get-request-body:
    description: GET operations must not declare a request body.
    severity: error
    given: $.paths[*][get]
    then:
      field: requestBody
      function: falsy

  post-request-body-required:
    description: POST operations should declare a request body.
    severity: warn
    given: $.paths[*][post]
    then:
      field: requestBody
      function: truthy

  # ── GENERAL QUALITY ──────────────────────────────────────────────────
  example-encouraged:
    description: Schema properties should include example values.
    severity: info
    given: $.components.schemas[*].properties[*]
    then:
      field: example
      function: truthy

  external-docs-encouraged:
    description: The spec should reference external documentation.
    severity: info
    given: $
    then:
      field: externalDocs
      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/noun-project-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.