Legal Citation Resolution: Turning a Cite Into the Right Section, Every Time

Legal citation resolution is the job of turning a human-written citation string into one specific, retrievable section of law. It sounds like string parsing. It is not, because the same provision gets cited a dozen different ways, several of those ways are ambiguous across states, and some of the most common "citations" are not citations at all. The hardest engineering problem in a resolver is not coverage. It is knowing when to refuse.

A resolver that returns a plausible wrong section is worse than one that returns nothing, because nothing gets checked and a wrong answer gets filed.

TL;DR

  • A citation is a human string, not an identifier. "42 U.S.C. 1983", "42 USC 1983", and "Section 1983" all point at one section, and only one of them is a well-formed citation.
  • State abbreviation forms are set by state style manuals, not by a national standard. O.C.G.A., MCA, IC, GS, and GL are all real, and several collide across jurisdictions.
  • The single-regex approach fails in a specific, predictable way. A forward-scanning search does not stop at the shortest match, so widening a pattern to catch one more state makes it match a fragment inside another string.
  • A wrong match is more expensive than a miss. The most valuable property of a citator is a clear not-found with a suggestion, not a confident guess.
  • actId is stable and not guessable. Take it from a resolve or search response. Hand-built ids usually 404.
  • Unresolved citations are still charged, failed calls are not. The parsing and lookup work happens either way, so an honest not-found costs the same as a hit.

A five-step flow from a raw citation string through normalization, jurisdiction matching, resolution to one section, and refusal when ambiguous.

4-question check
Question 1 of 4

Why does widening a single citation regex to cover one more state cause wrong answers?

This post sits in our legal data infrastructure series.

For related coverage, see Parsing Legal Citations in Code: Bluebook Forms, State Variants, and the Ones You Must Refuse for the string formats behind every case below, Hybrid Search Over 12 Million Legal Passages: Why Semantic Alone Fails on Law for where a resolved citation enters the ranking, and Amendment History and Point-in-Time Law: What a Legal API Can and Cannot Tell You.

The shape of the problem

Every legal AI product hits the same wall. A model, a user, or a contract emits a string that looks like a citation, and something has to turn it into a document. Between the string and the document sit four classes of hard case, each breaking a different naive assumption.

Loading diagram...

Class 1: federal forms that vary by punctuation

Start with the easy one, which is still not easy. Consider the civil rights cause of action.

String a user might sendWell formedResolvable
42 U.S.C. 1983YesYes
42 U.S.C. § 1983YesYes
42 USC 1983InformalYes
42 U. S. C. 1983Reporter style with spacesShould be
Section 1983NoOnly as a name
1983NoNo, and it must not be

Punctuation and the section symbol are cosmetic, and normalization handles them. The interesting rows are the last two.

Section 1983 is a colloquial name. So is Section 230, so is Rule 12(b)(6), so is the ADA. They carry meaning to a lawyer and almost no structure to a parser. A resolver that treats them as citations will fail or, worse, guess.

1983 is a number. It is also a year. Resolving a bare number should be answered with a no, because the failure mode is silent and unbounded.

Class 2: state abbreviations, set jurisdiction by jurisdiction

There is no national standard for abbreviating a state code. Each state's courts and legislature set the form, and the forms collide. O.C.G.A. is Georgia, MCA is Montana or Mississippi, IC is Indiana or Idaho, GS is North Carolina, and GL is Massachusetts, which numbers by chapter and section rather than title and section. The full inventory of shape classes, and the parsing traps that break them, is in Parsing Legal Citations in Code.

What matters here is the consequence for the resolver. Bare initialisms cannot be disambiguated from the string alone. They need jurisdiction context from elsewhere in the request, the document, or the conversation, and the right behavior is to require that context or refuse. A resolver that picks Indiana because Indiana is more common is right most of the time and catastrophically wrong the rest of the time, in a way the caller cannot detect.

Which forms a given resolver actually parses is an empirical question. Send the form, read the response, store the result. The forms we accept are listed in the citation formats reference.

Class 3: regulations, which have a different tree

Statutes are title, chapter, section. Regulations are title, part, section, and the section number itself contains a period that is structural rather than decorative.

29 C.F.R. 1910.1200 is the hazard communication standard. 1910 is the part, 1200 is the section within it. Splitting on the period and treating 1200 as a subsection loses the part, and the part is what disambiguates.

State administrative codes are worse, because the naming is not shared. The California Code of Regulations, the Texas Administrative Code, and the Code of Maryland Regulations each structure and cite themselves differently. Across 52 jurisdictions the regulations corpus runs to roughly 1.46 million sections, each with its own citation grammar.

