Compliance API

Endpoint — states

The core compliance data: one record per U.S. jurisdiction (50 states + DC).

GET /api/v1/compliance/states

Returns a summary record for every jurisdiction. The summary deliberately omits the long-form prose fields (special_notes, withdrawal_process, etc.) to keep the payload small for list views.

curl -H "Authorization: Bearer $TOKEN" https://homeschoolfox.com/api/v1/compliance/states

Response (truncated):

{
  "data": [
    {
      "abbreviation": "AK",
      "name": "Alaska",
      "compulsory_age_range": "7-16",
      "notice": { "required": false, "type": "none", "recipient_label": null, "frequency": "none", "approval_required": false },
      "requirements": { "hours_per_year": null, ... }
    },
    ...
  ],
  "meta": { "api_version": "v1", "count": 51 }
}

GET /api/v1/compliance/states/:abbreviation

The full record for one jurisdiction. The :abbreviation is the standard two-letter postal code, case-insensitive.

curl -H "Authorization: Bearer $TOKEN" https://homeschoolfox.com/api/v1/compliance/states/MO

Response (excerpted):

{
  "data": {
    "abbreviation": "MO",
    "name": "Missouri",
    "compulsory_age_range": "7-17",
    "notice": { "required": false, "type": "none", "recipient_label": null, "frequency": "none", "approval_required": false },
    "requirements": {
      "hours_per_year": 1000,
      "core_hours_per_year": 600,
      "secondary_hours_per_year": null,
      "secondary_hours_start_grade": null,
      "days_per_year": null,
      "hours_or_days": null,
      "hours_per_day": null,
      "subjects": ["reading", "math", "social studies", "language arts", "science"]
    },
    "assessment": { "type": "none", "frequency": "none", "description": "...", "allows_portfolio": null },
    "portfolio": { "required": true, "records_required": true, "description": "..." },
    "reporting": { "frequency": "none" },
    "esa": { "program_name": "MOScholars (...)", "amount_cents": 637500, "homeschool_access": "status_change", ... },
    "tax_credit_summary": null,
    "umbrella_summary": null,
    "filing_deadlines": {},
    "required_documents": [],
    "special_notes": "...",
    "withdrawal_process": null,
    "source_urls": [
      { "name": "HSLDA", "url": "https://hslda.org/legal/missouri" },
      { "name": "Families for Home Education (FHE-MO)", "url": "https://fhe-mo.org/" }
    ],
    "editorial": {
      "schema_version": "1",
      "last_verified_at": "2026-05-12",
      "needs_review": false,
      "verification_notes": null
    }
  },
  "meta": {
    "api_version": "v1",
    "version": 1,
    "released_at": "2026-05-12",
    "git_sha": "ec3bc5a2"
  }
}

States that raise the hour requirement by grade

Four states set a higher annual hour floor for older students, and they do not agree on where the line falls. requirements.secondary_hours_start_grade is the first grade at which secondary_hours_per_year replaces hours_per_year:

State hours_per_year secondary_hours_per_year secondary_hours_start_grade Authority
MT 720 1080 4 MCA 20-1-301(2)
NE 1032 1080 9 92 NAC 13 § 004.01, with Neb. Rev. Stat. 79-101
NY 900 990 7 8 NYCRR 100.10(f)(2)
PA 900 990 7 24 P.S. § 13-1327.1(c)

Do not assume grade 7. Both fields are null for the 47 states with no split.

Reading esa

esa.homeschool_access says whether a homeschooling family can reach the program's money (open, status_change, closed, unclear). Do not infer it from amount_cents: an amount may be absent because no figure is verified, and present on a program that pays out only to families who first leave the homeschool track. The esa_programs endpoint carries the full field notes.

esa.reporting.deadline_keys names the entries in this state's filing_deadlines that belong to the ESA program rather than to homeschooling. A client rendering a homeschool compliance calendar should exclude them — Arizona's four quarterly dates bind ESA contract holders, and an Arizona homeschooler files an affidavit once and reports nothing after.

Reading reporting

reporting.frequency is none, annual, quarterly, as_needed, or at_specified_grades. It covers a report of progress or results. The notice of intent lives in notice and never counts here. at_specified_grades means results are filed only in the grades listed in reporting.grades (CO, ND, WV), and nothing is filed in other years. reporting.due carries a statewide due date where the statute sets one. Both keys are absent for other states.

Reading notice

