Laws API

The Data Services API also covers statutes, regulations, constitutions, and agency guidance — US federal plus all 50 states and DC — as structured, citable documents with verbatim text. Same API, same base URL, same bearer API key as the case-law endpoints; laws add three endpoints:

Endpoint Method Purpose
/api/v1/laws/search POST Full-text search with jurisdiction/type/subtree filters
/api/v1/laws/get POST Retrieve one document by citation, register citation, path, or id
/api/v1/laws/collections GET Enumerate available codes/registers (filter values)

Authentication is the same bearer API key as the rest of the Data Services API.

The document model

Laws are stored as trees: each collection (a code or register, e.g. cfr, uscode, ca-ccr) is a hierarchy of documents — titles contain chapters contain parts contain sections. Every document has:

  • id — UUID, the stable handle for follow-up requests
  • parent_id — its container (fetch it to zoom out)
  • path — its ltree position within the collection (e.g. cfr.t21.cI.scH.p820.spA.s820_1); paths are unique within a collection only
  • node_typeLEAF (a content section), PARENT (a container), or TOMBSTONE (a repealed/reserved placeholder)
  • citation (canonical, e.g. 21 C.F.R. § 820.1), number, title
  • register_citation — Federal Register / state register cite, for regulations that carry one
  • html — the verbatim text (containers usually have none); external link hrefs are stripped (anchor text and in-page # anchors survive)
  • url — the Midpage page for this exact version; source_url — the official source
  • state ("Federal" or a state name), country, collection_type

Currency: what "current" means

There is no stored is_current flag — currency is derived at request time. A document is current iff:

  1. it is not marked historical (superseded by the source),
  2. its effective_date is not in the future — a future-effective amendment is stored as a version but is not yet the law,
  3. no newer version is already in force — until a future amendment's effective date arrives, the older text remains current ("anchor" semantics), and
  4. it is not a TOMBSTONE — repeal/removal markers are lifecycle notes, never the law in force.

Both endpoints filter to current law by default. search takes current_only: false, get takes include_historical: true to widen; every returned document carries is_current either way. The derivation reacts to the calendar automatically: on the amendment's effective day, the old version stops being current and the new one starts, with no data change required.

Historical access requires an API key with historical entitlement: without it, the widening flags and historical-id lookups return 403 and versions comes back empty.

Versions

When a document has version history (statute amended, regulation revised), get returns a versions array: every version's id, effective_date, publication_date, source_as_of, plus is_historical / is_current flags, oldest first, with is_requested marking the one returned as document. To read an older (or future) version, get it by its id — an id lookup always returns that exact version (historical versions require full historical access).

Note on dates: effective_date is only populated when the source states it. Several state codes publish session-year history rather than effective dates — those versions have effective_date: null and order by publication / observation dates instead.

get on a container (node_type: "PARENT") returns children: its current children in the code's authored order — navigation rows only (id, number, citation, title, sort_order; no text), capped at 250 with the true count in children_total. Typical drill-down:

get { citation: "21 C.F.R. Part 820" }        → part container + section list
get { id: <child id from children[]> }        → the section text
get { id: <parent_id> }                       → back up to the container

There is deliberately no children pagination: when a container has more than 250 children, walking the tree is the wrong tool — search directly for the target section (query + collections + path_prefix) and get the hit.

Lookup methods on get

Provide exactly one of:

Field Use for Notes
citation canonical cites insensitive to punctuation, spacing, and §/sec./section style ("42 USC 1983" matches "42 U.S.C. § 1983"); wording must still match the canonical form — no abbreviation aliasing
register_citation FR / state register cites regulations & rule documents
path tree positions pair with collection (paths unique per collection)
id anything already seen always unambiguous; returns that exact version regardless of currency

The optional collection field narrows path/citation/register-citation lookups and accepts either a collection id ("cfr") or a human-readable name ("Code of Federal Regulations"), resolved server-side — an unresolvable value returns 400 with did-you-mean suggestions. Request bodies are strict: unknown keys return 400 rather than being silently ignored.

If a citation matches several documents (rare, e.g. the same cite in two collections), the response has status: "ambiguous" and matches — candidate summaries; retry with one of the ids. A miss returns 404 with status: "not_found" and a message saying why — including the case where only non-current versions matched (retry with include_historical: true to retrieve those).

Search filters

search narrows with states (full names or USPS abbreviations, "Federal" for federal law — validated server-side, unrecognized values 400 with did-you-mean suggestions), collection_types, collections (ids or human-readable names, resolved the same way), path_prefix (a dot-separated ltree prefix, e.g. "cfr.t21" for all of 21 CFR — malformed prefixes 400 rather than matching nothing), and inclusive effective_date_from/to / publication_date_from/to ranges. Pagination is page / page_size (max 50); results beyond the first 10,000 cannot be paginated — narrow the query instead. Each hit carries the document's identifying fields, dates, currency flags, highlighted snippets, and url (the Midpage page for that exact version). A page can hold fewer than page_size results; total is an estimate for the whole query.

State surveys

The search response includes state_counts: estimated per-state hit counts for the whole query, not just the returned page — use them to scope a survey, then verify each state with a filtered search. A 5-state survey is two steps:

// 1. Where is this regulated, and how much?
POST /api/v1/laws/search
{ "query": "telehealth prescribing controlled substances",
  "collection_types": ["regulation"] }
// → state_counts: { "California": 12, "Texas": 8, ... }

// 2. Per state: top provisions, then `get` each by id for verbatim text.
POST /api/v1/laws/search
{ "query": "telehealth prescribing controlled substances",
  "collection_types": ["regulation"], "states": ["California"] }

GET /api/v1/laws/collections lists every collection with state and type, e.g. to confirm which states have a regulation collection before surveying.

Relationship to the MCP laws tools

The MCP server (latest version) exposes the same data layer as two tools: searchLaws (search + stateCounts, camelCase inputs, same collections / states resolution) and analyzeLaw (lookup by citation / registerCitation / path / id with the same children + versions model — but analysis-only: a question is required, the document text is analyzed internally, and the tool returns verified verbatim passages rather than raw text; use this API's get when you need the html itself).