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

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.
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 send | Well formed | Resolvable |
|---|---|---|
42 U.S.C. 1983 | Yes | Yes |
42 U.S.C. § 1983 | Yes | Yes |
42 USC 1983 | Informal | Yes |
42 U. S. C. 1983 | Reporter style with spaces | Should be |
Section 1983 | No | Only as a name |
1983 | No | No, 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.
| Operation | MCP tool | What it is for |
|---|---|---|
| Resolve a citation string | resolve_statute_citation | Turn a human string into an actId and an official source URL |
| Fetch many sections at once | get_sections_batch | Resolve a brief's worth of cites without N round trips |
| Find what cites this section | get_section_cited_by | Build a local citation graph |
| Find the analog in other states | get_section_cross_state | Answer "what is the equivalent of this in the other 49" |
| Pull defined terms | get_section_definitions | Get the definitions that govern the section you resolved |
| Read adjacent sections | get_section_neighbors | Recover the structural context a bare citation drops |
| Read change history | get_section_changes | See 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_S1983for 42 U.S.C. 1983.SAL_PL116-136_DVA_TII_S1109for 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
What is legal citation resolution?
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.
New legal AI guides, weekly.
Further Reading
Hybrid Search Over 12 Million Legal Passages: Why Semantic Alone Fails on Law
Read postParsing Legal Citations in Code: Bluebook Forms, State Variants, and the Ones You Must Refuse
Read postWhat a Legal Data API Should Guarantee: The Schema Contract
Read post128 Fields on One Statute: What Rich Legal Metadata Actually Buys You
Read postFrom a State Website to an API Response: Every Layer a Legal Corpus Passes Through
Read postAmendment History and Point-in-Time Law: What a Legal API Can and Cannot Tell You
Read post
Co-Founder & CTO
Priyansh leads engineering and AI at Vaquill, from the matter workbench to drafting, document comparison, document matrix, and citation-verified research.