Parsing Legal Citations in Code: Bluebook Forms, State Variants, and the Ones You Must Refuse

A legal citation is a human-typed string, and the same provision gets written a dozen ways, so a citation parser is a normalization problem before it is a lookup problem. The hard part isn't 42 U.S.C. 1983. It is the hyphen that came out of a PDF and isn't a hyphen, the trailing period that belongs to one state and not another, and the two-letter acronym two states both claim. Below: the shape classes that actually appear in traffic, five traps that silently break resolution, and the argument that a citator must be allowed to say no.

Reviewed and corrected on 12 October 2026. This is general information, not legal advice. The API results quoted below were run on that date.

TL;DR

  • Citation parsing is string archaeology. Federal codes, state acronym codes, state subject codes, court rules, and colloquial names are five different grammars sharing one input box.
  • Invisible characters are the top silent failure. Six Unicode dash variants render identically to a plain hyphen, so a pasted citation looks perfect and resolves to nothing.
  • A system that cannot read its own published citation is the worst class of bug. If the citation field you serve says O.C.G.A. and your resolver only knows Ga. Code Ann., you hand customers a string you then reject. Test it by round-tripping.
  • Fix trailing punctuation with a retry, never a normalization. Connecticut publishes citations where the trailing period is genuinely part of the citation.
  • Bare acronyms such as MCA and IC are each claimed by two states. A resolver should refuse a form it cannot pin to one state. Refusing is a feature, and confidently wrong is the only unacceptable outcome.
  • A parser deserves a regression suite built from real pasted text, and the checklist at the end gives eight tests to start with.

Three cards reading six Unicode dash variants, five silent failure traps, and refusing rather than guessing on ambiguous citations.

Spot the problem
Question 1 of 4

A user pastes a citation copied from a PDF into your tool. It looks perfect on screen, but nothing resolves. Where should you look first?

This post belongs to our legal data infrastructure series.

For related coverage, see Legal Citation Resolution: Turning a Cite Into the Right Section, Every Time for what a resolver does with a parsed string.

The problem, stated concretely

Ask ten lawyers to cite the same provision and you get several strings. All of them are correct in their own context. Your parser gets one input box.

Style manuals don't save you here. A style manual tells an author how to write a citation. It does not describe what your users will type. Users type what their state prints, what their firm's template prints, and what they half-remember.

Here is what actually arrives, grouped by grammar rather than by publisher. The forms the Vaquill resolver accepts are inventoried in the citation formats reference. Every example in the first five rows resolved on 12 October 2026. The last two rows did not, by design, as the table shows.

Shape classExamplesWhat the parser has to pull out
Federal statute42 U.S.C. 1983, 42 U.S.C. § 1983Title number, code token, section
Federal regulation29 CFR 1910.1200, 29 C.F.R. § 1910.1200Title number, CFR token, part and section joined by a dot
State code by compilation acronymO.C.G.A. 51-1-6, RCW 4.16.080, SDCL 15-2-14, NDCC 32-12.1-03, ORC 2305.10, NMSA 37-1-8, RSMo 516.120, HRS 657-7, CGS 52-584, CRS 13-80-102, KSA 60-513The acronym is the state's own name for its code, so the jurisdiction is implied and never written
State code by named subject codeTex. Prop. Code 24.005, Cal. Civ. Code 1542State abbreviation, subject code name, section. The subject code is load-bearing
Court ruleFed. R. Civ. P. 12, Del. Ct. Ch. R. 126A rule set, which is a different corpus from the statute book
Bare form with no jurisdictionCode 8.01-243 (Virginia), General Statutes 42a-1-101 (Connecticut)Nothing identifies the state. The state is context the string doesn't carry. Both returned resolved: false
Colloquial name"Section 230", "the CARES Act"Not a citation at all. A name for a provision. Both returned resolved: false with missReason: "malformed"

The acronym row is where most engineering time goes. Each acronym is a jurisdiction claim compressed into a few characters, and the compression is lossy. RCW is unambiguous. IC isn't.

The colloquial row is where product expectations go wrong. "Section 230" is a nickname for a federal statute section, and "the CARES Act" is a public law. Neither is a citation your resolver should pretend to parse. Send those down the search path.

A short history of how citations got this messy