notice.required alone does not tell you what a family owes.

  • notice.frequency — annual, once, on_withdrawal, none, or unknown. Eleven states file once and never again (AL, AZ, FL, HI, KS, NC, NV, OR, SD, UT, WV). Do not assume annual.
  • notice.type — now also county, regional and school. Four states file somewhere other than a district or the state: AZ and MT with the county school superintendent, OR with a regional education service district, HI with the child's own school principal.
  • notice.recipient_label — a written phrase that overrides the office implied by type, for a recipient the enum cannot name. Mississippi's county school attendance officer is the case it exists for.
  • notice.form_label and notice.form_note: set when the filing is a form an office supplies, not a letter the parent writes. Alabama is the case they exist for: the parent files the church school enrollment form the superintendent provides, countersigned by the church school's administrator (Ala. Code 16-28-7). North Carolina also carries them, because DNPE takes the notice only through its online system. A client that offers a letter template should skip it when form_label is present.
  • notice.approval_required — true in MA and RI, where the program must be approved before instruction begins. Filing is not permission, and a family that starts on filing day can be found truant.

Two states carry required: false with a type set and frequency: "on_withdrawal": NH and WY ask for nothing to begin homeschooling, but require notice when a child is withdrawn from public school: a declaration of home education in NH (RSA 193-A:3), and in WY a notice to the district or, failing that, an in-person meeting to sign written consent (W.S. 21-4-102(c)). A client that renders required: false as "no paperwork" is wrong for exactly those two.

Reading portfolio and assessment

portfolio.required means a portfolio of student work specifically, and is deliberately narrow: it gates the portfolio guide on the site. Seventeen more states mandate records without mandating a portfolio (attendance registers, academic records, retained test results), and those carry portfolio.records_required: true with required: false. A client that reads required: false as "no recordkeeping duty" is wrong in CA, CO, GA, HI, IN, KY, ME, MN, MT, NC, ND, NY, OR, RI, VT, WA and WV. records_required is always true where required is true.

assessment.allows_portfolio matters only where type is "choice", and says whether a portfolio is among the methods the statute accepts. It is false in CO, VA and WA, where a portfolio satisfies nothing on its own: Colorado takes a test or an evaluation by a qualified person, Virginia a test composite or an evaluation, Washington an approved standardized test or a written assessment by a certificated person. Do not render a generic "testing, portfolio, or evaluation" for every choice state.

States that set both hours and days

Seven states carry a value in both hours_per_year and days_per_year, and they do not all mean the same thing. requirements.hours_or_days says which:

  • true — either figure satisfies the statute on its own. PA (24 P.S. § 13-1327.1(c); PDE: "Either 180 days or 900 hours of instruction are required") and WA (RCW 28A.195.010(1): "no less than one hundred eighty school days or the equivalent in annual minimum instructional hour offerings").
  • false — both bind. NY puts the 180 days and the 900/990 hours under one stem in 8 NYCRR 100.10(f) with no "or" and no election. CO, GA, ND and RI express one rule as a daily rate across a day count, so their annual hours are derived rather than an alternative.

Do not render the two figures as a joint requirement without checking this flag. It is null for the 44 states that set only one figure or neither.

Some states give families a choice of legal tracks. South Carolina, for example, has three accountability options. The structured fields (notice, requirements, assessment, portfolio, reporting) describe the track most families use, and editorial.track_basis names that track. A rule that binds only another track, such as South Carolina's 4.5-hour day under Option 1, appears in the description fields and special_notes, not in the structured fields. track_basis is absent or null for a single-track state.

Optional query params

  • ?as_of=YYYY-MM-DD — return the snapshot active on that date
  • ?version=N — pin to a specific monotonic version number

You can only pass one at a time. Both 404 if the requested date predates the first snapshot or the version doesn't exist.

GET /api/v1/compliance/states/:abbreviation/versions

Returns the version history for one state, newest-first. Each version row carries the version number, release date, short SHA, commit author, commit message (change_summary), and a diff block listing top-level keys added / removed / changed vs the previous version.

curl -H "Authorization: Bearer $TOKEN" https://homeschoolfox.com/api/v1/compliance/states/MO/versions

Optional ?limit=N (default 50, max 200).

GET /api/v1/compliance/states/:abbreviation/versions/:version_number

Returns the historical payload at a specific version — byte-identical to what GET /states/MO?version=N returns. Useful for explicit pinning where you want the version number visible in the URL.