openapi: 3.1.0
info:
  title: DistrictFacts Public Data API
  version: 1.6.0
  description: |-
    Static public records projected from the same per-district artifacts used by
    DistrictFacts pages. Measures are the values each agency published, unrounded.
    Line items marked aggregate: true are DistrictFacts sums of the agency's rows,
    as the row's locator says. Line items span several categories (for example
    spending and tax apportionment in New Jersey); sum only within one category
    and never across aggregate and non-aggregate rows. New York carries Fiscal Profile
    history and FY2025 finance components with source hashes; coverage_note identifies
    website fields not yet exported. Its state-only district has leaid: null and an
    explicit district_key; never interpret that key as an NCES identifier.

    Salary figures: Role-level averages are included. Leadership single-position
    figures are included and flagged as such. Assistant-principal salaries and
    aggregates of two or fewer positions drawn from a source list that names each
    person are withheld. No named individuals are included. The website may show
    more than the open-data files.

    School history: Georgia school discipline figures are carried for the five
    most recent school years only, so every district file stays under 8 MiB; the
    record's school_history_limits says which periods it keeps, and the full
    history stays on each school's page.

    The CC BY 4.0 licence is final for all 13 states and D.C. Connecticut figures come
    from the Connecticut State Department of Education (CT EdSight, listed as
    public domain on data.ct.gov) and are published without warranty; small counts
    are suppressed under CSDE's student-privacy rules. The exceptions list is kept
    for any future state whose terms are under review; it is empty today.

    Version policy: Additive changes (new fields, files, states) ship within v1
    without notice. Anything that removes, renames or changes the meaning of a field
    ships as v2 at a new path, with v1 kept for at least 6 months.

    How to cite: Cite the district page URL, the record's generated_at date, and,
    for exact reproducibility, the file's sha256 from districts.json.
  license:
    name: CC BY 4.0
    identifier: CC-BY-4.0
    x-status: final
    x-effective: '2026-09-28'
    x-terms: https://www.districtfacts.com/open-data/#terms
    x-exceptions: []
servers:
  - url: https://www.districtfacts.com
    description: Static production host
paths:
  /data/v1/openapi.yaml:
    get:
      operationId: getApiV1OpenApi
      summary: Get the OpenAPI 3.1 contract for this API
      responses:
        '200':
          description: OpenAPI 3.1 contract
          content:
            application/yaml:
              schema:
                type: string
  /data/v1/changelog.json:
    get:
      operationId: getApiV1Changelog
      summary: Get the API v1 release history
      responses:
        '200':
          description: Ordered API v1 changelog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Changelog'
  /data/v1/index.json:
    get:
      operationId: getApiV1Index
      summary: List available states
      responses:
        '200':
          description: State index
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StateIndex'
  /data/v1/{state}/districts.json:
    get:
      operationId: getApiV1StateDistricts
      summary: List districts available for a state
      parameters:
        - $ref: '#/components/parameters/AvailableState'
      responses:
        '200':
          description: District directory
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DistrictDirectory'
        '404':
          description: State directory not found
  /data/v1/{state}/bulk.ndjson:
    get:
      operationId: getApiV1StateBulk
      summary: Get every district record in a state as newline-delimited JSON
      description: >-
        Records are in slug order. Each line is byte-identical to the content of
        that district's individual JSON file, and the file is newline-terminated.
      parameters:
        - $ref: '#/components/parameters/AvailableState'
      responses:
        '200':
          description: One ApiDistrict JSON object per line
          content:
            application/x-ndjson:
              schema:
                type: string
                description: Newline-delimited ApiDistrict objects.
        '404':
          description: State bulk file not found
  /data/v1/{state}/districts/{slug}.json:
    get:
      operationId: getApiV1District
      summary: Get one district and its published, withheld, and not-collected claims
      parameters:
        - $ref: '#/components/parameters/AvailableState'
        - name: slug
          in: path
          required: true
          schema:
            type: string
            minLength: 1
      responses:
        '200':
          description: Projected district record
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiDistrict'
        '404':
          description: District record not found
