Amendment History and Point-in-Time Law: What a Legal API Can and Cannot Tell You

Almost no US primary-law API gives you true point-in-time retrieval, and Vaquill AI does not either. The corpus holds one current text per citation, so there is no as_of=DATE query, no version list per section, and no arbitrary-date diff.

That is the honest starting point, and everything useful in this post follows from it. Four things do exist that people usually mean when they ask for point-in-time, and each answers a different question. If you know which one answers yours, you can build a compliance workflow that holds up. If you assume they are all the same feature, you will ship something that quietly gives wrong answers about dates.

Vaquill AI US statutes browser showing section text with amendment history and currency fields

TL;DR

  • There is no historical text query. One current text per citation. No as_of, no version list, no diff between two arbitrary dates. If your use case needs the exact words a section carried on a past date, no field here supplies it.
  • Four real capabilities exist: amendment history per section, a yearFrom/yearTo currency filter, change events captured per refresh, and per-section diffs on a watched board.
  • yearTo is not an as-of filter. It filters on the last amendment year the publisher credits, so a section amended in 2025 disappears from yearTo=2024 even though it existed and was in force in 2024.
  • Our own census, dated 2026-08-30: across 3,518,180 sections in four state-level corpora, 912,388 (25.9%) carry no amendment year at all, 548,762 assert a year with no stored publisher note behind it, and 155,589 hold a note that never parsed into a year.
  • A missing amendment year is usually a publisher property, not a scraping failure. Some state publishers print no amendment credit. Any vendor quoting one "amendment history coverage" percentage is quoting a number that mixes three unrelated things.
  • The pattern that works is snapshot and watch: store the text and its hash on the date you relied on it, subscribe to changes on the sections you care about, and go to the Statutes at Large corpus when you need law as enacted.
4-question check
Question 1 of 4

What does yearTo=2024 actually filter on?

This post closes out our legal data infrastructure series.

Two of the four capabilities below have posts of their own: How We Know a Law Changed for change events, and Law Change Alerts API for the diffs and their delivery.

What point-in-time law actually means

True point-in-time retrieval is a specific thing. You hand the system a citation and a date, and it returns the exact words that section carried on that date.

To do that, a system has to store every version of every section with an effective-date range on each one. That requires the publisher to have published those versions in the first place. Most US state publishers publish a current code and nothing else, so the versions do not exist to be stored.

Where historical versions do exist, they usually exist as a stream of amending acts rather than as reconstructed section texts. Rebuilding a 2019 section from a 2026 code plus a pile of session laws is a legislative-drafting exercise, not a database query. A vendor that quietly implies otherwise is selling you a derivation and calling it a record.

The four things that do exist

1. Amendment history per section

Every section carries a block of history fields, each defined in the amendment history reference. The important one is not a year we computed. It is sourceCredit and amendmentNote, the publisher's own credit line, stored as printed.

Ask for just the history fields with the fields parameter, which prunes the response to what you asked for. actId and citation always come back, because a row without them cannot be used or attributed.

curl -s https://api.vaquill.ai/api/v1/us/statutes/section/USC_T42_C21_S1983 \
  -H "Authorization: Bearer $VAQUILL_API_KEY" \
  -G --data-urlencode "fields=sourceCredit,amendmentNote,amendmentYears,amendmentsCount,lastAmendedYear,lastAmendedDate,originalEnactmentDate,priorEffectiveDates,currentThrough,currencyYear,goodLawStatus"

An unknown field name is rejected with 422, so a typo does not silently drop a field you needed. That matters more than it sounds: a silently missing history field looks exactly like a section with no history.

Each field answers a narrow question, and the second column matters less than the third.

FieldAnswersDoes not answer
sourceCreditWhat the publisher printed as the section's credit line, verbatimWhether the credit is complete or when the publisher last revised it
amendmentNoteThe stored history note behind the yearAnything, if the publisher printed no note
amendmentYearsEvery year we parsed out of that noteWhich subsection each year touched
amendmentsCountHow many amendment events the note describesWhether an amendment was substantive or a renumbering
lastAmendedYearThe most recent year the publisher creditsWhether the amendment is in force yet
lastAmendedDateA full date, where the publisher printed oneA date, for the many publishers that print only a year
originalEnactmentDateWhen the section first entered the codeWhat the section said then
priorEffectiveDatesDates on which earlier versions took effectThe text of any of those earlier versions
currentThroughWhat the publisher says the whole code is current throughWhat changed between that date and today
goodLawStatusWhether the section reads as live, repealed, or unknownWhether a court has limited it

The last two rows have their own status and currency reference. Read the right column top to bottom. Amendment history tells you when a section moved. It never tells you what it moved from.