What a resolve call returns

curl -s https://api.vaquill.ai/api/v1/us/statutes/resolve \
  -H "Authorization: Bearer $VQ_KEY" \
  -d '{"citation": "O.C.G.A. 51-1-6"}'
{
  "resolved": true,
  "actId": "ga-ocga-51-1-6",
  "citation": "O.C.G.A. § 51-1-6",
  "jurisdiction": "ga",
  "corpusType": "STATE",
  "confidence": "exact"
}

The important branch is the other one. When a form is claimed by two jurisdictions, the response is "resolved": false with the candidates listed, rather than a guess that silently returns another state's law.

Class 4: renumbered, repealed, moved, or superseded

This is the class that separates a lookup table from a citator.

A citation written in 2014 may point at a section that no longer exists under that number. Four things could have happened:

  • Renumbered. The provision is intact under a new number. The right answer is the new section plus a note that the number moved.
  • Moved. The provision went to a different title or chapter, sometimes a different code entirely.
  • Repealed. The number is now empty. The right answer is "repealed," not "not found," because those mean different things to a lawyer.
  • Superseded. A newer enactment covers the ground, and the old text may still be printed. This is the dangerous one, because a naive retriever serves it as current law.

Why the single-regex approach fails

Almost every citation resolver starts as one regular expression, and then gains an alternative each time a state fails to resolve. The failure is not that the regex gets slow. It is that a forward-scanning search does not stop at the shortest match. The engine returns the first position where any alternative succeeds, so a widened pattern can match a fragment sitting partway through a longer string. That produces a match, not an error, and the fragment then gets minted into a permanent identifier and stored. A narrow regex misses, and a miss is visible. A widened regex mislabels, and a mislabel is invisible until a lawyer catches it. The anchoring and staged-parse fixes are worked through in detail in the parsing post.

One fix belongs here, because it is a property of the resolver rather than of the pattern. Round-trip the result. Take the section you resolved to, render its own citation, and resolve that. If a code's own published citation form does not come back to that code's sections, the resolver has a hole. Making that a test rather than an assumption is the difference between a citator you can trust and one that has never been challenged.

Knowing when you do not know

A citator's most important property is its refusal behavior. Coverage is second.

When resolution fails, three things belong in the response:

  • A clear not-found, distinguishable from an error and from an empty result set.
  • A reason. Unknown abbreviation, ambiguous jurisdiction, structurally valid but no such section, and repealed are four different situations, and the caller should be able to branch on them.
  • A suggestion. If the string almost matched something, say what. The most common cause of a failed resolve is a caller who has the right provision and the wrong form.

Returning the nearest neighbor as if it were a hit optimizes a demo and breaks a product. A wrong section that looks right gets pasted into a memo. A not-found gets checked by a human in thirty seconds.

What the resolve API gives you

Resolution is one operation. The rest of the surface exists because a resolved citation is rarely the end of the question.

OperationMCP toolWhat it is for
Resolve a citation stringresolve_statute_citationTurn a human string into an actId and an official source URL
Fetch many sections at onceget_sections_batchResolve a brief's worth of cites without N round trips
Find what cites this sectionget_section_cited_byBuild a local citation graph
Find the analog in other statesget_section_cross_stateAnswer "what is the equivalent of this in the other 49"
Pull defined termsget_section_definitionsGet the definitions that govern the section you resolved
Read adjacent sectionsget_section_neighborsRecover the structural context a bare citation drops
Read change historyget_section_changesSee what moved on that section

Two deserve a note.

Batch retrieval changes an integration's shape. A brief has 40 citations in it, and forty sequential resolves plus forty sequential fetches is a slow, fragile pipeline. Batch turns citation checking into one pass, billed per item, and the citation lookup recipe walks that pass end to end.

Neighbors is underrated. A citation names a section, but the meaning of that section frequently lives in the one before it, which carries the definitions and scope. When you resolve for an LLM's benefit, neighbors is often the difference between a correct answer and a confident misreading.

The actId contract

An actId is a stable identifier for one citable section. Two examples:

  • USC_T42_C21_S1983 for 42 U.S.C. 1983.
  • SAL_PL116-136_DVA_TII_S1109 for section 1109 of the CARES Act, in the Statutes at Large corpus.

They look guessable. They are not. The second carries a public law number, a division, and a title, and no pattern-matching on the first will let you construct it.

The rule: take the actId from a response and store it. Search returns it, resolve returns it, and every downstream operation takes it. Building one by hand produces a 404, which fails loudly but avoidably.

This is deliberate, not an accident of the schema, and the section id reference explains the shape. An id you can construct is an id callers will construct wrongly. An id you must fetch keeps the corpus as the authority on its own structure.