components:
  parameters:
    AvailableState:
      name: state
      in: path
      required: true
      description: A state currently present in the static v1 corpus.
      schema:
        $ref: '#/components/schemas/AvailableStateCode'
  schemas:
    ApiVersion:
      type: string
      const: 1.6.0
    LicenceStatus:
      type: string
      enum: [final, under_review]
    StateCode:
      type: string
      enum: [NJ, CT, PA, DE, NH, MD, RI, VT, DC, GA]
    AvailableStateCode:
      type: string
      enum: [nj, ct, pa, de, nh, md, ri, vt, dc, ga]
    Category:
      type: string
      enum: [finance, students, staff, outcomes, programs, climate, funding, fiscal]
    WithheldReason:
      type: string
      enum:
        - agency_suppressed
        - failed_distribution_gate
        - definition_not_comparable
        - source_unverified
        - superseded_pending_review
        - privacy_review
    AbsenceCapability:
      type: string
      enum:
        - finance_detail
        - funding_taxes
        - staffing
        - outcomes
        - students
        - programs
        - climate
        - fiscal_transparency
        - trends
        - comparisons
        - schools
    ApiSource:
      type: object
      additionalProperties: false
      required: [id, url, sha256, locator, retrieved_at, source_host]
      properties:
        id:
          type: string
          minLength: 1
        url:
          type: string
          format: uri
          pattern: '^[Hh][Tt][Tt][Pp][Ss]?://'
        sha256:
          type: string
          pattern: '^[A-Fa-f0-9]{64}$'
        locator:
          type: string
          minLength: 1
        retrieved_at:
          type: string
          format: date-time
        source_host:
          type: string
          minLength: 1
    PublishedCell:
      type: object
      additionalProperties: false
      required: [status, key, label, category, period, definition, value, unit, source_id, locator]
      properties:
        status:
          type: string
          const: published
        key:
          type: string
          minLength: 1
        label:
          type: string
          minLength: 1
        category:
          $ref: '#/components/schemas/Category'
        period:
          type: string
          minLength: 1
        definition:
          type: string
          minLength: 1
        value:
          type: number
          description: Exact stored value; monetary units remain integer cents.
        unit:
          type: string
          minLength: 1
        source_id:
          type: string
          minLength: 1
          description: Key in the district record's sources registry.
        locator:
          type: string
          minLength: 1
        position_class:
          type: string
          const: single_position
          description: Identifies a published leadership salary held by one position.
    WithheldCell:
      type: object
      additionalProperties: false
      required: [status, key, label, category, period, definition, reason, note, source_id, locator]
      properties:
        status:
          type: string
          const: withheld
        key:
          type: string
          minLength: 1
        label:
          type: string
          minLength: 1
        category:
          $ref: '#/components/schemas/Category'
        period:
          type: string
          minLength: 1
        definition:
          type: string
          minLength: 1
        reason:
          $ref: '#/components/schemas/WithheldReason'
        note:
          type: string
          minLength: 1
        source_id:
          type: string
          minLength: 1
          description: Key in the district record's sources registry.
        locator:
          type: string
          minLength: 1
    NotCollectedCell:
      type: object
      additionalProperties: false
      required: [status, key, label, category, note]
      properties:
        status:
          type: string
          const: not_collected
        key:
          type: string
          minLength: 1
        label:
          type: string
          minLength: 1
        category:
          $ref: '#/components/schemas/Category'
        note:
          type: string
          minLength: 1
        capability:
          $ref: '#/components/schemas/AbsenceCapability'
    Cell:
      oneOf:
        - $ref: '#/components/schemas/PublishedCell'
        - $ref: '#/components/schemas/WithheldCell'
        - $ref: '#/components/schemas/NotCollectedCell'
      discriminator:
        propertyName: status
        mapping:
          published: '#/components/schemas/PublishedCell'
          withheld: '#/components/schemas/WithheldCell'
          not_collected: '#/components/schemas/NotCollectedCell'
    SchoolCell:
      oneOf:
        - $ref: '#/components/schemas/PublishedCell'
        - $ref: '#/components/schemas/WithheldCell'
      discriminator:
        propertyName: status
        mapping:
          published: '#/components/schemas/PublishedCell'
          withheld: '#/components/schemas/WithheldCell'
    Notice:
      type: object
      additionalProperties: false
      description: Context that changes how the record's figures should be read.
      required: [text, href, link_label, locator, source_id]
      properties:
        text:
          type: string
          minLength: 1
        href:
          type: string
          pattern: '^/[a-z]{2}/[a-z0-9-]+/$'
        link_label:
          type: string
          minLength: 1
        locator:
          type: string
          minLength: 1
        source_id:
          type: string
          minLength: 1
          description: Key in the district record's sources registry.
    LineItem:
      type: object
      additionalProperties: false
      required:
        - code
        - label
        - category
        - category_code
        - location
        - period
        - aggregate
        - general_fund_cents
        - grants_revolving_cents
        - total_cents
        - per_pupil_cents
        - locator
        - source_id
      properties:
        code:
          type: string
        label:
          type: string
        category:
          type: string
        category_code:
          type: string
        location:
          type: string
        period:
          type: string
        aggregate:
          type: boolean
        general_fund_cents:
          type: [integer, 'null']
          minimum: -9007199254740991
          maximum: 9007199254740991
        grants_revolving_cents:
          type: [integer, 'null']
          minimum: -9007199254740991
          maximum: 9007199254740991
        total_cents:
          type: [integer, 'null']
          minimum: -9007199254740991
          maximum: 9007199254740991
        per_pupil_cents:
          type: [integer, 'null']
          minimum: -9007199254740991
          maximum: 9007199254740991
        locator:
          type: string
        source_id:
          type: string
          minLength: 1
          description: Key in the district record's sources registry.
    School:
      type: object
      additionalProperties: false
      required: [code, name, grades, page_url, measures]
      properties:
        code:
          type: string
          pattern: '^[0-9A-Za-z-]{3,16}$'
        name:
          type: string
          minLength: 1
        grades:
          type: string
        page_url:
          type: string
          format: uri
        measures:
          type: array
          items:
            $ref: '#/components/schemas/SchoolCell'
        notice:
          $ref: '#/components/schemas/Notice'
    ApiDistrict:
      type: object
      additionalProperties: false
      required:
        - api_version
        - state
        - leaid
        - slug
        - name
        - generated_at
        - page_url
        - licence_status
        - attribution
        - sources
        - measures
        - schools
        - line_items
      properties:
        api_version:
          $ref: '#/components/schemas/ApiVersion'
        state:
          $ref: '#/components/schemas/StateCode'
        leaid:
          type: [string, 'null']
          pattern: '^\d{7}$'
          description: NCES LEAID; null only for a NY state-only identity with district_key.
        district_key:
          type: string
          pattern: '^(\d{7}|ny-beds-\d{12})$'
        slug:
          type: string
          minLength: 1
        name:
          type: string
          minLength: 1
        state_district_id:
          type: string
          description: Exact state agency district identifier where provided.
        coverage_note:
          type: string
          description: Explicit limits of this export versus website coverage.
        generated_at:
          type: string
          format: date-time
        page_url:
          type: string
          format: uri
        licence_status:
          $ref: '#/components/schemas/LicenceStatus'
        attribution:
          type: string
          const: 'Source: DistrictFacts (districtfacts.com), compiled from public records listed in sources.'
        sources:
          type: object
          description: Only sources referenced by this district's cells, school cells, line items, and notices.
          additionalProperties:
            $ref: '#/components/schemas/ApiSource'
        measures:
          type: array
          items:
            $ref: '#/components/schemas/Cell'
        schools:
          type: array
          items:
            $ref: '#/components/schemas/School'
        line_items:
          type: array
          items:
            $ref: '#/components/schemas/LineItem'
        notice:
          $ref: '#/components/schemas/Notice'
        school_history_limits:
          type: array
          description: Added in 1.3.0. Present only where the file carries fewer periods of a school-level series than the website shows (Georgia school discipline, the five most recent school years).
          items:
            $ref: '#/components/schemas/SchoolHistoryLimit'
    SchoolHistoryLimit:
      type: object
      additionalProperties: false
      required: [measures, periods, note]
      properties:
        measures:
          type: string
          description: Key pattern of the school measures the limit applies to, for example school_discipline_*.
        periods:
          type: array
          minItems: 1
          description: The periods the file carries for those measures, oldest first.
          items:
            type: string
        note:
          type: string
    StateIndexEntry:
      type: object
      additionalProperties: false
      required: [state, name, districts, api_url, bulk_url, bulk_bytes, districts_sha256]
      properties:
        state:
          $ref: '#/components/schemas/AvailableStateCode'
        name:
          type: string
          minLength: 1
        districts:
          type: integer
          minimum: 0
        api_url:
          type: string
          format: uri-reference
        bulk_url:
          type: string
          format: uri-reference
        bulk_bytes:
          type: integer
          minimum: 0
        districts_sha256:
          type: string
          pattern: '^[a-f0-9]{64}$'
        source_notice:
          type: string
          description: Attribution a source licence requires for this state's records (for example dc.gov's CC BY 3.0). Present only where one applies.
    StateIndex:
      type: object
      additionalProperties: false
      required: [api_version, generated_at, licence, attribution, docs, changelog, version_policy, states, excluded]
      properties:
        api_version:
          $ref: '#/components/schemas/ApiVersion'
        generated_at:
          type: string
          format: date-time
        licence:
          type: object
          additionalProperties: false
          required: [id, url, status, effective, terms, exceptions]
          properties:
            id:
              type: string
              const: CC-BY-4.0
            url:
              type: string
              format: uri
              const: https://creativecommons.org/licenses/by/4.0/
            status:
              type: string
              const: final
            effective:
              type: string
              format: date
              const: '2026-09-28'
            terms:
              type: string
              format: uri
              const: https://www.districtfacts.com/open-data/#terms
            exceptions:
              type: array
              items:
                type: object
                additionalProperties: false
                required: [state, status, note]
                properties:
                  state:
                    type: string
                  status:
                    type: string
                    enum: [under_review]
                  note:
                    type: string
        attribution:
          type: string
          const: 'Source: DistrictFacts (districtfacts.com), compiled from public records listed in sources.'
        docs:
          type: string
          const: /data/v1/openapi.yaml
        changelog:
          type: string
          const: /data/v1/changelog.json
        version_policy:
          type: string
          const: 'Additive changes (new fields, files, states) ship within v1 without notice. Anything that removes, renames or changes the meaning of a field ships as v2 at a new path, with v1 kept for at least 6 months.'
        states:
          type: array
          items:
            $ref: '#/components/schemas/StateIndexEntry'
        excluded:
          type: array
          items:
            type: string
          description: Explicit excluded record classes; current exclusions are listed in the index.
    DistrictDirectoryEntry:
      type: object
      additionalProperties: false
      required: [slug, name, leaid, page_url, api_url, sha256, bytes]
      properties:
        leaid:
          type: [string, 'null']
          pattern: '^\d{7}$'
          description: NCES LEAID; null only for a NY state-only identity with district_key.
        district_key:
          type: string
          pattern: '^(\d{7}|ny-beds-\d{12})$'
        slug:
          type: string
          minLength: 1
        name:
          type: string
          minLength: 1
        page_url:
          type: string
          format: uri
        api_url:
          type: string
          format: uri-reference
        sha256:
          type: string
          pattern: '^[a-f0-9]{64}$'
        bytes:
          type: integer
          minimum: 0
    DistrictDirectory:
      type: array
      items:
        $ref: '#/components/schemas/DistrictDirectoryEntry'
    ChangelogEntry:
      type: object
      additionalProperties: false
      required: [date, version, changes]
      properties:
        date:
          type: string
          format: date
        version:
          type: string
          pattern: '^1\.\d+\.\d+$'
        changes:
          type: array
          minItems: 1
          items:
            type: string
            minLength: 1
    Changelog:
      type: array
      items:
        $ref: '#/components/schemas/ChangelogEntry'