The Bluebook began in 1926 as a joint project of four law reviews: Harvard, Columbia, Penn and Yale. The Legal Information Institute notes it takes its name from the color of its cover (LII). It started small. Josh Blackman counted its length across editions on his blog and found 28 pages in 1926 and 511 pages in the 2010 edition (Blackman, April 2011). He also made a point that every parser author should keep in mind. He did not format his own citations to the Bluebook in that post, and still: "somehow, you knew what I was talking about." People forgive a wrong format. Code doesn't.

The Bluebook is also only one grammar. LII says many courts, associations and universities have their own citation rules. The best-known effort to replace the mess was neutral citation: a court-assigned reference that stays the same whatever the publisher, with paragraph numbers for pinpoints. The Wisconsin Bar proposed it to its supreme court in 1994. By 2007 the American Association of Law Libraries and the American Bar Association had endorsed the idea and a number of states, Wisconsin among them, had adopted it (Peter W. Martin, Law Library Journal, 2007). Free Law Project still argues for it today: its page says over a dozen courts have issued neutral citations and shows the shape, a year, a court abbreviation and a serial number, as in "2024 N.H. 1" (Free Law Project). If you ever see that shape in your traffic, it is a different class from everything in the table below.

Statute citations have their own history. On April 27, 2020 the US Supreme Court decided Georgia v. Public.Resource.Org, which concerned the Official Code of Georgia Annotated, the code behind the O.C.G.A. prefix you will meet below. The Court held that the annotations in that code are not copyrightable, under the rule that officials who make law cannot be the authors of the works they produce in their official duties (opinion, supremecourt.gov). The lesson for a parser is mundane. Who owns a citation form, and who publishes the code behind it, has been a real legal question, so treat a state's own printed form as the safest thing to accept.

Five traps, and why each one happens

These are the ones that fail most quietly, ordered from quietest up. Each is a defect any citation parser can have, and each is easy to test for.

1. Invisible dash characters

A normalizer that handles two dash characters, U+2013 and U+2014, misses U+2010, U+2011, U+2012, U+2015, U+2212, and U+FF0D.

Those six look like a plain hyphen in almost every font a lawyer sees. A citation copied out of a PDF or a Word brief carries one and the reader can't tell. Every hyphen-numbered jurisdiction breaks on this, which is most state codes: 51-1-6, 8.01-243, 42a-1-101, 15-2-14.

The user sees a correct citation, the parser returns nothing, and no error message can help, because the string on screen is right.

Why do so many exist? Because Unicode treats them as different characters with different jobs. Its names list gives HYPHEN, NON-BREAKING HYPHEN, FIGURE DASH, EN DASH, EM DASH, HORIZONTAL BAR, MINUS SIGN and FULLWIDTH HYPHEN-MINUS, the last being a wide form of the ordinary hyphen-minus (Unicode names list). Any tool that touches the text can swap one for another, and you can't see the difference.

Fix: normalize the whole Unicode dash family to the ASCII hyphen before you tokenize anything, then add a test that feeds each codepoint through and asserts an identical result. On 12 October 2026 the Vaquill resolver returned the same section, O.C.G.A. § 51-1-6, for O.C.G.A. § 51‑1‑6 typed with U+2011 non-breaking hyphens as for the plain-hyphen form.

2. The doubled section symbol

A federal parser may accept §§ while the state parser doesn't, so 42 U.S.C. §§ 1983 resolves and O.C.G.A. §§ 51-1-6 doesn't, and the difference is invisible to the caller because both inputs are the same shape. Two parsers that disagree about the same token is a worse bug than one parser that is wrong, because the behavior is inconsistent rather than merely broken. (On 12 October 2026 both forms resolved in the Vaquill resolver.)

The doubled symbol is a plural marker in ordinary legal writing. It appears whenever an author cited a range and then trimmed it, which happens constantly in briefs and in model output.

Fix: one symbol-handling routine, shared. A preprocessing step that exists in two places will eventually exist in two versions.

3. Trailing sentence punctuation

42 U.S.C. § 1983. fails in a naive parser. A citation at the end of a sentence carries the period, and users paste sentences.

The obvious fix is to strip trailing punctuation during normalization. That fix is wrong. Connecticut publishes regulation citations where the trailing period is genuinely part of the citation, for example Conn. Agencies Regs. § 38a-147 Exhibit 1. Strip it and you break a form that was correct. On 12 October 2026 the resolver returned that Connecticut citation with its period intact, and returned 42 U.S.C. § 1983 (2026) for the sentence-final form.

