---
title: "Find the citations in a text and resolve each"
method: POST
path: "/api/v1/us/statutes/extract-citations"
tags: ["US Statutes"]
---

# Find the citations in a text and resolve each

`POST /api/v1/us/statutes/extract-citations`

## Request

- Base URL: `https://api.vaquill.ai`
- URL: `https://api.vaquill.ai/api/v1/us/statutes/extract-citations`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`
- TypeScript: <https://skmtc.dev/vaquill/apis/vaquill-developer-api/docs/api/v1/us/statutes/extract-citations/post.ts> (a function with typed inputs; its response is checked against the schema)

Read a block of text (a brief, a memo, a model's answer) and return every distinct non-case legal
citation in it, each resolved to the section it names. The text-in counterpart of
`POST /us/statutes/resolve`: one call instead of extracting the citations yourself and sending them
in batches of 50. The text is never stored.

Extraction reads statutes, regulations, court rules, constitutions and registers, in federal and
state form (`42 U.S.C. § 1983(b)`, `Cal. Civ. Code § 1942`, `Fed. R. Civ. P. 12`, `8 CCR 1206-2`,
`N.Y. Const. art. I, § 8`); it does not extract case citations. It is measured, not perfect (about
0.92 to 0.98 precision and 0.80 to 0.85 recall on unseen opinion text), so a citation can be missed
and a non-citation read as one; `resolved` is what we verified. The same text can yield different
citations when `extractorVersion` changes. Pass `state` (the document's jurisdiction) so a citation
that names none, such as `Civil Rule 56.1`, is checked against the right one.

**Cost**: 2 credits per distinct citation (section, not pinpoint) sent to the resolver, the
same as `POST /us/statutes/resolve`. The extraction itself is free: `resolve: false` returns the
citations found and charges nothing. An unresolved citation is charged (the verdict is the
product); a session-law citation (`skipped: session_law`, read it under `/us/session-laws`) is not
resolved here and not charged. A citation we fail to process is refunded one by one, and an outage
that takes half of them or more answers 503 and refunds the call. The charge is taken after
extraction and before any lookup, so `creditsConsumed` is known up front. One call resolves at
most 200 distinct citations, so at most 400 credits; `truncated: true` says
more were found and only the listed ones were charged.

**Limits.** `text` is at most 200,000 characters (a 422, never a truncation). A key may send
5,000 resolutions a day through this route; past that the
call answers 429 with `Retry-After` and nothing is charged.

**Rows.** `raw` is the first mention copied exactly from your text and `charStart` / `charEnd` its
offsets (`text[charStart:charEnd] == raw`, in Unicode code points); a section read out of a list
(`§§ 1981, 1983`) has no span of its own, so its offsets are null. A citation and its pinpoint are
one row however often the pair appears (`mentions` counts them), and different pinpoints of one
section are separate rows resolved and charged once. `counts.found` is rows,
`counts.distinctSections` is sections (the unit of price and cap). `includeUnresolved: false`
drops only rows that were sent to the resolver and did not resolve; `skipped` rows are always kept
and `counts` and the charge never change.

**Ranges.** A range (`§§ 2510-2523`, `§ 201 through 219`, `29 U.S.C. § 201-219`) returns its two
ends as rows marked `rangeRole` (`start` / `end`) with `rangePartner` naming the other end; each end
is resolved and charged as its own section. A short word range (plain section numbers, up to 25
between the ends) also lists the sections between as `rangeRole: interior`, `skipped:
range_interior` rows, which are never resolved or charged (`counts.interior`). A wider range, or a
hyphen range, returns its ends only and `notes` says so (browse between them with
`GET /us/statutes/divisions`). A list stops where the next citation starts, so
`42 U.S.C. §§ 1981, 1983 and 28 U.S.C. § 1331` is three citations.

**Pinpoints.** A citation printed with a pinpoint also carries `pinpointMatch`, the unit it names
located inside the resolved section, as on `GET /us/statutes/resolve`, at no extra cost and for at
most about 20 seconds per call. Read `pinpointMatch.matched` first: `false` (or `matchLevel`
`ancestor` or `none`) says the section has no such unit, and `structure.quality: heuristic` says the
outline was parsed from the text and failed a check. A unit that exists is not proof it says what
your text claims: compare `pinpointMatch.text` with the claim. A row whose pinpoint could not be
looked up keeps its resolution and gets a `pinpointNote`.

**Unresolved rows** carry `missReason`, `candidates` and `missReasonHint`, as on
`GET /us/statutes/resolve`, at no extra charge.

## Headers

- `Idempotency-Key` string, nullable: Optional. 16 to 255 visible ASCII characters (a UUIDv4 works). Repeating the same request with the same key replays the original 2xx response for 24 hours without charging again. The same key on a different request is rejected. With the account's `cacheQueryDerivedData` retention setting off, no body is stored and a retry answers 409 `idempotency_replay_unavailable`, still without a second charge.

## Request body

- ExtractCitationsRequest
  - `text` string, required: The text to read, 1 to 200,000 characters (a brief is about 30,000). Longer is a 422, never a silent truncation: a partial list reported as the whole is a wrong answer. Plain text; a body that is mostly binary is a 422.
  - `state` 'al' | 'ak' | 'as' | 'az' | 'ar' | 'ca' | 'co' | 'ct' | 'de' | 'dc' | 'fl' | 'ga' | 'gu' | 'hi' | 'id' | 'il' | 'in' | 'ia' | 'ks' | 'ky' | 'la' | 'me' | 'md' | 'ma' | 'mi' | 'mn' | 'ms' | 'mo' | 'mt' | 'ne' | 'nv' | 'nh' | 'nj' | 'nm' | 'ny' | 'nc' | 'nd' | 'mp' | 'oh' | 'ok' | 'or' | 'pa' | 'pr' | 'ri' | 'sc' | 'sd' | 'tn' | 'tx' | 'ut' | 'vt' | 'vi' | 'va' | 'wa' | 'wv' | 'wi' | 'wy', nullable: The jurisdiction of the DOCUMENT as a two-letter state code (the forum of a filing, the governing state of a matter). It labels and scopes a citation that names no jurisdiction of its own (`Civil Rule 56.1`, `Gov. Code § 12940`); a citation that names a jurisdiction is always checked against that one, whatever this says.
  - `resolve` boolean: Resolve each distinct citation to its section (priced per distinct citation, see the endpoint's Cost). `false` returns the extraction alone, which is free.
  - `includeUnresolved` boolean: `false` drops the rows that were sent to the resolver and did not resolve. Rows with `skipped` set (`session_law`, `error`, `range_interior`) are always kept, because they are not a verdict about the citation. The `counts` and the charge are unchanged: an unresolved citation is a verdict that was paid for. Ignored when `resolve` is false.

## Response `200`

The distinct citations found, each resolved unless `resolve` is false.

- ExtractCitationsResponse
  - `citations` ExtractedCitationRow[]: One row per distinct citation and pinpoint, in order of first appearance.
    - `citation` string, required: The citation, normalised. Pass it to `GET /us/statutes/resolve` to re-check.
    - `raw` string, required: The first mention, copied character for character from your text, pinpoint included (a wrapped line or a no-break space stays as you sent it). A section read out of a list or range (`§§ 1981, 1983`) has no span of its own: its `raw` is then the normalised citation and `charStart` / `charEnd` are null.
    - `charStart` integer, nullable: Offset of the first mention's first character in `text`, counted in Unicode code points (a Python index; JavaScript counts UTF-16 units, which differ only past the Basic Multilingual Plane). `text[charStart:charEnd] == raw`. Null when `raw` has no span of its own.
    - `charEnd` integer, nullable: Offset just past the first mention's last character (see `charStart`).
    - `rangeRole` 'start' | 'end' | 'interior', nullable: `start` or `end` when your text wrote this section as one end of a range (`18 U.S.C. §§ 2510-2523`, `§§ 2510 to 2523`, `29 U.S.C. § 201-219`): the two ends are two rows, each resolved and charged as its own section. `interior` marks a section BETWEEN the ends of a short word range (`§§ 101 through 104` lists 102 and 103, up to 25 per range): it is listed for completeness, carries `skipped: range_interior`, is never resolved and is never charged (resolve it with `POST /us/statutes/resolve`). A longer range, and a hyphen range, return the ends only. Null for a citation that is not part of a range.
    - `rangePartner` string, nullable: The `citation` at the other end of the range, when `rangeRole` is `start` or `end`. For an `interior` row, the `citation` of its range's `start`; the `end` row follows the interior rows.
    - `type` string, required: `Statute`, `Regulation`, `Court Rule`, `Constitution`, `Admin. Register`, `Session Law` or `Other`. A coarse label read from the string; it can be wrong, and `resolved` is what we verified.
    - `subtype` string, required: A finer label: `usc`, `cfr`, `state_statute`, `state_rule`, `local_rule`, ...
    - `jurisdiction` string, nullable: `US` or a two-letter state code, when one could be read.
    - `jurisdictionSource` 'cite' | 'court', nullable: `cite` when the citation names its jurisdiction itself; `court` when it was inferred from your `state`. Null when unknown.
    - `pinpoint` string: The pinpoint this row checks (`(b)`), empty when the mention had none.
    - `mentions` integer: How many times this citation WITH this pinpoint appears in the text (one row, however many).
    - `resolved` boolean, required: True when the citation resolved to a section. False when it did not, when `resolve` was false, or when it was `skipped`.
    - `actId` string, nullable: The section it resolved to. Read it at `/us/statutes/section/{actId}`.
    - `subsection` string, nullable: The pinpoint the resolver echoed, as on `/resolve`, dot-joined (`b`, `b.2`). The unit itself is in `pinpointMatch`.
    - `matchLevel` 'container' | 'multiple' | 'repaired' | 'register_reference', nullable: Present only when `section` is NOT the exact unit you cited. `container`: you cited a container in one of the supported forms (a Missouri, New Jersey, West Virginia or Wyoming regulation series or chapter, a constitution article or amendment, an Indiana chapter, an Illinois article, or a U.S. Code chapter such as `18 U.S.C. ch. 96`; a U.S. Code subchapter is not read), and `section` is the FIRST section inside it (see `container` for how many there are and what they are). A state STATUTE chapter, subchapter, title, article or part cited with its container word and a code name we read for that state (`8 Del. C. ch. 1, subch. VII`, `Tex. Penal Code ch. 19`, `N.Y. Penal Law art. 125`, `Mass. Gen. Laws ch. 93A`, `La. R.S. Title 14`) also answers `container`, with `section` the first live section in statutory order, but only where we hold that container and its number is unique within the code. A container we do not hold, a missing or unrecognised code name, a regulation or rule set, or a number that repeats in the code (California repeats chapter numbers across its titles) when the citation names more than one level, returns `resolved: false` with `missReason: container_not_supported`. An article that IS the section unit (`La. Civ. Code art. 2315`, `Tex. Code Crim. Proc. art. 38.23`) is read as a section. To browse chapters, titles and articles use `GET /us/statutes/divisions`. `multiple`: you cited several sections (`§§ 8, 10`, `§§ 1983-1985`) and `section` is the first of them (see `citedSections`), or your citation fits more than one held section (`D.C.COLO.LR 7.1` is both Local Civil and Local Criminal Rule 7.1) and every candidate is listed in `container`; a state statute container number that repeats within its code (`Cal. Penal Code tit. 8` is a title of each of four parts) lists each candidate container's first section there, never picking one, when there are at most 10. `repaired`: the code name you wrote is a recognised miscite and `section` is the one it names (see `repair`); or (`repair.kind: prior_edition`) the text you cited has been replaced by a current one, and `section` is NOT the text you cited: a C.F.R. or U.S.C. section the current code no longer holds, answered with the MOST RECENT historical annual edition we hold, or a superseded rule set whose rule numbers were kept, answered with the current rule (`Tex. R. Civ. Evid. 803` is answered by `Tex. R. Evid. 803`), or a state regulation we hold only as superseded filings, answered with the latest filing (`W. Va. Code R. § 64-14-5`); `repair.interpretedAs` names what was served and why, and `repair.alternatives` lists the other held editions, if any; or (`repair.kind: former_law`, `former_number`) you cited a FORMER section number, recodified or renumbered since, and `section` is the successor the state's own disposition table or history line names, never the former text (a former section split into several answers `multiple`, every successor in `citedSections`). or (`repair.kind: code_name_inferred`) your citation named no code (`CODE ANN. § 73.001`, `Code 4121-3-34`) and `section` is the only one in the state that carries that number, so the code was inferred from the number and not read from your citation; or (`repair.kind: repealed_range`, `renumbered_range`, `range_member`) you cited a New York or California section the publisher printed only inside a run of sections (`6 NYCRR 420.4` inside `420.3 to 420.4`), and `section` is that stub, not the section's own text (`repair.interpretedAs` names the range); `register_reference`: you cited a state register page, and `section` is a rule whose history cites it (see `registerReference`). `resolved` keeps its meaning (a section was returned) and is true at every level; read `matchLevel` to tell an exact section from a first-of-several. Absent for an exact match.
    - `pinpointMatch` PinpointResult: A pinpoint located in a section. See `PINPOINT_DESCRIPTION`.
      - `requested` string, required: The pinpoint as asked for, in canonical form.
      - `path` string[]: The path of the unit this block describes. Equal to the requested path on an exact match; shorter under `matchLevel: ancestor`; empty when nothing was located.
      - `label` string, nullable: The unit's label as printed.
      - `level` string, nullable: The unit's drafting level: `subsection`, `paragraph`, `subparagraph`, `clause` or `subclause`.
      - `inferred` boolean: True when the located unit was read from inside a paragraph (a run-in list such as `either (i) x, or (ii) y`) and not printed on its own line by the publisher. The rest of the tree is the publisher's own.
      - `matched` boolean, required: True only when the WHOLE pinpoint exists in the section (`matchLevel: exact`). False is a verdict about the pinpoint, not a failure of the call, and is charged like any resolved section.
      - `matchLevel` 'exact' | 'ancestor' | 'none', required: `exact`: the whole path exists. `ancestor`: only a leading part exists and the block describes the deepest unit that does. `none`: not even the first label exists, or the section has no recoverable outline.
      - `text` string, nullable: The unit's own text, without its descendants. Null unless a unit was located.
      - `leadIn` PinpointLeadIn[]: Each ancestor's own text, outermost first. Empty for a top-level unit.
        - `path` string[], required: That ancestor's path.
        - `label` string, required: Its label as printed.
        - `text` string, required: The ancestor's own text: the lead-in that the located unit completes.
      - `fullText` string, nullable: The unit and everything beneath it, as printed. Null unless a unit was located.
      - `children` PinpointChild[]: The units directly beneath this one.
        - `path` string[], required: The child's path.
        - `label` string, required: Its label as printed.
        - `level` string, required: Its drafting level.
        - `text` string, required: Its own text, without its descendants.
        - `inferred` boolean: True when the unit was read from inside a paragraph (a run-in list such as `either (i) x, or (ii) y`) and not printed on its own line by the publisher.
      - `siblings` PinpointSiblings: The labels either side of the located unit, within the same parent.
        - `previous` string, nullable: The previous sibling's label, null for the first.
        - `next` string, nullable: The next sibling's label, null for the last.
      - `charStart` integer, nullable: Offset of the unit's first character in `plain`.
      - `charEnd` integer, nullable: Offset just past the unit's last character in `plain` (including its children).
      - `kind` 'range' | 'list', nullable: Present when your citation named more than one unit: `range` (`(a)-(c)`) or `list` (`(a), (b)`). This block describes the FIRST unit only; the others are in `also`. Ask for each with `GET /us/statutes/section/{actId}/body?pinpoint=`. Omitted from the response when it has no value.
      - `also` string[]: With `kind`: the other units your citation named, as pinpoints (`["(b)", "(c)"]`), at most 20. Omitted from the response when it has no value.
      - `unmatched` string[]: Under `matchLevel: ancestor`, the part of the requested path that does not exist, e.g. `["7"]` when `(c)(7)` was asked and the section ends at `(c)(4)`. Omitted from the response when it has no value.
      - `available` string[]: On a miss, the labels that DO exist at the level where the lookup stopped (at most 50), so a mistyped pinpoint can be corrected without another call. Omitted from the response when it has no value.
      - `reason` string, nullable: Present when `matched` is false. `no_such_subsection`: the outline is known and has no such unit. `no_structure`: we could not recover this section's outline, so a pinpoint cannot be located (the section text is still at `/body`). Omitted from the response when it has no value.
      - `note` string, nullable: A sentence saying what happened, when `matched` is false. Omitted from the response when it has no value.
      - `structure` PinpointStructure: How far the outline behind a pinpoint (or a `subsections` tree) can be trusted.
        - `source` 'artifact' | 'request_time', required: `artifact`: a pre-built outline of this section, used only after its hash matched the exact text we serve. `request_time`: the outline was parsed from the text on this request.
        - `quality` string, nullable: `native` (built from the publisher's own markup), `verified` (parsed from the text and passed every check), `heuristic` (parsed from the text and failed a check: use it, but confirm against the text) or `none` (no outline could be recovered). Null when the outline carries no grade.
        - `builder` string, nullable: Which builder produced the outline.
        - `textSha256` string, nullable: SHA-256 of the `plain` text every `charStart` / `charEnd` offset indexes into. Hash your copy of `plain` to confirm the offsets apply to it.
    - `pinpointNote` string, nullable: Present only when the citation carried a pinpoint and we could not look it up this time (the section text was unavailable, or the lookup failed or ran out of time). The section itself resolved and is charged as usual; `subsection` still echoes the pinpoint. Retrying usually answers.
    - `missReason` 'not_held' | 'former_law' | 'ambiguous_jurisdiction' | 'malformed' | 'out_of_scope_corpus' | 'container_not_supported' | 'index_warming' | 'lookup_failed' | 'miss_reason_unavailable', nullable: Present only when `resolved` is false and none of `citationOutsideFilters` / `sessionLaw` already explains it. WHY the citation did not resolve: `ambiguous_jurisdiction` (the form is one several jurisdictions print, or names none, and no `state` was given: pass `state`; `candidates` lists the held sections it could mean when we can name them, and never picks one), `out_of_scope_corpus` (it names a body of law this route does not serve; `missReasonHint` says which and where to look), `former_law` (it names a repealed, renumbered or retired compilation we do not serve as current law; `missReasonHint` names the successor where one is known), `malformed` (no parser reads it: a docket or database token, a case or treatise citation, a string with no section number, or a subdivision or edition year with no section before it), `container_not_supported` (it cites a chapter, article, title or part, not a section; this route resolves sections, and answers a container only in the forms `matchLevel: container` lists: browse it with `GET /us/statutes/divisions`, or cite a section inside it), `not_held` (it reads as a citation to a jurisdiction and corpus we serve, and that corpus does not hold the section), `index_warming` (batch items only: the code this citation names was still being read when the call arrived, so there is NO verdict about the citation; the item is not billed, and sending it again in a few seconds answers it), `lookup_failed` (batch items only: the citation resolved, but WE failed to load or serialize its section, so this says nothing about whether the section exists; the item is refunded and counted in the response's `failedCount`, and retrying it answers it). `miss_reason_unavailable` (the check that explains a miss timed out or failed on our side, so this response carries NO explanation: it says nothing about why the citation did not resolve, and resending it usually answers; the citation's own verdict, unresolved, is unchanged and charged as any miss is). A miss is still charged, because the verdict is the product, except `index_warming` and `lookup_failed`, which are not verdicts and are never charged; the reason itself adds no charge. Absent on a resolved citation.
    - `candidates` MissReasonCandidate[], nullable: Present only with `missReason: ambiguous_jurisdiction`, and only when we can name the held sections the citation could mean (an acronym two states print, such as `CCR`). At most 10 entries. Pass the right one's `state` to `GET /us/statutes/resolve` to resolve it. Never a pick: when several are listed, none is the answer.
      - `state` string, required: The jurisdiction to pass as `state` to resolve this one.
      - `actId` string, required: The section's id.
      - `citation` string, nullable: Its own citation, as it appears in results.
      - `subsection` string, nullable: The pinpoint the citation carried, if any.
    - `missReasonHint` string, nullable: Present with `missReason`. One sentence on what to try next. Prose for a human or an agent; do not parse it. Omitted from the response when it has no value.
    - `resolvedWithState` string, nullable: The state scope that produced the answer when the unscoped resolve missed: the state the citation names, else your `state`. Null when it resolved as written.
    - `skipped` 'session_law' | 'error' | 'range_interior', nullable: Why no resolution was attempted or it failed: `session_law` (an act as enacted, not a code section: read it under `/us/session-laws`; not charged), `error` (we failed to process it; refunded) or `range_interior` (a section between the ends of a range, listed but never resolved; not charged).
  - `counts` ExtractCitationsCounts, required
    - `found` integer, required: ROWS found: one per distinct citation and pinpoint, before the 200-section cut. Three pinpoints of one section are three rows; `distinctSections` counts sections.
    - `distinctSections` integer: DISTINCT SECTIONS those rows name before the cut: the unit the price and the 200-section cap count in.
    - `resolved` integer, required: How many resolved to a section.
    - `unresolved` integer, required: How many were sent to the resolver and did not.
    - `skipped` integer, required: How many billable-position rows were not resolved (`session_law`, `error`). `range_interior` rows are counted in `interior`, not here.
    - `interior` integer: Sections listed between the ends of a short `through` range (`rangeRole: interior`). Not counted in `found`, `distinctSections` or the charge.
  - `truncated` boolean: True when more than 200 distinct citations were found and `citations` holds the first of them. `counts.found` still reports the number found, and only the ones listed were resolved and charged.
  - `notes` string[]: Plain-language notes on the answer, for example a range wider than 25 sections that returned its two ends only.
  - `textChars` integer, required: Characters of `text` read.
  - `extractorVersion` string, required: The extractor's version. The same text can yield different citations when it changes, so cache extractions by text AND this value.
  - `creditsConsumed` number: Credits actually charged. 0 when `resolve` is false.
  - `processingTimeMs` number: Server-side time for this request in milliseconds. Not billed on.

## Other responses

- `401`: Missing, invalid or revoked API key.
- `402`: Insufficient credits (`insufficient_credits`, never retry). `creditsRequired` and `creditsAvailable` say how short you are when known; top up, then repeat the call.
- `409`: Idempotency conflict. `idempotency_in_progress`: the original request with this key is still running (retry after `Retry-After`). `idempotency_replay_unavailable`: it completed but its response was too large to replay; nothing is charged again, so check the credit balance instead of retrying.
- `422`: `text` is empty, over the character limit, not text, or `state` is unknown. `idempotency_key_reused`: the `Idempotency-Key` was already used with a different request.
- `429`: Rate limit exceeded, or this key's daily resolution allowance is spent (`Retry-After` says when it resets). Nothing was charged.
- `500`: Internal error (`internal_error`, retryable). Any credits charged for the call are refunded; retry with backoff and quote `requestId` to support if it persists.
- `503`: The statutes corpus is temporarily unavailable. The charge is refunded.

## Changes

- **2026-11-07** `340c0d24d0b6`: 2 warning, 62 info
  - added the new `miss_reason_unavailable` enum value to the `citations/items/missReason/anyOf[subschema #1]/` response property for the response status `200`
  - added the new `range_interior` enum value to the `citations/items/skipped/anyOf[subschema #1]/` response property for the response status `200`
  - added the non-success response with the status `500`
  - added the optional property `candidateCount` to the response with the `401` status
  - …60 more

[Operation history](https://skmtc.dev/vaquill/apis/vaquill-developer-api/changes/api/v1/us/statutes/extract-citations/post.md)

---

[API](https://skmtc.dev/vaquill/apis/vaquill-developer-api.md) - [All operations](https://skmtc.dev/vaquill/apis/vaquill-developer-api/llms.txt) - [OpenAPI document](https://skmtc.dev/vaquill/apis/vaquill-developer-api/revisions/340c0d24d0b6?raw)