2. The yearFrom and yearTo currency filter

POST /us/statutes/search takes yearFrom and yearTo. They are useful and they are the single most misread parameter on the surface.

curl -s https://api.vaquill.ai/api/v1/us/statutes/search \
  -H "Authorization: Bearer $VAQUILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"data breach notification deadline",
       "corpusType":"STATE","state":"tx",
       "yearFrom":2023,"limit":20}'

That call means: give me sections whose last credited amendment year is 2023 or later. It is a good way to find recently touched law.

It does not mean: give me the law as it stood in 2023. Two sharp edges follow from that, and both bite in production.

Edge one: yearTo excludes sections that existed. A section last amended in 2025 drops out of yearTo=2024, even though the section was on the books and in force throughout 2024. The filter is about when the text last changed, not about when the text was live. If you use yearTo as an as-of filter, you get a set that systematically omits the sections most likely to have changed under you.

Edge two: sections with no credit vanish once either bound is set. The filter can only compare a year it has. About a fifth of sections across the corpus carry no amendment credit at all, and at the state level our census puts it higher. Set yearFrom or yearTo and every one of those sections leaves the result set with no error and no warning.

3. Change events captured per refresh

The refresh re-pulls a source on its cadence, reconciles what came back against what is stored, and turns a difference in a section's stored text into a change event logged against that run.

GET /boards costs nothing and lists every watchable source, each row naming when it was last read cleanly, the interval it runs on, and how that read went. That is how you check a real cadence rather than a marketing number, and the intervals are set out per source in what refresh cadence actually means.

curl -s "https://api.vaquill.ai/api/v1/boards?limit=250" \
  -H "Authorization: Bearer $VAQUILL_API_KEY"

Two honest limits. A change event is a detected difference between two retrievals, which is not the same thing as an official effective-date announcement, and a publisher can restate a page without changing the law. Treat an event as a signal to go look, never as a legal conclusion. How We Know a Law Changed catalogues the four false positive families.

The second limit is more structural: change events are forward-only. They start when the source starts being refreshed. There is no back catalog of events for a section you began watching yesterday.

4. Per-section diffs on a watched board

When a watch fires, you can fetch the diff for that change and see the per-section text difference between the two retrievals.

This is the closest thing to a version comparison in the whole surface, and it is worth being precise about its scope. It compares retrieval N against retrieval N+1 for one section on one board. It cannot compare 2021 against 2024, because there was no retrieval pair spanning those dates and the intermediate texts were never stored as versions.

Which of the four answers your question

The mapping below includes the rows where the answer is nothing, because those are the rows people assume are covered.

Question a user asksWhat answers it
What does this section say todaySection retrieval
When did this section last changelastAmendedYear, lastAmendedDate
How many times has it been amendedamendmentsCount, amendmentYears
Which acts amended it, in the publisher's wordssourceCredit, amendmentNote
When did this section first enter the codeoriginalEnactmentDate
What is this whole code current throughcurrentThrough, currencyNote
Which sections in Texas changed since 2023yearFrom on search
Did this section change since I last lookedA watch plus a change event
Exactly which words changed in that eventThe per-section change diff
Is this source being refreshed at all right nowGET /boards, retrievalStatus
What did this section say on 14 March 2019Nothing
Show me every version of this sectionNothing
Diff this section between 2020 and 2023Nothing
Which sections were in force on a past dateNothing
Was this amendment substantive or a renumberingNothing in a structured field, read sourceCredit yourself
Loading diagram...

The original data: our own amendment-history census

On 2026-08-30 we ran a full census rather than a sample. It covered 5,763,696 stored points across four state-level corpora. Points are retrieval passages, and long documents split into several, so the section-level number is what matters: 3,518,180 sections.

Of those sections:

FindingSectionsShare
No amendment year at all912,38825.9%
A year asserted, with no stored publisher note behind it548,76215.6%
A note stored, but no year ever parsed out of it155,5894.4%

Those three rows are not three severities of the same problem. They have three different causes, and telling them apart is the part worth stealing.

Cause one: the note was never captured from the publisher's page. The history line exists in print and our extraction did not pick it up, usually because that publisher puts it somewhere structurally different from the section body. This is a real gap on our side and it is fixable.

Cause two: the note was captured but never parsed into a year. We hold the publisher's words and could not turn them into a number. Credit lines are wildly inconsistent across 52 jurisdictions, and a parser written against one state's format returns nothing on another's. The stored note is still correct and still readable by a human. This is the 155,589 row.