What happens when you send garbage

Unknown filter values are rejected with a 422, not silently matched against nothing. A typo in a corpusType or source value returns an error rather than an empty result set that looks like a coverage gap. That empty-set behavior is the most common cause of "your API has no data for X" reports against systems that allow it.

The source filter's error message lists every valid code. Send a bad one and the response tells you the whole vocabulary, at the point of failure, which is where documentation actually gets read.

Provenance, and why it belongs in a resolution post

Resolved sections carry the publisher's own source URL where the publisher exposes one, and the sourcing rule is a government publisher, never a commercial aggregator and never one as a fallback. How to tell where a statute actually came from walks the fields that make that checkable.

That matters more for resolution than for search. When a resolver says "this citation points at this text," the only way to audit the claim is to open the publisher's page and compare. A resolution result without a checkable source URL is an assertion.

Where a state's only consolidated text sits behind a commercial licensee, the corpus carries a gap and declares it. Arkansas and Vermont court rules are in that position: the judiciary sites publish the amendment stream, and consolidated text comes only from a commercial licensee. Colorado publishes rule-change orders, and its last government compilation is from 2023, so serving it would mean serving superseded procedure as current law.

What to design around

Versioning here is the amendment record, not a date parameter. A citation maps to exactly one stored text, the one in force now, and no as_of=DATE argument will move you off it. What exists is amendment history per section, a lastAmendedYear, a yearFrom/yearTo currency filter that reads the last amendment year the publisher credits, and per-section diffs on watched sources going forward. If your product needs the text as it stood on a past date, resolve will give you today's section and you will need another source for the history.

About a fifth of sections carry no amendment credit at all, because some publishers print none. Those are excluded once you set either currency bound, which is correct and also something to know before you set one.

Paused corpora are declared with their reasons in the freshness array of the free coverage response. A paused corpus is complete and officially sourced but not currently re-pulling, most often because the publisher's own access rules require it.

You can check current per-jurisdiction coverage in the API coverage docs, try resolution against live data in the playground, or read the parsers themselves, since the collection layer is published at open-us-law.

FAQ

It is the process of turning a written legal citation into a specific, retrievable section of law. A resolver normalizes the string, identifies which code it belongs to, parses the structural parts, and looks up the matching section. The output should be a stable identifier plus the publisher's own source URL.

Why do state citation formats vary so much?

Because each state's courts and legislature set their own citation style, and there is no binding national standard for primary law abbreviations. Georgia uses O.C.G.A., Montana uses MCA, North Carolina uses GS, and Massachusetts uses a chapter-and-section form. Several of those initialisms collide across states, which is why jurisdiction context matters.

Is "Section 230" a citation?

No. It is a colloquial name for 47 U.S.C. 230. The string carries no title and no code, so a resolver has to treat it as a name lookup against a list of well-known provisions rather than as a citation parse. The same applies to "Section 1983" and "Rule 12(b)(6)".

What happens when a cited section has been repealed?

A good citator distinguishes repealed from not-found, because they mean different things. Repealed means the number existed and is now empty. Not-found means the resolver could not identify the citation at all. If a provision was renumbered or moved instead, the useful answer is the successor section plus an explicit note that the number changed.

Can I build an actId myself instead of calling resolve?

No. Ids are stable but not guessable, and hand-built ones usually 404. USC_T42_C21_S1983 includes the chapter, which the citation string does not name, and session law ids carry public law numbers and division structure. Take the id from a search or resolve response and store it alongside your own record.

Why am I charged for a citation that did not resolve?

Because the work is the same either way. Normalization, code identification, and corpus lookup all run whether or not the string matches a section. Charging only for hits would push a resolver toward returning a guess instead of an honest not-found. Calls that fail on our side, meaning 5xx errors, are not charged.

How do I resolve 40 citations from one brief efficiently?

Use batch section retrieval rather than looping. It turns a fragile sequence of round trips into one pass, and billing is per item, so you pay for what you asked for. Pair it with the neighbors operation if you are feeding results to an LLM, since the governing definitions frequently sit in the preceding section.

Does the API support point-in-time citation lookup?

Versioning here runs off the amendment record rather than a date parameter. Each citation maps to a single stored text, the one currently in force, and the API exposes no date argument to move off it. Amendment history, a lastAmendedYear per section, a yearFrom/yearTo currency filter, and per-section change records are what exist instead.

What is the difference between citation resolution and citation verification?

Resolution answers "which section does this string point at." Verification answers "does that section support the claim it was cited for." A system needs both, and the verification layers are covered in our four-layer citation verification post.

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.
18 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.