So the fix is a retry, not a normalization. Try the string exactly as given. If that misses, try again with trailing punctuation trimmed. The ordering matters more than it looks: a retry can only turn a miss into a hit, and can never turn a hit into a different hit. A normalization can do both.

That asymmetry is the reusable idea. When a cleanup step might destroy a legitimate input, don't clean. Attempt, then attempt again with the cleanup applied, and take the first success.

4. Cross-corpus collisions

Del. Ct. Ch. R. 126 is a Delaware Court of Chancery rule. A statute resolver that sees a Delaware jurisdiction and the number 126 can return a Delaware statute with that number, because the number matched and nobody checked that the code token belonged to the corpus being searched. A bare SCR 31.08 has the same problem: it is a court rule form, and a statute resolver can answer it with a statute.

This is the most dangerous class in the list. It returns a plausible wrong answer with a real citation and a real source URL attached. A miss is visible; a wrong hit isn't, and here it answers a rule of procedure with substantive law.

Fix: refuse another corpus's code. If the token identifies a court rule set, the statute resolver returns nothing rather than falling back on the number. Corpus identity beats numeric match, always. On 12 October 2026 Del. Ct. Ch. R. 126 resolved in the Vaquill resolver to the court rule (corpus STATE_RULES, "Guardian or Trustee may be Examined"), and SCR 31.08 returned resolved: false with missReason: "ambiguous_jurisdiction".

5. The widened regex that started matching a fragment

This one lives in extraction code rather than in the resolver, and it is the subtlest. Suppose a pattern pulls a document number out of a publisher's page, and someone widens it to cover one more state's numbering scheme. The wider alternation can then match a fragment sitting later inside a longer identifier on a different state's pages, and that fragment can become part of a permanent stable id.

A forward-scanning search does not stop at the shortest match, and alternation order cannot save you. The engine scans left to right and returns the first position where any branch matches. Put the longer branch first and you still haven't constrained where the scan starts. A pattern that is not anchored is free to succeed on a tail fragment.

Fix: anchor the pattern, or match the whole field and validate the capture against the shape you expected. Then log what each candidate document became, so a malformed id shows up in an audit record rather than in a customer's bookmark six months later.

The case study: when a citation format round-trips badly

Every Georgia statute section is cited as O.C.G.A. followed by the section number. That is correct. O.C.G.A. is what the Official Code of Georgia Annotated is called, and it is the form a Georgia practitioner types.

Now suppose your resolver only knew Ga. Code Ann.. The API would return a section to a customer, the customer would copy the citation field out of that response, send it back to the resolve endpoint, and get nothing. The system would publish an identifier it could not consume.

That bug class survives an audit for a boring reason: an audit is a photograph, and photographs go stale the moment anything ships. A corpus added after the audit inherits none of its guarantees. The fix cannot be a one-time sweep. A per-jurisdiction round-trip test is better: take a real citation string out of your served data, feed it to the resolver, and assert it comes back to the same section. Adding a jurisdiction adds a test row, and shipping a corpus that cannot read itself fails the build.

You can run the Georgia version of that test against Vaquill today. On 12 October 2026 O.C.G.A. 51-1-6 resolved to STATE_GA_T51_C1_S51-1-6 with the citation field O.C.G.A. § 51-1-6, and the older Ga. Code Ann. 51-1-6 form resolved to the same section.

Normalize before you match

The dash and space variants are what break naive matching, so strip them first. This function was run on 12 October 2026 against both examples below and against each of the eight dash codepoints named above:

import re, unicodedata

DASHES = "\u2010\u2011\u2012\u2013\u2014\u2015\u2212\uFF0D"

def normalize(cite: str) -> str:
    s = unicodedata.normalize("NFKC", cite)
    s = s.translate({ord(d): "-" for d in DASHES})
    s = s.replace("\u00a7\u00a7", "\u00a7").replace("\u00a0", " ")
    return re.sub(r"\s+", " ", s).strip()

normalize("42 U.S.C. \u00a7\u20091983")      # -> '42 U.S.C. \u00a7 1983'
normalize("O.C.G.A. \u00a7 51\u20111\u20116")  # -> 'O.C.G.A. \u00a7 51-1-6'