Cause three: something on the page that is not the history got read as if it were. This is the dangerous one, because it produces a confident year rather than a blank. A banner, a sidebar, a session identifier, or a notice about a pending change can all look like a date to a parser. A year with no note behind it is where this class hides, which is why we count that row separately instead of folding it into "has a year."

Why a missing year is usually the publisher's property

This next finding changes how you should read every coverage number in this category.

A missing amendment year is usually a property of the publisher, not a scraping failure. Some state publishers simply do not print an amendment credit under each section. There is no note to capture, so no parser can produce one.

Delaware is the clean case. Its publisher prints no year at all, so the year is legitimately derived from a volume-to-year table rather than from a per-section credit line. Delaware therefore reads as almost entirely "unsupported" in a census like ours while being entirely correct.

One state carries the whole argument. An unsupported year is not the same thing as a wrong year. A census that treats "no publisher note behind this year" as a defect will flag a correct derivation and a fabricated parse identically, and the two need opposite responses.

The three questions to ask instead of "what is your coverage percentage"

Any vendor, us included, can quote one amendment-history coverage number. That number mixes an extraction gap, a parsing gap, and a publisher that prints nothing, and those three have nothing to do with each other. Ask these instead.

  1. For the jurisdictions I care about, what share of sections carry an amendment year, and what share carry the publisher's own note behind that year? Two numbers, not one. The gap between them is the size of the trust problem.
  2. When you assert a year with no note, where did the year come from? A volume-to-year table is a defensible derivation. A parse of unlabeled page text is not. Silence here is the answer.
  3. Show me one section in this state where you have no year, and tell me whether the publisher prints one. If the publisher prints one and they do not have it, that is a real gap. If the publisher prints none, their number is measuring the publisher, and so is everyone else's.

What to build instead

If you need point-in-time behavior, you build it. There are three routes and most compliance teams need the first two together.

Route one: snapshot the text on the date you relied on it

The only record of what a section said on the day you made a decision is the copy you took that day. Take it, hash it, and keep it with the metadata that makes it defensible later.

import hashlib, json, datetime, requests

BASE = "https://api.vaquill.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

def snapshot(act_id: str) -> dict:
    body = requests.get(f"{BASE}/us/statutes/section/{act_id}/body",
                        headers=HEADERS, timeout=30).json()
    meta = requests.get(
        f"{BASE}/us/statutes/section/{act_id}",
        headers=HEADERS, timeout=30,
        params={"fields": "citation,externalUrl,currentThrough,currencyNote,"
                          "lastAmendedYear,sourceCredit,goodLawStatus"},
    ).json()

    text = body["text"]
    # Normalize before hashing so whitespace churn is not a false positive.
    normalized = " ".join(text.split())
    digest = hashlib.sha256(normalized.encode("utf-8")).hexdigest()

    return {
        "actId": act_id,
        "citation": meta["citation"],
        "text": text, # keep the raw text, the hash alone is not enough
        "sha256": digest,
        "retrievedAt": datetime.datetime.now(datetime.UTC).isoformat(),
        "publisherUrl": meta.get("externalUrl"),
        "currentThrough": meta.get("currentThrough"),
        "lastAmendedYear": meta.get("lastAmendedYear"),
        "sourceCredit": meta.get("sourceCredit"),
    }

Four details in that function do the real work.

  • Store the raw text, not only the hash. A hash proves a change happened. It cannot reproduce what you read.
  • Normalize before hashing. Publishers reflow whitespace constantly. Hash the raw bytes and you will get change alerts that are pure noise, and after the third one nobody reads them.
  • Keep currentThrough next to retrievedAt. They are different facts. retrievedAt is when you fetched. currentThrough is what the publisher says the code is current through, and it is often much older. Design against the second, monitor the first.
  • Keep externalUrl. It points at the publisher's own copy, which is what an auditor or an opposing counsel will actually open.

Route two: watch the sections you care about so you learn when the snapshot goes stale

A snapshot alone rots silently. Pair it with a watch on the board that covers that jurisdiction and corpus, and the two together give you the property you actually wanted from point-in-time: a dated text plus a guarantee you will be told when it stops being current.

Loading diagram...

The branch at the bottom is where most implementations go wrong. A change event means a retrieval differed, which includes a publisher reformatting a page. Re-hash before you page anyone. And never overwrite the old snapshot, because the chain of dated snapshots is the thing that answers "what did we rely on in March" two years from now.

Webhook delivery for these alerts exists today, along with endpoints to list changes, fetch a diff, list deliveries, and send a test. Boards expose their last clean retrieval and its status, so a paused or failing source is visible rather than silently stale. That matters here, because a snapshot paired with a dead watch is worse than no watch at all. The integration shape is in the law change webhooks post.

