Columbia University · API Governance Rules

Columbia University API Rules

Spectral linting rules defining API design standards and conventions for Columbia University.

0 Rules
View Rules File View on GitHub

Spectral Ruleset

Raw ↑
x-generated: '2026-08-19'
x-method: derived
x-source: >-
  Derived from openapi/columbia-library-hours-openapi.yml and from defects observed live on
  Columbia University surfaces on 2026-08-19. Columbia publishes no governance ruleset, style
  guide or design standard for any of its APIs.
x-provider: Columbia University
x-providerId: columbia
x-operator: institution
x-format: spectral
description: >-
  A governance ruleset for the one contract Columbia University actually operates. Every rule
  below was written against a real observation on hours.library.columbia.edu, not imported from a
  generic ruleset — the failing ones are failing right now.
rules:
- id: columbia-required-param-must-not-soft-200
  severity: error
  status: failing
  surface: columbia:library-hours
  statement: >-
    An operation whose handler requires a query parameter must reject a request that omits it
    with 400, not fall through to an HTML page with HTTP 200.
  observed: >-
    GET /api/v1/locations/butler with no date parameter returns HTTP 200 and the service's HTML
    web page. The caller is told the request succeeded.
  evidence: https://hours.library.columbia.edu/api/v1/locations/butler
  remediation: >-
    Declare date / start_date / end_date as a required oneOf at the operation level and return
    the service's existing 400 envelope when neither form is supplied.
- id: columbia-error-must-carry-machine-code
  severity: warn
  status: failing
  surface: columbia:library-hours
  statement: >-
    An error body must carry a stable machine-readable code, not only a human message with the
    HTTP status repeated as a text prefix.
  observed: '{"error":{"msg":"400: invalid date"},"data":null}'
  evidence: https://hours.library.columbia.edu/api/v1/locations/butler?date=notadate
  remediation: >-
    Add a stable `code` member (e.g. invalid_date, date_range_inverted, location_not_found)
    alongside msg, or adopt RFC 9457 problem+json.
- id: columbia-nullable-collection-should-be-empty-not-null
  severity: warn
  status: failing
  surface: columbia:library-hours
  statement: >-
    A collection response must return an empty collection when there is nothing to return, not
    null, so that a consumer has one shape to handle.
  observed: >-
    /locations/open_now returns "data": null when no library is open, and a keyed object
    otherwise. Confirmed in Columbia's own source: `open_locations_hash.empty? ? nil : ...`.
  evidence: https://hours.library.columbia.edu/api/v1/locations/open_now
  remediation: Return an empty object.
- id: columbia-field-name-must-describe-content
  severity: warn
  status: failing
  surface: columbia:library-hours
  statement: A field name must describe what the field contains.
  observed: >-
    `formatted_date` contains a time or a time range ("Until 09:00PM", "09:00AM-09:00PM"), never
    a date.
  remediation: Rename to formatted_hours, or move display strings out of the contract entirely.
- id: columbia-unset-string-should-be-null-not-empty
  severity: info
  status: failing
  surface: columbia:library-hours
  statement: An unset optional string should be null or absent, consistently, not an empty string.
  observed: short_note and short_note_url return "" when unset while note returns a value or "".
- id: columbia-collection-must-be-enumerable
  severity: warn
  status: failing
  surface: columbia:library-hours
  statement: >-
    A resource addressed by an identifier must have an operation that lists the valid
    identifiers.
  observed: >-
    /locations/{code} requires a location code, but the only listing operation is open_now, which
    returns exclusively the locations open at that moment. There is no way to discover the code
    for a library that is currently closed.
  remediation: Add GET /locations returning the full location set.
- id: columbia-timezone-must-be-explicit
  severity: warn
  status: failing
  surface: columbia:library-hours
  statement: A time value must carry or declare its timezone.
  observed: >-
    open_time and close_time are bare HH:MM strings. America/New_York is inferable from context
    and stated nowhere in the response, the documentation or any schema.
- id: columbia-api-must-publish-a-contract
  severity: error
  status: failing
  surface: all
  statement: A published API must publish a machine-readable description of itself.
  observed: >-
    Columbia publishes no OpenAPI, no schema and no reference documentation for the Hours API.
    The contract in this repository was reverse-engineered from live probes and Columbia's own
    source code.
  remediation: >-
    Publish openapi/columbia-library-hours-openapi.yml, or its equivalent, from
    hours.library.columbia.edu.
- id: columbia-api-hostname-must-serve-something
  severity: warn
  status: failing
  surface: api.library.columbia.edu
  statement: A hostname reserved for an API must publish at least one route or a catalogue.
  observed: >-
    api.library.columbia.edu serves a page titled "API Home | Columbia University Libraries" whose
    body is the words "API Home. CUL API." Every path beneath it 404s with a placeholder whose
    body reads "A custom errorhandler for 404 responses".
  evidence: https://api.library.columbia.edu/
- id: columbia-machine-access-must-not-contradict-open-licence
  severity: warn
  status: failing
  surface: columbia:academic-commons, columbia:clio-opendata
  statement: >-
    An institution that releases data under an open licence should not proof-of-work-challenge
    machine access to the same data.
  observed: >-
    Columbia University Libraries publishes the CLIO catalogue as CC0 MARCXML bulk downloads on
    lito.cul.columbia.edu while serving an Anubis anti-crawler challenge to every automated
    request against clio.columbia.edu, academiccommons.columbia.edu and
    geodata.library.columbia.edu. The bulk route is the sanctioned one; nothing says so anywhere
    a machine would look.
  remediation: >-
    Publish an llms.txt or a robots.txt that names the bulk extract path as the machine route.
    Columbia currently publishes neither — both return 404 on www.columbia.edu.
- id: columbia-passing-anonymous-read
  severity: info
  status: passing
  surface: columbia:library-hours
  statement: A public read API should be callable with no credential and no click-through.
  observed: >-
    Both operations skip authentication explicitly in Columbia's own source and set
    Access-Control-Allow-Origin: *. No key, no quota, no registration.
- id: columbia-passing-correct-status-codes
  severity: info
  status: passing
  surface: columbia:library-hours
  statement: Error conditions should use the correct HTTP status code.
  observed: >-
    404 for an unknown location code, 400 for an unparseable date and for an inverted range, each
    with a JSON body and the correct content type. Only the missing-parameter path is wrong.
x-summary:
  total: 12
  failing: 10
  passing: 2
  errors: 2
  warnings: 6
  info: 4
x-notes: >-
  Ten failing rules against one 2-operation API reads harsher than it is. The service does the
  hard part right — it is genuinely public, genuinely CORS-enabled and genuinely returns correct
  status codes — and fails almost entirely on things nobody at Columbia was ever asked to do,
  because there is no institutional API governance function to ask.

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/columbia-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.