Run it on both sides of every comparison. A parser that normalizes the query but not the stored form fails on exactly the citations a user pasted from a PDF.

What practitioners say

A lawyer's view and a developer's view of citations differ, and both are fair. In 2016 a Hacker News commenter wrote that open court feeds are incomplete because "most court website feeds don't have Bluebook citable pagination," and that the Supreme Court and "a few states with strong neutral citation rules are exceptions, but, alas, not the rule" (kemitchell, Hacker News, January 2016). That is the pinpoint problem in one sentence: a citation is more than a case name and a volume.

In 2025 a commenter, arguing about AI and legal checks, took the opposite angle: "Legal citations are already a format you can plug into a legal database to get a result" (freejazz, Hacker News, September 2025). For a case reporter inside a subscription database, that's true enough. The traps in this post show up when the input is a state statute written the way that state prints it, pasted from a PDF, with no database in front to forgive it.

Knowing when to refuse

Now the design argument, which is the part most citation parsers get backwards.

Some acronyms are claimed by two states: MCA (Montana Code Annotated, Mississippi Code Annotated) and IC (Indiana Code, Idaho Code), for example. A bare RS, GS or GL is claimed by several. Here is what the Vaquill resolver returned on 12 October 2026 for a set of bare forms:

InputResult
MCA 27-2-204Resolved to Mont. Code Ann. § 27-2-204
MCA 15-1-49resolved: false, missReason: "ambiguous_jurisdiction"
IC 34-11-2-4Resolved to Ind. Code § 34-11-2-4
GS 14-1 and GS 14-1.1Resolved to N.C. Gen. Stat. sections
GL 1-2resolved: false, ambiguous_jurisdiction
RS 14:30resolved: false, ambiguous_jurisdiction
SCR 31.08resolved: false, ambiguous_jurisdiction
Code 8.01-243resolved: false, ambiguous_jurisdiction
General Statutes 42a-1-101resolved: false, ambiguous_jurisdiction

So a bare acronym is not always refused, and one that resolves is a judgment call by the resolver. The response's own hint says what to do: write the code out in full, or pass state. When you know the jurisdiction, pass it, because a constraint beats a guess.

Consider what a wrong guess costs. MCA 27-2-204 under a Montana reading and the same string under a Mississippi reading would be two different provisions of two different states' law. A resolver that picks one returns a real section, with a real citation, a real source URL, and a real body of text. Everything about the response looks right except which state's law it is.

No downstream check catches that. Not a citation verifier, because the citation resolves. Not a human skimming a source link, because the link works and the text reads like law. The error survives into a filing.

Compare the cost of a miss. resolved: false sends the user back to add a jurisdiction, which takes a few seconds.

For a citator, the ability to refuse is the feature. A parser that resolves 100 percent of inputs isn't more capable than one that resolves 90 percent and refuses 10. It is the same parser with the safety removed.

This is the same failure mode that makes legal AI dangerous, at a different layer. A model that won't say "I do not know" invents a case. A resolver that won't say resolved: false invents a jurisdiction. Confidently wrong is the only truly unacceptable outcome here, because it is the one nobody catches.

A rule worth holding: when a citation names no jurisdiction and more than one jurisdiction claims the form, refuse or ask. Do not rank by corpus size. Do not prefer the state the caller queried last. Do not return the most likely one with a lower confidence score, because a confidence score in a JSON response is a number nobody reads.

Parsing versus searching

A resolved citation and a search result are different things. The search endpoint documents that passing a citation as the query resolves it to that section at rank 1; on 12 October 2026 a search for 42 U.S.C. 1983 returned that section first with matchQuality: "exact". When a string does not parse as a citation, it falls back to ranking by meaning, and a ranker built on meaning has little to go on with a symbolic string such as O.C.G.A. 51-1-6. An identifier carries almost no meaning as text.

The generalization for anyone building retrieval over structured content: exact-identifier lookup and semantic search are different problems, and a system that only does the second feels unreliable precisely on the queries users are most confident about. Getting a typed citation wrong costs more trust than ten mediocre semantic results.

A checklist for your own citation parser