Route three: go to the enacting text

For federal law there is a genuine historical record, and it is a different corpus. Statutes at Large holds public and private laws as enacted: 5,256 laws across the 113th to 119th Congresses, 2013 onward, served as 110,287 sections under corpusType=SESSION_LAW and refreshed weekly.

An enacted text does not move. What Congress passed in 2016 still reads in 2026 exactly as it read in 2016, which is what makes it a real historical record rather than a reconstruction.

These items carry actStatus: "enacted" and goodLawStatus: "unknown" on purpose. That pairing is the corpus telling you the truth: it knows what was enacted and it is making no claim about whether that text survives in the code today. A real actId looks like SAL_PL116-136_DVA_TII_S1109, CARES Act section 1109. Note that ids are stable and not guessable, so take them from a search or resolve response rather than building them by hand.

Use this route when the question is "what did this act say when it passed." Do not use it when the question is "what is the law today," because an enacted section may have been amended, repealed, or renumbered since.

FAQ

Can I query a US statute as it existed on a past date?

Not through this API, and not through most primary-law APIs. The corpus holds one current text per citation, with no as_of parameter, no version list, and no arbitrary-date diff. What exists instead is amendment history per section, a yearFrom/yearTo currency filter, change events per refresh, and per-section diffs on watched boards.

What is the difference between point-in-time law and amendment history?

Point-in-time gives you the words a section carried on a date you name. Amendment history gives you the dates on which the words changed, in the publisher's own credit line. History tells you when a section moved and never what it moved from, which is why it is a currency signal rather than an archive.

Does yearTo return the law as it stood in that year?

No, and this is the most common misread on the surface. yearTo filters on the last amendment year the publisher credits, so a section amended in 2025 is excluded by yearTo=2024 even though it was in force all through 2024. Sections carrying no amendment credit also drop out entirely as soon as either bound is set.

Why do so many sections have no amendment year?

Three separate reasons, and they need different fixes. The publisher may print no credit line at all, in which case there is nothing to capture; the note may exist and not have been captured; or the note may have been captured and never parsed into a year. Our 2026-08-30 census found 912,388 of 3,518,180 state-corpora sections, 25.9 percent, with no year.

Is a missing amendment year a data quality defect?

Usually not. It is most often a property of the publisher rather than a scraping failure, and Delaware is the clearest example: its publisher prints no year, so the year is derived from a volume-to-year table and Delaware reads as almost entirely unsupported while being correct. An unsupported year and a wrong year are different problems with opposite responses.

How should I evaluate a vendor's amendment history coverage claim?

Do not accept a single percentage, because that number mixes an extraction gap, a parsing gap, and publishers who print nothing. Ask for two numbers per jurisdiction, the share of sections with a year and the share with the publisher's own note behind that year. Then ask where an unsupported year came from, and ask them to show you one section with no year in a state where the publisher prints one.

What is a change event, exactly?

A change event is a detected difference between two retrievals of the same section, logged against a refresh run and carrying a per-section diff. It is not an official effective-date announcement, and a publisher can restate a page without changing the law. Treat it as a signal to look rather than a legal conclusion.

How do I build point-in-time behavior on top of an API that does not have it?

Snapshot the section text on the date you rely on it, store the raw text plus a SHA-256 of the whitespace-normalized text plus retrievedAt, currentThrough, and the publisher URL. Then create a watch on that source so you are told when the text changes, re-hash on every event to filter out reformatting, and write a new snapshot without deleting the old one. The result is forward-only, so start before you need it.

Which corpus gives me a real historical record of federal law?

Statutes at Large, served as corpusType=SESSION_LAW, holds public and private laws as enacted across the 113th to 119th Congresses, 2013 onward. Those texts do not move, which is what makes them historical. They carry goodLawStatus: "unknown" deliberately, because an enacted text says nothing about whether it survives in today's code.

Does an amendment year tell me whether the change is in force?

No. lastAmendedYear is the year the publisher credits for the most recent amendment, which can precede or follow the effective date. Read effectiveDate and priorEffectiveDates where the publisher supplies them, and read currentThrough for what the whole code is current through, because a code compiled through January will not yet reflect a spring session.

The most complete US primary law API.
Every US statute, regulation, constitution, and executive order through one REST and MCP API. 4M+ sections, section-level citations, and links to the official source. Plus a free open dataset.
23 min read

New legal AI guides, weekly.

Priyansh Khodiyar

Priyansh Khodiyar

Co-Founder & CTO

Priyansh leads engineering and AI at Vaquill, from the matter workbench to drafting, document comparison, document matrix, and citation-verified research.