Eight items. Run them against whatever you have built.

  1. Feed it every Unicode dash. U+2010, U+2011, U+2012, U+2013, U+2014, U+2015, U+2212, U+FF0D. All eight must produce the same result as an ASCII hyphen. Add the non-breaking space while you are there.
  2. Round-trip your own output, per jurisdiction. Take a citation string out of your own served data, resolve it, and assert you land on the same record. Make this a test that grows when you add a jurisdiction, not a script someone runs.
  3. Use one preprocessing routine, shared by every code path. If federal and state parsing normalize separately, they will diverge, and the divergence will be invisible from outside.
  4. Handle trailing punctuation as a retry, not a strip. Try as given, then trimmed. Never let a cleanup step destroy a form a publisher prints legitimately.
  5. Refuse another corpus's code token. A court rule must not be answered from a statute table because the number matched. Corpus identity outranks numeric match.
  6. Anchor every extraction pattern, or validate the capture. A forward-scanning search finds fragments inside longer strings, and alternation order doesn't prevent it.
  7. Enumerate the ambiguous forms deliberately and return a refusal for each. Write them down as a list with the reason. resolved: false with an explanation beats a guess with a confidence score.
  8. Test with strings from real documents, not strings you typed. Copy from a PDF, from Word, from a court's website, from a model's output. Your own keyboard produces only clean input, which is the input that already works.

Items 1, 4 and 8 find something in most parsers within an hour.

FAQ

Ambiguity across jurisdictions, followed by invisible characters. Acronyms like MCA and IC are claimed by more than one state, and Virginia and Connecticut print bare forms that name no jurisdiction at all. The character problem is quieter: six Unicode dash variants render identically to a hyphen, so a pasted citation looks correct and doesn't resolve.

Do I need the Bluebook to parse citations?

No. A style manual tells an author how to write a citation; it doesn't describe the strings your users actually type. Build against the forms government publishers print and the forms practitioners in each state use, then test with real pasted input. The Bluebook is copyrighted, and reproducing its tables in your code or docs is a separate problem you don't need to take on.

Why would an API refuse to resolve a citation it could probably guess?

Because a wrong guess returns a real section from the wrong state, with a working source URL and text that reads like law. Nothing downstream catches that: not a citation verifier, because the citation resolves, and not a human skimming the link. A refusal costs the user a few seconds. A wrong jurisdiction can reach a filing.

How do you handle a citation with a trailing period?

Try the string exactly as given first, then retry with trailing punctuation trimmed, and take the first success. Stripping punctuation up front looks simpler but breaks Connecticut, which publishes regulation citations where the final period is part of the citation. A retry can only turn a miss into a hit; a normalization can turn a hit into a different hit.

Why does searching a citation string sometimes return poor results?

Because a ranker trained on meaning scores a symbolic identifier poorly, and a citation carries almost no meaning as prose. Resolving the citation first, or passing it to a search endpoint that documents exact-citation matching, avoids the problem. If the string does not parse, it competes on ordinary relevance with every other section that shares a number.

Resolution answers "which single section is this exact identifier," and it either succeeds or refuses. Search answers "which sections are about this," and it returns a ranked list. Keeping them as separate endpoints prevents a resolver that guesses and a search that pretends to be exact.

Can I build the citation format list myself from public sources?

Partly. Each state publishes its own code and its own preferred form, so the forms are observable, but assembling and maintaining them across the state and territory codes is ongoing work rather than a one-time table. The collection code is published at open-us-law if you want to read how the shapes are handled.

What happens if a citation resolves but the section moved?

The response carries renumberedTo and transferredTo fields, which are how you follow a citation that moved rather than treating the old identifier as dead. Sections also carry supersedes and supersededBy, and the citation enrichment recipe shows how to follow them. A resolver that only answers "found or not found" leaves you unable to trace a provision across a renumbering.

Try it against a citation you know is hard

Take the ugliest citation in your inbox, the one copied out of a PDF with a dash you can't identify, and send it to the resolve endpoint in the playground. The interesting result is which tool tells you honestly that it can't resolve it.

Connect our US primary law database.
Every US statute, regulation, constitution, and executive order via REST, MCP or SQL. 5.4M+ sections, section-level citations, and links to the official source. Plus a free open dataset.
Updated October 12, 202625 min read

New legal AI guides, weekly.

Priyansh Khodiyar

Priyansh Khodiyar

Co-Founder & CTO

Priyansh leads engineering and AI at Vaquill AI: the pipelines that pull statutes, regulations and court rules from every US jurisdiction's official publisher, and the REST API, MCP server and open dataset that serve them.